<commit_before>#include "common/buffer/buffer_impl.h"
#include "common/common/logger.h"
#include "common/http/http2/metadata_decoder.h"
#include "common/http/http2/metadata_encoder.h"
#include "common/runtime/runtime_impl.h"

#include "test/test_common/logging.h"

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "nghttp2/nghttp2.h"

// A global variable in nghttp2 to disable preface and initial settings for tests.
// TODO(soya3129): Remove after issue https://github.com/nghttp2/nghttp2/issues/1246 is fixed.
extern "C" {
extern int nghttp2_enable_strict_preface;
}

namespace Envoy {
namespace Http {
namespace Http2 {
namespace {

static const uint64_t STREAM_ID = 1;

// The buffer stores data sent by encoder and received by decoder.
struct TestBuffer {
  uint8_t buf[1024 * 1024] = {0};
  size_t length = 0;
};

// The application data structure passes to nghttp2 session.
struct UserData {
  MetadataEncoder* encoder;
  MetadataDecoder* decoder;
  // Stores data sent by encoder and received by the decoder.
  TestBuffer* output_buffer;
};

// Nghttp2 callback function for sending extension frame.
static ssize_t pack_extension_callback(nghttp2_session* session, uint8_t* buf, size_t len,
                                       const nghttp2_frame*, void* user_data) {
  EXPECT_NE(nullptr, session);

  MetadataEncoder* encoder = reinterpret_cast<UserData*>(user_data)->encoder;
  const uint64_t size_copied = encoder->packNextFramePayload(buf, len);

  return static_cast<ssize_t>(size_copied);
}

// Nghttp2 callback function for receiving extension frame.
static int on_extension_chunk_recv_callback(nghttp2_session* session, const nghttp2_frame_hd* hd,
                                            const uint8_t* data, size_t len, void* user_data) {
  EXPECT_NE(nullptr, session);
  EXPECT_GE(hd->length, len);

  MetadataDecoder* decoder = reinterpret_cast<UserData*>(user_data)->decoder;
  bool success = decoder->receiveMetadata(data, len);
  return success ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
}

// Nghttp2 callback function for unpack extension frames.
static int unpack_extension_callback(nghttp2_session* session, void** payload,
                                     const nghttp2_frame_hd* hd, void* user_data) {
  EXPECT_NE(nullptr, session);
  EXPECT_NE(nullptr, hd);
  EXPECT_NE(nullptr, payload);

  MetadataDecoder* decoder = reinterpret_cast<UserData*>(user_data)->decoder;
  bool result = decoder->onMetadataFrameComplete((hd->flags == END_METADATA_FLAG) ? true : false);
  return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
}

// Nghttp2 callback function for sending data to peer.
static ssize_t send_callback(nghttp2_session* session, const uint8_t* buf, size_t len, int flags,
                             void* user_data) {
  EXPECT_NE(nullptr, session);
  EXPECT_LE(0, flags);

  TestBuffer* buffer = (reinterpret_cast<UserData*>(user_data))->output_buffer;
  memcpy(buffer->buf + buffer->length, buf, len);
  buffer->length += len;
  return len;
}

} // namespace

class MetadataEncoderDecoderTest : public testing::Test {
public:
  void initialize(MetadataCallback cb) {
    decoder_ = std::make_unique<MetadataDecoder>(cb);

    // Enables extension frame.
    nghttp2_option_new(&option_);
    nghttp2_option_set_user_recv_extension_type(option_, METADATA_FRAME_TYPE);

    // Sets callback functions.
    nghttp2_session_callbacks_new(&callbacks_);
    nghttp2_session_callbacks_set_pack_extension_callback(callbacks_, pack_extension_callback);
    nghttp2_session_callbacks_set_send_callback(callbacks_, send_callback);
    nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(
        callbacks_, on_extension_chunk_recv_callback);
    nghttp2_session_callbacks_set_unpack_extension_callback(callbacks_, unpack_extension_callback);

    // Sets application data to pass to nghttp2 session.
    user_data_.encoder = &encoder_;
    user_data_.decoder = decoder_.get();
    user_data_.output_buffer = &output_buffer_;

    // Creates new nghttp2 session.
    nghttp2_enable_strict_preface = 0;
    nghttp2_session_client_new2(&session_, callbacks_, &user_data_, option_);
    nghttp2_enable_strict_preface = 1;
  }

  void cleanUp() {
    nghttp2_session_del(session_);
    nghttp2_session_callbacks_del(callbacks_);
    nghttp2_option_del(option_);
  }

  void verifyMetadataMapVector(MetadataMapVector& expect, MetadataMapPtr&& metadata_map_ptr) {
    for (const auto& metadata : *metadata_map_ptr) {
      EXPECT_EQ(expect.front()->find(metadata.first)->second, metadata.second);
    }
    expect.erase(expect.begin());
  }

  void submitMetadata(const MetadataMapVector& metadata_map_vector) {
    // Creates metadata payload.
    encoder_.createPayload(metadata_map_vector);
    for (uint8_t flags : encoder_.payloadFrameFlagBytes()) {
      int result =
          nghttp2_submit_extension(session_, METADATA_FRAME_TYPE, flags, STREAM_ID, nullptr);
      EXPECT_EQ(0, result);
    }
    // Triggers nghttp2 to populate the payloads of the METADATA frames.
    int result = nghttp2_session_send(session_);
    EXPECT_EQ(0, result);
  }

  nghttp2_session* session_ = nullptr;
  nghttp2_session_callbacks* callbacks_;
  MetadataEncoder encoder_;
  std::unique_ptr<MetadataDecoder> decoder_;
  nghttp2_option* option_;
  int count_ = 0;

  // Stores data received by peer.
  TestBuffer output_buffer_;

  // Application data passed to nghttp2.
  UserData user_data_;

  Runtime::RandomGeneratorImpl random_generator_;
};

TEST_F(MetadataEncoderDecoderTest, TestMetadataSizeLimit) {
  MetadataMap metadata_map = {
      {"header_key1", std::string(1024 * 1024 + 1, 'a')},
  };
  MetadataMapPtr metadata_map_ptr = std::make_unique<MetadataMap>(metadata_map);
  MetadataMapVector metadata_map_vector;
  metadata_map_vector.push_back(std::move(metadata_map_ptr));

  // Verifies the encoding/decoding result in decoder's callback functions.
  initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void {
    this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr));
  });

  // metadata_map exceeds size limit.
  EXPECT_LOG_CONTAINS("error", "exceeds the max bound.",
                      EXPECT_FALSE(encoder_.createPayload(metadata_map_vector)));

  std::string payload = std::string(1024 * 1024 + 1, 'a');
  EXPECT_FALSE(
      decoder_->receiveMetadata(reinterpret_cast<const uint8_t*>(payload.data()), payload.size()));

  cleanUp();
}

TEST_F(MetadataEncoderDecoderTest, TestDecodeBadData) {
  MetadataMap metadata_map = {
      {"header_key1", "header_value1"},
  };
  MetadataMapPtr metadata_map_ptr = std::make_unique<MetadataMap>(metadata_map);
  MetadataMapVector metadata_map_vector;
  metadata_map_vector.push_back(std::move(metadata_map_ptr));

  // Verifies the encoding/decoding result in decoder's callback functions.
  initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void {
    this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr));
  });
  submitMetadata(metadata_map_vector);

  // Messes up with the encoded payload, and passes it to the decoder.
  output_buffer_.buf[10] |= 0xff;
  decoder_->receiveMetadata(output_buffer_.buf, output_buffer_.length);
  EXPECT_FALSE(decoder_->onMetadataFrameComplete(true));

  cleanUp();
}

// Checks if accumulated metadata size reaches size limit, returns failure.
TEST_F(MetadataEncoderDecoderTest, VerifyEncoderDecoderMultipleMetadataReachSizeLimit) {
  MetadataMap metadata_map_empty = {};
  MetadataCallback cb = [](std::unique_ptr<MetadataMap>) -> void {};
  initialize(cb);

  ssize_t result = 0;

  for (int i = 0; i < 100; i++) {
    // Cleans up the output buffer.
    memset(output_buffer_.buf, 0, output_buffer_.length);
    output_buffer_.length = 0;

    MetadataMap metadata_map = {
        {"header_key1", std::string(10000, 'a')},
        {"header_key2", std::string(10000, 'b')},
    };
    MetadataMapPtr metadata_map_ptr = std::make_unique<MetadataMap>(metadata_map);
    MetadataMapVector metadata_map_vector;
    metadata_map_vector.push_back(std::move(metadata_map_ptr));

    // Encode and decode the second MetadataMap.
    decoder_->callback_ = [this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void {
      this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr));
    };
    submitMetadata(metadata_map_vector);

    result = nghttp2_session_mem_recv(session_, output_buffer_.buf, output_buffer_.length);
    if (result < 0) {
      break;
    }
  }
  // Verifies max metadata limit reached.
  EXPECT_LT(result, 0);
  EXPECT_LE(decoder_->max_payload_size_bound_, decoder_->total_payload_size_);

  cleanUp();
}

// Tests encoding/decoding small metadata map vectors.
TEST_F(MetadataEncoderDecoderTest, EncodeMetadataMapVectorSmall) {
  MetadataMap metadata_map = {
      {"header_key1", std::string(5, 'a')},
      {"header_key2", std::string(5, 'b')},
  };
  MetadataMapPtr metadata_map_ptr = std::make_unique<MetadataMap>(metadata_map);
  MetadataMap metadata_map_2 = {
      {"header_key3", std::string(5, 'a')},
      {"header_key4", std::string(5, 'b')},
  };
  MetadataMapPtr metadata_map_ptr_2 = std::make_unique<MetadataMap>(metadata_map);
  MetadataMap metadata_map_3 = {
      {"header_key1", std::string(1, 'a')},
      {"header_key2", std::string(1, 'b')},
  };
  MetadataMapPtr metadata_map_ptr_3 = std::make_unique<MetadataMap>(metadata_map);

  MetadataMapVector metadata_map_vector;
  metadata_map_vector.push_back(std::move(metadata_map_ptr));
  metadata_map_vector.push_back(std::move(metadata_map_ptr_2));
  metadata_map_vector.push_back(std::move(metadata_map_ptr_3));

  // Verifies the encoding/decoding result in decoder's callback functions.
  initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void {
    this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr));
  });
  submitMetadata(metadata_map_vector);

  // Verifies flag and payload are encoded correctly.
  const uint64_t consume_size = random_generator_.random() % output_buffer_.length;
  nghttp2_session_mem_recv(session_, output_buffer_.buf, consume_size);
  nghttp2_session_mem_recv(session_, output_buffer_.buf + consume_size,
                           output_buffer_.length - consume_size);

  cleanUp();
}

// Tests encoding/decoding large metadata map vectors.
TEST_F(MetadataEncoderDecoderTest, EncodeMetadataMapVectorLarge) {
  MetadataMapVector metadata_map_vector;
  for (int i = 0; i < 10; i++) {
    MetadataMap metadata_map = {
        {"header_key1", std::string(50000, 'a')},
        {"header_key2", std::string(50000, 'b')},
    };
    MetadataMapPtr metadata_map_ptr = std::make_unique<MetadataMap>(metadata_map);
    metadata_map_vector.push_back(std::move(metadata_map_ptr));
  }
  // Verifies the encoding/decoding result in decoder's callback functions.
  initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void {
    this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr));
  });
  submitMetadata(metadata_map_vector);
  // Verifies flag and payload are encoded correctly.
  const uint64_t consume_size = random_generator_.random() % output_buffer_.length;
  nghttp2_session_mem_recv(session_, output_buffer_.buf, consume_size);
  nghttp2_session_mem_recv(session_, output_buffer_.buf + consume_size,
                           output_buffer_.length - consume_size);
  cleanUp();
}

// Tests encoding/decoding with fuzzed metadata size.
TEST_F(MetadataEncoderDecoderTest, EncodeFuzzedMetadata) {
  MetadataMapVector metadata_map_vector;
  for (int i = 0; i < 10; i++) {
    Runtime::RandomGeneratorImpl random;
    int value_size_1 = random.random() % (2 * Http::METADATA_MAX_PAYLOAD_SIZE) + 1;
    int value_size_2 = random.random() % (2 * Http::METADATA_MAX_PAYLOAD_SIZE) + 1;
    MetadataMap metadata_map = {
        {"header_key1", std::string(value_size_1, 'a')},
        {"header_key2", std::string(value_size_2, 'a')},
    };
    MetadataMapPtr metadata_map_ptr = std::make_unique<MetadataMap>(metadata_map);
    metadata_map_vector.push_back(std::move(metadata_map_ptr));
  }

  // Verifies the encoding/decoding result in decoder's callback functions.
  initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void {
    this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr));
  });
  submitMetadata(metadata_map_vector);

  // Verifies flag and payload are encoded correctly.
  nghttp2_session_mem_recv(session_, output_buffer_.buf, output_buffer_.length);

  cleanUp();
}

using MetadataEncoderDecoderDeathTest = MetadataEncoderDecoderTest;

// Crash if a caller tries to pack more frames than the encoder has data for.
TEST_F(MetadataEncoderDecoderDeathTest, PackTooManyFrames) {
  MetadataMap metadata_map = {
      {"header_key1", std::string(5, 'a')},
      {"header_key2", std::string(5, 'b')},
  };
  MetadataMapPtr metadata_map_ptr = std::make_unique<MetadataMap>(metadata_map);
  MetadataMapVector metadata_map_vector;
  metadata_map_vector.push_back(std::move(metadata_map_ptr));

  initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void {
    this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr));
  });
  submitMetadata(metadata_map_vector);

  // Try to send an extra METADATA frame. Submitting the frame to nghttp2 should succeed, but
  // pack_extension_callback should fail, and that failure will propagate through
  // nghttp2_session_send. How to handle the failure is up to the HTTP/2 codec (in practice, it will
  // throw a CodecProtocolException).
  int result = nghttp2_submit_extension(session_, METADATA_FRAME_TYPE, 0, STREAM_ID, nullptr);
  EXPECT_EQ(0, result);
  EXPECT_DEATH(nghttp2_session_send(session_),
               "No payload remaining to pack into a METADATA frame.");

  cleanUp();
}

} // namespace Http2
} // namespace Http
} // namespace Envoy
<commit_msg>Fix MetadataEncoderDecoderDeathTest.PackTooManyFrames in coverage build. (#10279)<commit_after>#include "common/buffer/buffer_impl.h"
#include "common/common/logger.h"
#include "common/http/http2/metadata_decoder.h"
#include "common/http/http2/metadata_encoder.h"
#include "common/runtime/runtime_impl.h"

#include "test/test_common/logging.h"

#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "nghttp2/nghttp2.h"

// A global variable in nghttp2 to disable preface and initial settings for tests.
// TODO(soya3129): Remove after issue https://github.com/nghttp2/nghttp2/issues/1246 is fixed.
extern "C" {
extern int nghttp2_enable_strict_preface;
}

namespace Envoy {
namespace Http {
namespace Http2 {
namespace {

static const uint64_t STREAM_ID = 1;

// The buffer stores data sent by encoder and received by decoder.
struct TestBuffer {
  uint8_t buf[1024 * 1024] = {0};
  size_t length = 0;
};

// The application data structure passes to nghttp2 session.
struct UserData {
  MetadataEncoder* encoder;
  MetadataDecoder* decoder;
  // Stores data sent by encoder and received by the decoder.
  TestBuffer* output_buffer;
};

// Nghttp2 callback function for sending extension frame.
static ssize_t pack_extension_callback(nghttp2_session* session, uint8_t* buf, size_t len,
                                       const nghttp2_frame*, void* user_data) {
  EXPECT_NE(nullptr, session);

  MetadataEncoder* encoder = reinterpret_cast<UserData*>(user_data)->encoder;
  const uint64_t size_copied = encoder->packNextFramePayload(buf, len);

  return static_cast<ssize_t>(size_copied);
}

// Nghttp2 callback function for receiving extension frame.
static int on_extension_chunk_recv_callback(nghttp2_session* session, const nghttp2_frame_hd* hd,
                                            const uint8_t* data, size_t len, void* user_data) {
  EXPECT_NE(nullptr, session);
  EXPECT_GE(hd->length, len);

  MetadataDecoder* decoder = reinterpret_cast<UserData*>(user_data)->decoder;
  bool success = decoder->receiveMetadata(data, len);
  return success ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
}

// Nghttp2 callback function for unpack extension frames.
static int unpack_extension_callback(nghttp2_session* session, void** payload,
                                     const nghttp2_frame_hd* hd, void* user_data) {
  EXPECT_NE(nullptr, session);
  EXPECT_NE(nullptr, hd);
  EXPECT_NE(nullptr, payload);

  MetadataDecoder* decoder = reinterpret_cast<UserData*>(user_data)->decoder;
  bool result = decoder->onMetadataFrameComplete((hd->flags == END_METADATA_FLAG) ? true : false);
  return result ? 0 : NGHTTP2_ERR_CALLBACK_FAILURE;
}

// Nghttp2 callback function for sending data to peer.
static ssize_t send_callback(nghttp2_session* session, const uint8_t* buf, size_t len, int flags,
                             void* user_data) {
  EXPECT_NE(nullptr, session);
  EXPECT_LE(0, flags);

  TestBuffer* buffer = (reinterpret_cast<UserData*>(user_data))->output_buffer;
  memcpy(buffer->buf + buffer->length, buf, len);
  buffer->length += len;
  return len;
}

} // namespace

class MetadataEncoderDecoderTest : public testing::Test {
public:
  void initialize(MetadataCallback cb) {
    decoder_ = std::make_unique<MetadataDecoder>(cb);

    // Enables extension frame.
    nghttp2_option_new(&option_);
    nghttp2_option_set_user_recv_extension_type(option_, METADATA_FRAME_TYPE);

    // Sets callback functions.
    nghttp2_session_callbacks_new(&callbacks_);
    nghttp2_session_callbacks_set_pack_extension_callback(callbacks_, pack_extension_callback);
    nghttp2_session_callbacks_set_send_callback(callbacks_, send_callback);
    nghttp2_session_callbacks_set_on_extension_chunk_recv_callback(
        callbacks_, on_extension_chunk_recv_callback);
    nghttp2_session_callbacks_set_unpack_extension_callback(callbacks_, unpack_extension_callback);

    // Sets application data to pass to nghttp2 session.
    user_data_.encoder = &encoder_;
    user_data_.decoder = decoder_.get();
    user_data_.output_buffer = &output_buffer_;

    // Creates new nghttp2 session.
    nghttp2_enable_strict_preface = 0;
    nghttp2_session_client_new2(&session_, callbacks_, &user_data_, option_);
    nghttp2_enable_strict_preface = 1;
  }

  void cleanUp() {
    nghttp2_session_del(session_);
    nghttp2_session_callbacks_del(callbacks_);
    nghttp2_option_del(option_);
  }

  void verifyMetadataMapVector(MetadataMapVector& expect, MetadataMapPtr&& metadata_map_ptr) {
    for (const auto& metadata : *metadata_map_ptr) {
      EXPECT_EQ(expect.front()->find(metadata.first)->second, metadata.second);
    }
    expect.erase(expect.begin());
  }

  void submitMetadata(const MetadataMapVector& metadata_map_vector) {
    // Creates metadata payload.
    encoder_.createPayload(metadata_map_vector);
    for (uint8_t flags : encoder_.payloadFrameFlagBytes()) {
      int result =
          nghttp2_submit_extension(session_, METADATA_FRAME_TYPE, flags, STREAM_ID, nullptr);
      EXPECT_EQ(0, result);
    }
    // Triggers nghttp2 to populate the payloads of the METADATA frames.
    int result = nghttp2_session_send(session_);
    EXPECT_EQ(0, result);
  }

  nghttp2_session* session_ = nullptr;
  nghttp2_session_callbacks* callbacks_;
  MetadataEncoder encoder_;
  std::unique_ptr<MetadataDecoder> decoder_;
  nghttp2_option* option_;
  int count_ = 0;

  // Stores data received by peer.
  TestBuffer output_buffer_;

  // Application data passed to nghttp2.
  UserData user_data_;

  Runtime::RandomGeneratorImpl random_generator_;
};

TEST_F(MetadataEncoderDecoderTest, TestMetadataSizeLimit) {
  MetadataMap metadata_map = {
      {"header_key1", std::string(1024 * 1024 + 1, 'a')},
  };
  MetadataMapPtr metadata_map_ptr = std::make_unique<MetadataMap>(metadata_map);
  MetadataMapVector metadata_map_vector;
  metadata_map_vector.push_back(std::move(metadata_map_ptr));

  // Verifies the encoding/decoding result in decoder's callback functions.
  initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void {
    this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr));
  });

  // metadata_map exceeds size limit.
  EXPECT_LOG_CONTAINS("error", "exceeds the max bound.",
                      EXPECT_FALSE(encoder_.createPayload(metadata_map_vector)));

  std::string payload = std::string(1024 * 1024 + 1, 'a');
  EXPECT_FALSE(
      decoder_->receiveMetadata(reinterpret_cast<const uint8_t*>(payload.data()), payload.size()));

  cleanUp();
}

TEST_F(MetadataEncoderDecoderTest, TestDecodeBadData) {
  MetadataMap metadata_map = {
      {"header_key1", "header_value1"},
  };
  MetadataMapPtr metadata_map_ptr = std::make_unique<MetadataMap>(metadata_map);
  MetadataMapVector metadata_map_vector;
  metadata_map_vector.push_back(std::move(metadata_map_ptr));

  // Verifies the encoding/decoding result in decoder's callback functions.
  initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void {
    this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr));
  });
  submitMetadata(metadata_map_vector);

  // Messes up with the encoded payload, and passes it to the decoder.
  output_buffer_.buf[10] |= 0xff;
  decoder_->receiveMetadata(output_buffer_.buf, output_buffer_.length);
  EXPECT_FALSE(decoder_->onMetadataFrameComplete(true));

  cleanUp();
}

// Checks if accumulated metadata size reaches size limit, returns failure.
TEST_F(MetadataEncoderDecoderTest, VerifyEncoderDecoderMultipleMetadataReachSizeLimit) {
  MetadataMap metadata_map_empty = {};
  MetadataCallback cb = [](std::unique_ptr<MetadataMap>) -> void {};
  initialize(cb);

  ssize_t result = 0;

  for (int i = 0; i < 100; i++) {
    // Cleans up the output buffer.
    memset(output_buffer_.buf, 0, output_buffer_.length);
    output_buffer_.length = 0;

    MetadataMap metadata_map = {
        {"header_key1", std::string(10000, 'a')},
        {"header_key2", std::string(10000, 'b')},
    };
    MetadataMapPtr metadata_map_ptr = std::make_unique<MetadataMap>(metadata_map);
    MetadataMapVector metadata_map_vector;
    metadata_map_vector.push_back(std::move(metadata_map_ptr));

    // Encode and decode the second MetadataMap.
    decoder_->callback_ = [this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void {
      this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr));
    };
    submitMetadata(metadata_map_vector);

    result = nghttp2_session_mem_recv(session_, output_buffer_.buf, output_buffer_.length);
    if (result < 0) {
      break;
    }
  }
  // Verifies max metadata limit reached.
  EXPECT_LT(result, 0);
  EXPECT_LE(decoder_->max_payload_size_bound_, decoder_->total_payload_size_);

  cleanUp();
}

// Tests encoding/decoding small metadata map vectors.
TEST_F(MetadataEncoderDecoderTest, EncodeMetadataMapVectorSmall) {
  MetadataMap metadata_map = {
      {"header_key1", std::string(5, 'a')},
      {"header_key2", std::string(5, 'b')},
  };
  MetadataMapPtr metadata_map_ptr = std::make_unique<MetadataMap>(metadata_map);
  MetadataMap metadata_map_2 = {
      {"header_key3", std::string(5, 'a')},
      {"header_key4", std::string(5, 'b')},
  };
  MetadataMapPtr metadata_map_ptr_2 = std::make_unique<MetadataMap>(metadata_map);
  MetadataMap metadata_map_3 = {
      {"header_key1", std::string(1, 'a')},
      {"header_key2", std::string(1, 'b')},
  };
  MetadataMapPtr metadata_map_ptr_3 = std::make_unique<MetadataMap>(metadata_map);

  MetadataMapVector metadata_map_vector;
  metadata_map_vector.push_back(std::move(metadata_map_ptr));
  metadata_map_vector.push_back(std::move(metadata_map_ptr_2));
  metadata_map_vector.push_back(std::move(metadata_map_ptr_3));

  // Verifies the encoding/decoding result in decoder's callback functions.
  initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void {
    this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr));
  });
  submitMetadata(metadata_map_vector);

  // Verifies flag and payload are encoded correctly.
  const uint64_t consume_size = random_generator_.random() % output_buffer_.length;
  nghttp2_session_mem_recv(session_, output_buffer_.buf, consume_size);
  nghttp2_session_mem_recv(session_, output_buffer_.buf + consume_size,
                           output_buffer_.length - consume_size);

  cleanUp();
}

// Tests encoding/decoding large metadata map vectors.
TEST_F(MetadataEncoderDecoderTest, EncodeMetadataMapVectorLarge) {
  MetadataMapVector metadata_map_vector;
  for (int i = 0; i < 10; i++) {
    MetadataMap metadata_map = {
        {"header_key1", std::string(50000, 'a')},
        {"header_key2", std::string(50000, 'b')},
    };
    MetadataMapPtr metadata_map_ptr = std::make_unique<MetadataMap>(metadata_map);
    metadata_map_vector.push_back(std::move(metadata_map_ptr));
  }
  // Verifies the encoding/decoding result in decoder's callback functions.
  initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void {
    this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr));
  });
  submitMetadata(metadata_map_vector);
  // Verifies flag and payload are encoded correctly.
  const uint64_t consume_size = random_generator_.random() % output_buffer_.length;
  nghttp2_session_mem_recv(session_, output_buffer_.buf, consume_size);
  nghttp2_session_mem_recv(session_, output_buffer_.buf + consume_size,
                           output_buffer_.length - consume_size);
  cleanUp();
}

// Tests encoding/decoding with fuzzed metadata size.
TEST_F(MetadataEncoderDecoderTest, EncodeFuzzedMetadata) {
  MetadataMapVector metadata_map_vector;
  for (int i = 0; i < 10; i++) {
    Runtime::RandomGeneratorImpl random;
    int value_size_1 = random.random() % (2 * Http::METADATA_MAX_PAYLOAD_SIZE) + 1;
    int value_size_2 = random.random() % (2 * Http::METADATA_MAX_PAYLOAD_SIZE) + 1;
    MetadataMap metadata_map = {
        {"header_key1", std::string(value_size_1, 'a')},
        {"header_key2", std::string(value_size_2, 'a')},
    };
    MetadataMapPtr metadata_map_ptr = std::make_unique<MetadataMap>(metadata_map);
    metadata_map_vector.push_back(std::move(metadata_map_ptr));
  }

  // Verifies the encoding/decoding result in decoder's callback functions.
  initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void {
    this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr));
  });
  submitMetadata(metadata_map_vector);

  // Verifies flag and payload are encoded correctly.
  nghttp2_session_mem_recv(session_, output_buffer_.buf, output_buffer_.length);

  cleanUp();
}

using MetadataEncoderDecoderDeathTest = MetadataEncoderDecoderTest;

// Crash if a caller tries to pack more frames than the encoder has data for.
TEST_F(MetadataEncoderDecoderDeathTest, PackTooManyFrames) {
  Logger::StderrSinkDelegate stderr_sink(Logger::Registry::getSink()); // For coverage build.
  MetadataMap metadata_map = {
      {"header_key1", std::string(5, 'a')},
      {"header_key2", std::string(5, 'b')},
  };
  MetadataMapPtr metadata_map_ptr = std::make_unique<MetadataMap>(metadata_map);
  MetadataMapVector metadata_map_vector;
  metadata_map_vector.push_back(std::move(metadata_map_ptr));

  initialize([this, &metadata_map_vector](MetadataMapPtr&& metadata_map_ptr) -> void {
    this->verifyMetadataMapVector(metadata_map_vector, std::move(metadata_map_ptr));
  });
  submitMetadata(metadata_map_vector);

  // Try to send an extra METADATA frame. Submitting the frame to nghttp2 should succeed, but
  // pack_extension_callback should fail, and that failure will propagate through
  // nghttp2_session_send. How to handle the failure is up to the HTTP/2 codec (in practice, it will
  // throw a CodecProtocolException).
  int result = nghttp2_submit_extension(session_, METADATA_FRAME_TYPE, 0, STREAM_ID, nullptr);
  EXPECT_EQ(0, result);
  EXPECT_DEATH(nghttp2_session_send(session_),
               "No payload remaining to pack into a METADATA frame.");

  cleanUp();
}

} // namespace Http2
} // namespace Http
} // namespace Envoy
<|endoftext|>


<commit_before>#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <Eigen/Dense>
#include "firmware_generator.h"
#include "firmware_template.h"
#include "control_design_functions.h"

void firmware_generator(const std::vector<model_data> & md, std::string filename_base)
{
  const int observer_state_size = 1;    // observer state is w
  const int observer_input_size = 4;    // observer inputs are: steer, roll rate, steer rate, steer torque
  const int observer_output_size = 1;   // estimate of roll angle
  const int plant_model_state_size = 4; // roll, steer, roll rate, steer rate
  const int plant_model_input_size = 1; // torque

  std::string header_body{};
  header_body += "const uint32_t num_gains = "
              + std::to_string(md.size()) + ";\n";
  header_body += "const uint32_t observer_state_size = "
              + std::to_string(observer_state_size) + ";\n";
  header_body += "const uint32_t observer_input_size = "
              + std::to_string(observer_input_size) + ";\n";
  header_body += "const uint32_t observer_output_size = "
              + std::to_string(observer_output_size) + ";\n";
  header_body += "const uint32_t plant_model_state_size = "
              + std::to_string(plant_model_state_size) + ";\n";
  header_body += "const uint32_t plant_model_input_size = "
              + std::to_string(plant_model_input_size) + ";\n";

  std::ofstream header_file(filename_base + ".h");
  header_file << firmware_template::preamble
              << header_body << firmware_template::postamble;
  header_file.close();

  std::ofstream source_file(filename_base + ".cpp");
  source_file << generate_source(md, filename_base);
  source_file.close();
}

std::string generate_source(const std::vector<model_data> & md, std::string filename_base)
{
  //std::string source_body{};
  Eigen::IOFormat printfmt(Eigen::FullPrecision, 0, ", ", ", ", "", "", "{{", "}}");
  std::ostringstream out;
  out << "#include \"" << filename_base << ".h\"\n\n";
  out << "namespace control {\n\n";
  out << "const std::array<rt_controller_t, " << md.size();
  out << "> GainSchedule::schedule_ {{\n";

  for(auto it = md.begin(); it != md.end(); ++it) {
    out << "\t{\n";
    out << "\t\t" << it->theta_R_dot <<"f, // theta_R_dot\n";
    out << "\t\t{\n"; // start writing out controllers

    out << "\t\t\t{ // StateEstimator\n";
    out << "\t\t\t\t" << it->A_obs.format(printfmt) << ",\n";
    out << "\t\t\t\t" << it->B_obs.format(printfmt) << ",\n";
    out << "\t\t\t\t" << it->C_obs.format(printfmt) << ",\n";
    out << "\t\t\t\t" << it->D_obs.format(printfmt) << "\n";
    out << "\t\t\t},\n"; // StateEstimator End

    out << "\t\t\t{ // LQRController\n"; // LQRController Start
    out << "\t\t\t\t" << it->K_lqr.format(printfmt) << "\n";
    out << "\t\t\t},\n"; // LQRController End

    out << "\t\t\t{ // PIController\n"; // PIController Start
    out << "\t\t\t\t0, 0\n";
    out << "\t\t\t}\n"; // PIController End

    out << "\t\t}\n"; // controllers end
    if (it + 1 == md.end()) {
      out << "\t}\n";
    } else {
      out << "\t},\n";
    }
  }

  out << "}};\n\n} // namespace control";

  return out.str();
}

<commit_msg>Fix typo in model_data data member.<commit_after>#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <Eigen/Dense>
#include "firmware_generator.h"
#include "firmware_template.h"
#include "control_design_functions.h"

void firmware_generator(const std::vector<model_data> & md, std::string filename_base)
{
  const int observer_state_size = 1;    // observer state is w
  const int observer_input_size = 4;    // observer inputs are: steer, roll rate, steer rate, steer torque
  const int observer_output_size = 1;   // estimate of roll angle
  const int plant_model_state_size = 4; // roll, steer, roll rate, steer rate
  const int plant_model_input_size = 1; // torque

  std::string header_body{};
  header_body += "const uint32_t num_gains = "
              + std::to_string(md.size()) + ";\n";
  header_body += "const uint32_t observer_state_size = "
              + std::to_string(observer_state_size) + ";\n";
  header_body += "const uint32_t observer_input_size = "
              + std::to_string(observer_input_size) + ";\n";
  header_body += "const uint32_t observer_output_size = "
              + std::to_string(observer_output_size) + ";\n";
  header_body += "const uint32_t plant_model_state_size = "
              + std::to_string(plant_model_state_size) + ";\n";
  header_body += "const uint32_t plant_model_input_size = "
              + std::to_string(plant_model_input_size) + ";\n";

  std::ofstream header_file(filename_base + ".h");
  header_file << firmware_template::preamble
              << header_body << firmware_template::postamble;
  header_file.close();

  std::ofstream source_file(filename_base + ".cpp");
  source_file << generate_source(md, filename_base);
  source_file.close();
}

std::string generate_source(const std::vector<model_data> & md, std::string filename_base)
{
  //std::string source_body{};
  Eigen::IOFormat printfmt(Eigen::FullPrecision, 0, ", ", ", ", "", "", "{{", "}}");
  std::ostringstream out;
  out << "#include \"" << filename_base << ".h\"\n\n";
  out << "namespace control {\n\n";
  out << "const std::array<rt_controller_t, " << md.size();
  out << "> GainSchedule::schedule_ {{\n";

  for(auto it = md.begin(); it != md.end(); ++it) {
    out << "\t{\n";
    out << "\t\t" << it->theta_R_dot <<"f, // theta_R_dot\n";
    out << "\t\t{\n"; // start writing out controllers

    out << "\t\t\t{ // StateEstimator\n";
    out << "\t\t\t\t" << it->A_obs_d.format(printfmt) << ",\n";
    out << "\t\t\t\t" << it->B_obs_d.format(printfmt) << ",\n";
    out << "\t\t\t\t" << it->C_obs.format(printfmt) << ",\n";
    out << "\t\t\t\t" << it->D_obs.format(printfmt) << "\n";
    out << "\t\t\t},\n"; // StateEstimator End

    out << "\t\t\t{ // LQRController\n"; // LQRController Start
    out << "\t\t\t\t" << it->K_lqr.format(printfmt) << "\n";
    out << "\t\t\t},\n"; // LQRController End

    out << "\t\t\t{ // PIController\n"; // PIController Start
    out << "\t\t\t\t0, 0\n";
    out << "\t\t\t}\n"; // PIController End

    out << "\t\t}\n"; // controllers end
    if (it + 1 == md.end()) {
      out << "\t}\n";
    } else {
      out << "\t},\n";
    }
  }

  out << "}};\n\n} // namespace control";

  return out.str();
}

<|endoftext|>


<commit_before>#pragma once
#include <cstdint>
#include <nlohmann/json.hpp>
#include <vector>

#include "DatatypeEnum.hpp"
#include "RawBuffer.hpp"

namespace dai {

/// FeatureTracker configuration data structure
struct FeatureTrackerConfigData {
    struct CornerDetector {
        static constexpr const int AUTO = 0;

        enum class AlgorithmType : std::int32_t {
            /**
             * Harris corner detector.
             */
            HARRIS,
            /**
             * Shi-Thomasi corner detector.
             */
            SHI_THOMASI
        };
        /**
         * Corner detector algorithm type.
         */
        AlgorithmType algorithmType = AlgorithmType::HARRIS;

        /**
         * Ensures distributed feature detection across the image.
         * Image is divided into horizontal and vertical cells,
         * each cell has a target feature count = targetNumFeatures / cellGridDimension.
         * Each cell has it's own feature threshold.
         * A value of 4 means that the image is divided into 4x4 cells of equal width/height.
         * Maximum 4, minimum 1.
         */
        std::int32_t cellGridDimension = 4;

        /**
         * Target number of features to detect.
         * Maximum number of features is determined at runtime based on algorithm type.
         */
        std::int32_t targetNumFeatures = 320;

        /**
         * Hard limit for the maximum number of features that can be detected.
         * 0 means auto, will be set to the maximum value based on memory constraints.
         */
        std::int32_t maxNumFeatures = AUTO;

        /**
         * Enable 3x3 sobel operator to smoothen the image whose gradient is be computed.
         * If disabled a simple 1D row/column differentiator is used for gradient.
         */
        bool enableSobel = true;

        /**
         * Enable sorting detected features based on their score or not.
         */
        bool enableSorting = true;

        /**
         * Threshold settings structure for corner detector.
         */
        struct Thresholds {
            static constexpr const float AUTO = 0;

            /**
             * Minimum strength of a feature which will be detected.
             * 0 means automatic threshold update. Recommended so the tracker can adapt to different scenes/textures.
             * Each chell has its own threshold.
             * Empirical value.
             */
            float initialValue = AUTO;

            /**
             * Minimum limit for threshold.
             * Applicable when automatic threshold update is enabled.
             * 0 means auto, 6000000 for HARRIS, 1200 for SHI_THOMASI
             * Empirical value.
             */
            float min = AUTO;

            /**
             * Maximum limit for threshold.
             * Applicable when automatic threshold update is enabled.
             * 0 means auto.
             * Empirical value.
             */
            float max = AUTO;

            /**
             * When detected number of features exceeds the maximum in a cell threshold is lowered
             * by multiplying its value with this factor.
             */
            float decreaseFactor = 0.9;

            /**
             * When detected number of features doesn't exceed the maximum in a cell threshold is increased
             * by multiplying its value with this factor.
             */
            float increaseFactor = 1.1;
        };

        /**
         * Threshold settings.
         * These are advanced settings, suitable for debugging/special cases.
         */
        Thresholds thresholds;
    };

    /**
     * Used for feature reidentification between current and previous features.
     */
    struct MotionEstimator {
        bool enable = true;

        enum class AlgorithmType : std::int32_t {
            /**
             * Using the pyramidal Lucas-Kanade optical flow method.
             */
            LUCAS_KANADE_OPTICAL_FLOW
        };
        /**
         * Motion estimator algorithm type.
         */
        AlgorithmType algorithmType = AlgorithmType::LUCAS_KANADE_OPTICAL_FLOW;

        /**
         * Optical flow configuration structure.
         */
        struct OpticalFlow {
            static constexpr const std::int32_t AUTO = 0;

            /**
             * Number of pyramid levels, only for optical flow.
             * AUTO means it's decided based on input resolution: 3 if image width <= 640, else 4.
             * Valid values are either 3/4 for VGA, 4 for 720p and above.
             */
            std::int32_t pyramidLevels = AUTO;

            /**
             * Image patch width used to track features.
             * Must be an odd number, maximum 9.
             * N means the algorithm will be able to track motion at most (N-1)/2 pixels in a direction per pyramid level.
             * Increasing this number increases runtime
             */
            std::int32_t searchWindowWidth = 5;
            /**
             * Image patch height used to track features.
             * Must be an odd number, maximum 9.
             * N means the algorithm will be able to track motion at most (N-1)/2 pixels in a direction per pyramid level.
             * Increasing this number increases runtime
             */
            std::int32_t searchWindowHeight = 5;

            /**
             * Feature tracking termination criteria.
             * Optical flow will refine the feature position on each pyramid level until
             * the displacement between two refinements is smaller than this value.
             * Decreasing this number increases runtime.
             */
            float epsilon = 0.01;

            /**
             * Feature tracking termination criteria. Optical flow will refine the feature position maximum this many times
             * on each pyramid level. If the Epsilon criteria described in the previous chapter is not met after this number
             * of iterations, the algorithm will continue with the current calculated value.
             * Increasing this number increases runtime.
             */
            std::int32_t maxIterations = 9;
        };

        /**
         * Optical flow configuration.
         * Takes effect only if MotionEstimator algorithm type set to LUCAS_KANADE_OPTICAL_FLOW.
         */
        OpticalFlow opticalFlow;
    };

    /**
     * Used for feature filtering.
     */
    struct FeatureMaintainer {
        bool enable = true;

        /**
         * Used to filter out detected feature points that are too close.
         * Unit of measurement is squared euclidian distance in pixels.
         */
        float minimumDistanceBetweenFeatures = 50;

        /**
         * Optical flow measures the tracking error for every feature.
         * If the point can’t be tracked or it’s out of the image it will set this error to a maximum value.
         * This threshold defines the level where the tracking accuracy is considered too bad to keep the point.
         */
        float lostFeatureErrorThreshold = 50000;

        /**
         * Once a feature was detected and we started tracking it, we need to update its Harris score on each image.
         * This is needed because a feature point can disappear, or it can become too weak to be tracked. This
         * threshold defines the point where such a feature must be dropped.
         * As the goal of the algorithm is to provide longer tracks, we try to add strong points and track them until
         * they are absolutely untrackable. This is why, this value is usually smaller than the detection threshold.
         */
        float trackedFeatureThreshold = 200000;
    };

    /**
     * Corner detector configuration.
     * Used for feature detection.
     */
    CornerDetector cornerDetector;

    /**
     * Motion estimator configuration.
     * Used for feature reidentification between current and previos features.
     */
    MotionEstimator motionEstimator;

    /**
     * FeatureMaintainer configuration.
     * Used for feature maintaining.
     */
    FeatureMaintainer featureMaintainer;
};

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(FeatureTrackerConfigData::CornerDetector::Thresholds, initialValue, min, max, decreaseFactor, increaseFactor);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
    FeatureTrackerConfigData::CornerDetector, algorithmType, cellGridDimension, targetNumFeatures, maxNumFeatures, thresholds, enableSobel, enableSorting);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
    FeatureTrackerConfigData::MotionEstimator::OpticalFlow, pyramidLevels, searchWindowWidth, searchWindowHeight, epsilon, maxIterations);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(FeatureTrackerConfigData::MotionEstimator, enable, algorithmType, opticalFlow);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
    FeatureTrackerConfigData::FeatureMaintainer, enable, minimumDistanceBetweenFeatures, lostFeatureErrorThreshold, trackedFeatureThreshold);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(FeatureTrackerConfigData, cornerDetector, motionEstimator, featureMaintainer);

/// RawFeatureTrackerConfig configuration structure
struct RawFeatureTrackerConfig : public RawBuffer {
    FeatureTrackerConfigData config;

    void serialize(std::vector<std::uint8_t>& metadata, DatatypeEnum& datatype) const override {
        nlohmann::json j = *this;
        metadata = nlohmann::json::to_msgpack(j);
        datatype = DatatypeEnum::FeatureTrackerConfig;
    };

    NLOHMANN_DEFINE_TYPE_INTRUSIVE(RawFeatureTrackerConfig, config);
};

}  // namespace dai
<commit_msg>Add HW motion estimator algorith type to motion estimator config<commit_after>#pragma once
#include <cstdint>
#include <nlohmann/json.hpp>
#include <vector>

#include "DatatypeEnum.hpp"
#include "RawBuffer.hpp"

namespace dai {

/// FeatureTracker configuration data structure
struct FeatureTrackerConfigData {
    struct CornerDetector {
        static constexpr const int AUTO = 0;

        enum class AlgorithmType : std::int32_t {
            /**
             * Harris corner detector.
             */
            HARRIS,
            /**
             * Shi-Thomasi corner detector.
             */
            SHI_THOMASI
        };
        /**
         * Corner detector algorithm type.
         */
        AlgorithmType algorithmType = AlgorithmType::HARRIS;

        /**
         * Ensures distributed feature detection across the image.
         * Image is divided into horizontal and vertical cells,
         * each cell has a target feature count = targetNumFeatures / cellGridDimension.
         * Each cell has it's own feature threshold.
         * A value of 4 means that the image is divided into 4x4 cells of equal width/height.
         * Maximum 4, minimum 1.
         */
        std::int32_t cellGridDimension = 4;

        /**
         * Target number of features to detect.
         * Maximum number of features is determined at runtime based on algorithm type.
         */
        std::int32_t targetNumFeatures = 320;

        /**
         * Hard limit for the maximum number of features that can be detected.
         * 0 means auto, will be set to the maximum value based on memory constraints.
         */
        std::int32_t maxNumFeatures = AUTO;

        /**
         * Enable 3x3 sobel operator to smoothen the image whose gradient is be computed.
         * If disabled a simple 1D row/column differentiator is used for gradient.
         */
        bool enableSobel = true;

        /**
         * Enable sorting detected features based on their score or not.
         */
        bool enableSorting = true;

        /**
         * Threshold settings structure for corner detector.
         */
        struct Thresholds {
            static constexpr const float AUTO = 0;

            /**
             * Minimum strength of a feature which will be detected.
             * 0 means automatic threshold update. Recommended so the tracker can adapt to different scenes/textures.
             * Each chell has its own threshold.
             * Empirical value.
             */
            float initialValue = AUTO;

            /**
             * Minimum limit for threshold.
             * Applicable when automatic threshold update is enabled.
             * 0 means auto, 6000000 for HARRIS, 1200 for SHI_THOMASI
             * Empirical value.
             */
            float min = AUTO;

            /**
             * Maximum limit for threshold.
             * Applicable when automatic threshold update is enabled.
             * 0 means auto.
             * Empirical value.
             */
            float max = AUTO;

            /**
             * When detected number of features exceeds the maximum in a cell threshold is lowered
             * by multiplying its value with this factor.
             */
            float decreaseFactor = 0.9;

            /**
             * When detected number of features doesn't exceed the maximum in a cell threshold is increased
             * by multiplying its value with this factor.
             */
            float increaseFactor = 1.1;
        };

        /**
         * Threshold settings.
         * These are advanced settings, suitable for debugging/special cases.
         */
        Thresholds thresholds;
    };

    /**
     * Used for feature reidentification between current and previous features.
     */
    struct MotionEstimator {
        bool enable = true;

        enum class AlgorithmType : std::int32_t {
            /**
             * Using the pyramidal Lucas-Kanade optical flow method.
             */
            LUCAS_KANADE_OPTICAL_FLOW,
            /**
             * Using a dense motion estimation hardware block (Block matcher).
             */
            HW_MOTION_ESTIMATION
        };
        /**
         * Motion estimator algorithm type.
         */
        AlgorithmType algorithmType = AlgorithmType::LUCAS_KANADE_OPTICAL_FLOW;

        /**
         * Optical flow configuration structure.
         */
        struct OpticalFlow {
            static constexpr const std::int32_t AUTO = 0;

            /**
             * Number of pyramid levels, only for optical flow.
             * AUTO means it's decided based on input resolution: 3 if image width <= 640, else 4.
             * Valid values are either 3/4 for VGA, 4 for 720p and above.
             */
            std::int32_t pyramidLevels = AUTO;

            /**
             * Image patch width used to track features.
             * Must be an odd number, maximum 9.
             * N means the algorithm will be able to track motion at most (N-1)/2 pixels in a direction per pyramid level.
             * Increasing this number increases runtime
             */
            std::int32_t searchWindowWidth = 5;
            /**
             * Image patch height used to track features.
             * Must be an odd number, maximum 9.
             * N means the algorithm will be able to track motion at most (N-1)/2 pixels in a direction per pyramid level.
             * Increasing this number increases runtime
             */
            std::int32_t searchWindowHeight = 5;

            /**
             * Feature tracking termination criteria.
             * Optical flow will refine the feature position on each pyramid level until
             * the displacement between two refinements is smaller than this value.
             * Decreasing this number increases runtime.
             */
            float epsilon = 0.01;

            /**
             * Feature tracking termination criteria. Optical flow will refine the feature position maximum this many times
             * on each pyramid level. If the Epsilon criteria described in the previous chapter is not met after this number
             * of iterations, the algorithm will continue with the current calculated value.
             * Increasing this number increases runtime.
             */
            std::int32_t maxIterations = 9;
        };

        /**
         * Optical flow configuration.
         * Takes effect only if MotionEstimator algorithm type set to LUCAS_KANADE_OPTICAL_FLOW.
         */
        OpticalFlow opticalFlow;
    };

    /**
     * Used for feature filtering.
     */
    struct FeatureMaintainer {
        bool enable = true;

        /**
         * Used to filter out detected feature points that are too close.
         * Unit of measurement is squared euclidian distance in pixels.
         */
        float minimumDistanceBetweenFeatures = 50;

        /**
         * Optical flow measures the tracking error for every feature.
         * If the point can’t be tracked or it’s out of the image it will set this error to a maximum value.
         * This threshold defines the level where the tracking accuracy is considered too bad to keep the point.
         */
        float lostFeatureErrorThreshold = 50000;

        /**
         * Once a feature was detected and we started tracking it, we need to update its Harris score on each image.
         * This is needed because a feature point can disappear, or it can become too weak to be tracked. This
         * threshold defines the point where such a feature must be dropped.
         * As the goal of the algorithm is to provide longer tracks, we try to add strong points and track them until
         * they are absolutely untrackable. This is why, this value is usually smaller than the detection threshold.
         */
        float trackedFeatureThreshold = 200000;
    };

    /**
     * Corner detector configuration.
     * Used for feature detection.
     */
    CornerDetector cornerDetector;

    /**
     * Motion estimator configuration.
     * Used for feature reidentification between current and previos features.
     */
    MotionEstimator motionEstimator;

    /**
     * FeatureMaintainer configuration.
     * Used for feature maintaining.
     */
    FeatureMaintainer featureMaintainer;
};

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(FeatureTrackerConfigData::CornerDetector::Thresholds, initialValue, min, max, decreaseFactor, increaseFactor);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
    FeatureTrackerConfigData::CornerDetector, algorithmType, cellGridDimension, targetNumFeatures, maxNumFeatures, thresholds, enableSobel, enableSorting);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
    FeatureTrackerConfigData::MotionEstimator::OpticalFlow, pyramidLevels, searchWindowWidth, searchWindowHeight, epsilon, maxIterations);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(FeatureTrackerConfigData::MotionEstimator, enable, algorithmType, opticalFlow);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
    FeatureTrackerConfigData::FeatureMaintainer, enable, minimumDistanceBetweenFeatures, lostFeatureErrorThreshold, trackedFeatureThreshold);
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(FeatureTrackerConfigData, cornerDetector, motionEstimator, featureMaintainer);

/// RawFeatureTrackerConfig configuration structure
struct RawFeatureTrackerConfig : public RawBuffer {
    FeatureTrackerConfigData config;

    void serialize(std::vector<std::uint8_t>& metadata, DatatypeEnum& datatype) const override {
        nlohmann::json j = *this;
        metadata = nlohmann::json::to_msgpack(j);
        datatype = DatatypeEnum::FeatureTrackerConfig;
    };

    NLOHMANN_DEFINE_TYPE_INTRUSIVE(RawFeatureTrackerConfig, config);
};

}  // namespace dai
<|endoftext|>


<commit_before>/***************************************************************************
 *   Copyright (C) 2012 by the fifechan team                               *
 *   http://fifechan.github.com/fifechan                                   *
 *   This file is part of fifechan.                                        *
 *                                                                         *
 *   fifechan is free software; you can redistribute it and/or             *
 *   modify it under the terms of the GNU Lesser General Public            *
 *   License as published by the Free Software Foundation; either          *
 *   version 2.1 of the License, or (at your option) any later version.    *
 *                                                                         *
 *   This library is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
 *   Lesser General Public License for more details.                       *
 *                                                                         *
 *   You should have received a copy of the GNU Lesser General Public      *
 *   License along with this library; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
 ***************************************************************************/

/*
 * For comments regarding functions please see the header file.
 */

#include "fifechan/widgets/flowcontainer.hpp"

#include "fifechan/exception.hpp"

namespace fcn {

    FlowContainer::FlowContainer():
        mAlignment(Center) {
        //setBorderSize(1);
        //setPadding(2);
        setOpaque(true);
    }

    FlowContainer::~FlowContainer() {
    }

    void FlowContainer::setAlignment(FlowContainer::Alignment alignment) {
        mAlignment = alignment;
    }

    FlowContainer::Alignment FlowContainer::getAlignment() const {
        return mAlignment;
    }

    void FlowContainer::adjustContent() {
        // diff means border, padding, ...
        int diffW = getDimension().width - getChildrenArea().width;
        int diffH = getDimension().height - getChildrenArea().height;

        // max size that can be used for layouting
        // if no size is set the parent or max size is used
        int containerW = getWidth();
        int containerH = getHeight();
        if (getParent()) {
            // width
            if (containerW - diffW < 1) {
                containerW = getParent()->getChildrenArea().width;
            } else {
                containerW = std::min(containerW, getParent()->getChildrenArea().width);
            }
            containerW = std::max(containerW, getMinSize().getWidth());
            containerW = std::min(containerW, getMaxSize().getWidth()) - diffW;;
            // height
            if (containerH - diffH < 1) {
                containerH = getParent()->getChildrenArea().height;
            } else {
                containerH = std::min(containerH, getParent()->getChildrenArea().height);
            }
            containerH = std::max(containerH, getMinSize().getHeight());
            containerH = std::min(containerH, getMaxSize().getHeight()) - diffH;
        } else {
            containerW -= diffW;
            containerH -= diffH;
            if (containerW < 1) {
                containerW = getMaxSize().getWidth() - diffW;
            }
            if (containerH < 1) {
                containerH = getMaxSize().getHeight() - diffH;
            }
        }

        // calculates max layout size per column or row
        std::vector<int> layoutMax;
        int tmpSize = 0;
        int x = 0;
        int y = 0;
        int visibleChilds = 0;
        std::list<Widget*>::const_iterator currChild(mChildren.begin());
        std::list<Widget*>::const_iterator endChildren(mChildren.end());
        for(; currChild != endChildren; ++currChild) {
            Widget* child = *currChild;
            if (!child->isVisible()) {
                continue;
            }
            ++visibleChilds;
            const Rectangle& rec = child->getDimension();
            if (mLayout == Vertical) {
                // new column
                // negative bottom margin have no effect here
                if (y + child->getMarginTop() + rec.height + (child->getMarginBottom() > 0 ? child->getMarginBottom() : 0) > containerH) {
                    y = 0;
                    layoutMax.push_back(tmpSize);
                    tmpSize = 0;
                    continue;
                }
                y += rec.height + child->getMarginTop() + child->getMarginBottom() + getVerticalSpacing();
                tmpSize = std::max(tmpSize, rec.width + child->getMarginLeft() + (child->getMarginRight() > 0 ? child->getMarginRight() : 0));
            } else {
                // new row
                // negative right margin have no effect here
                if (x + rec.width + child->getMarginLeft() + (child->getMarginRight() > 0 ? child->getMarginRight() : 0) > containerW) {
                    x = 0;
                    layoutMax.push_back(tmpSize);
                    tmpSize = 0;
                    continue;
                }
                x += rec.width + child->getMarginLeft() + child->getMarginRight() + getHorizontalSpacing();
                tmpSize = std::max(tmpSize, rec.height + child->getMarginTop() + (child->getMarginBottom() > 0 ? child->getMarginBottom() : 0));
            }
        }
        if (tmpSize != 0) {
            layoutMax.push_back(tmpSize);
        }

        // places all widgets
        x = 0;
        y = 0;
        int totalW = 0;
        int totalH = 0;
        unsigned int layout = 0;
        if (mLayout == Vertical && visibleChilds > 0) {
            currChild = mChildren.begin();
            endChildren = mChildren.end();
            for(; currChild != endChildren; ++currChild) {
                if (!(*currChild)->isVisible()) {
                    continue;
                }
                int columnW = layoutMax[layout];
                int layoutW = (*currChild)->getWidth() + (*currChild)->getMarginLeft() + ((*currChild)->getMarginRight() > 0 ? (*currChild)->getMarginRight() : 0);
                Rectangle dim((*currChild)->getMarginLeft(), (*currChild)->getMarginTop(), layoutW, (*currChild)->getHeight());

                // new column
                // negative bottom margin have no effect here
                if (y + (*currChild)->getMarginTop() + dim.height + ((*currChild)->getMarginBottom() > 0 ? (*currChild)->getMarginBottom() : 0) > containerH) {
                    x += columnW + getHorizontalSpacing();
                    y = 0;
                    ++layout;
                    columnW = layoutMax[layout];
                }
                dim.y += y;

                switch (getAlignment()) {
                  case Left:
                      dim.x += x;
                      break;
                  case Center:
                      dim.x += x + (columnW - layoutW) / 2;
                      break;
                  case Right:
                      dim.x += x + (columnW - layoutW);
                      break;
                  default:
                      throw FCN_EXCEPTION("Unknown alignment.");
                }

                (*currChild)->setDimension(dim);
                y += (*currChild)->getHeight() + (*currChild)->getMarginTop() + (*currChild)->getMarginBottom() + getVerticalSpacing();
                totalW = std::max(totalW, dim.x + dim.width);
                totalH = std::max(totalH, y);
            }
            // remove last spacing
            totalH -= getVerticalSpacing();
        } else if (mLayout == Horizontal && visibleChilds > 0) {
            currChild = mChildren.begin();
            endChildren = mChildren.end();
            for(; currChild != endChildren; ++currChild) {
                if (!(*currChild)->isVisible()) {
                    continue;
                }
                int rowH = layoutMax[layout];
                int layoutH = (*currChild)->getHeight() + (*currChild)->getMarginTop() + ((*currChild)->getMarginBottom() > 0 ? (*currChild)->getMarginBottom() : 0);
                Rectangle dim((*currChild)->getMarginLeft(), (*currChild)->getMarginTop(), (*currChild)->getWidth(), layoutH);

                // new row
                // negative right margin have no effect here
                if (x + (*currChild)->getMarginLeft() + dim.width + ((*currChild)->getMarginRight() > 0 ? (*currChild)->getMarginRight() : 0) > containerW) {
                    x = 0;
                    y += rowH + getVerticalSpacing();
                    ++layout;
                    rowH = layoutMax[layout];
                }
                dim.x += x;

                switch (getAlignment()) {
                  case Top:
                      dim.y += y;
                      break;
                  case Center:
                      dim.y += y + (rowH - layoutH) / 2;
                      break;
                  case Bottom:
                      dim.y += y + (rowH - layoutH);
                      break;
                  default:
                      throw FCN_EXCEPTION("Unknown alignment.");
                }

                (*currChild)->setDimension(dim);
                x += (*currChild)->getWidth() + (*currChild)->getMarginLeft() + (*currChild)->getMarginRight() + getHorizontalSpacing();
                totalW = std::max(totalW, x);
                totalH = std::max(totalH, dim.y + dim.width);

            }
            // remove last spacing
            totalW -= getHorizontalSpacing();
        }

        // resize container
        totalW += diffW;
        totalH += diffH;
        setSize(totalW, totalH);
    }

    void FlowContainer::setLayout(Container::LayoutPolicy policy) {
        if (policy == Circular) {
            throw FCN_EXCEPTION("Circular layout is not implemented for the FlowContainer.");
        } else {
            Container::setLayout(policy);
        }
    }

    void FlowContainer::resizeToContent(bool recursiv) {
        if (recursiv) {
            std::list<Widget*>::const_iterator currChild(mChildren.begin());
            std::list<Widget*>::const_iterator endChildren(mChildren.end());
            for(; currChild != endChildren; ++currChild) {
                if (!(*currChild)->isVisible()) {
                    continue;
                }
                (*currChild)->resizeToContent(recursiv);
            }
        }
        if (mLayout != Absolute) {
            adjustContent();
        }
    }

    void FlowContainer::expandContent(bool recursiv) {
        if (mLayout != Absolute) {
            adjustContent();
        }
        // not really needed
        if (recursiv) {
            std::list<Widget*>::const_iterator currChild(mChildren.begin());
            std::list<Widget*>::const_iterator endChildren(mChildren.end());
            for(; currChild != endChildren; ++currChild) {
                if (!(*currChild)->isVisible()) {
                    continue;
                }
                (*currChild)->expandContent(recursiv);
            }
        }
    }

}
<commit_msg> * Fixed layouting problems with FlowContainer. The resizeToContent() call sets the size to min size if the widget have a parent.<commit_after>/***************************************************************************
 *   Copyright (C) 2012 by the fifechan team                               *
 *   http://fifechan.github.com/fifechan                                   *
 *   This file is part of fifechan.                                        *
 *                                                                         *
 *   fifechan is free software; you can redistribute it and/or             *
 *   modify it under the terms of the GNU Lesser General Public            *
 *   License as published by the Free Software Foundation; either          *
 *   version 2.1 of the License, or (at your option) any later version.    *
 *                                                                         *
 *   This library is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
 *   Lesser General Public License for more details.                       *
 *                                                                         *
 *   You should have received a copy of the GNU Lesser General Public      *
 *   License along with this library; if not, write to the                 *
 *   Free Software Foundation, Inc.,                                       *
 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA          *
 ***************************************************************************/

/*
 * For comments regarding functions please see the header file.
 */

#include "fifechan/widgets/flowcontainer.hpp"

#include "fifechan/exception.hpp"

namespace fcn {

    FlowContainer::FlowContainer():
        mAlignment(Center) {
        setOpaque(true);
    }

    FlowContainer::~FlowContainer() {
    }

    void FlowContainer::setAlignment(FlowContainer::Alignment alignment) {
        mAlignment = alignment;
    }

    FlowContainer::Alignment FlowContainer::getAlignment() const {
        return mAlignment;
    }

    void FlowContainer::adjustContent() {
        // diff means border, padding, ...
        int diffW = ABS(getDimension().width - getChildrenArea().width);
        int diffH = ABS(getDimension().height - getChildrenArea().height);

        int containerW = getChildrenArea().width;
        int containerH = getChildrenArea().height;

        // calculates max layout size per column or row
        std::vector<int> layoutMax;
        int tmpSize = 0;
        int x = 0;
        int y = 0;
        int visibleChilds = 0;
        std::list<Widget*>::const_iterator currChild(mChildren.begin());
        std::list<Widget*>::const_iterator endChildren(mChildren.end());
        for(; currChild != endChildren; ++currChild) {
            Widget* child = *currChild;
            if (!child->isVisible()) {
                continue;
            }
            ++visibleChilds;
            const Rectangle& rec = child->getDimension();
            if (mLayout == Vertical) {
                // new column
                // negative bottom margin have no effect here
                if (y + child->getMarginTop() + rec.height + (child->getMarginBottom() > 0 ? child->getMarginBottom() : 0) > containerH) {
                    y = 0;
                    layoutMax.push_back(tmpSize);
                    tmpSize = 0;
                }
                y += rec.height + child->getMarginTop() + child->getMarginBottom() + getVerticalSpacing();
                tmpSize = std::max(tmpSize, rec.width + child->getMarginLeft() + (child->getMarginRight() > 0 ? child->getMarginRight() : 0));
            } else {
                // new row
                // negative right margin have no effect here
                if (x + rec.width + child->getMarginLeft() + (child->getMarginRight() > 0 ? child->getMarginRight() : 0) > containerW) {
                    x = 0;
                    layoutMax.push_back(tmpSize);
                    tmpSize = 0;
                }
                x += rec.width + child->getMarginLeft() + child->getMarginRight() + getHorizontalSpacing();
                tmpSize = std::max(tmpSize, rec.height + child->getMarginTop() + (child->getMarginBottom() > 0 ? child->getMarginBottom() : 0));
            }
        }
        if (tmpSize != 0) {
            layoutMax.push_back(tmpSize);
        }

        // places all widgets
        x = 0;
        y = 0;
        int totalW = 0;
        int totalH = 0;
        unsigned int layout = 0;
        if (mLayout == Vertical && visibleChilds > 0) {
            currChild = mChildren.begin();
            endChildren = mChildren.end();
            for(; currChild != endChildren; ++currChild) {
                if (!(*currChild)->isVisible()) {
                    continue;
                }
                int columnW = layoutMax[layout];
                int layoutW = (*currChild)->getWidth() + (*currChild)->getMarginLeft() + ((*currChild)->getMarginRight() > 0 ? (*currChild)->getMarginRight() : 0);
                Rectangle dim((*currChild)->getMarginLeft(), (*currChild)->getMarginTop(), layoutW, (*currChild)->getHeight());

                // new column
                // negative bottom margin have no effect here
                if (y + (*currChild)->getMarginTop() + dim.height + ((*currChild)->getMarginBottom() > 0 ? (*currChild)->getMarginBottom() : 0) > containerH) {
                    x += columnW + getHorizontalSpacing();
                    y = 0;
                    ++layout;
                    columnW = layoutMax[layout];
                }
                dim.y += y;

                switch (getAlignment()) {
                  case Left:
                      dim.x += x;
                      break;
                  case Center:
                      dim.x += x + (columnW - layoutW) / 2;
                      break;
                  case Right:
                      dim.x += x + (columnW - layoutW);
                      break;
                  default:
                      throw FCN_EXCEPTION("Unknown alignment.");
                }

                (*currChild)->setDimension(dim);
                y += (*currChild)->getHeight() + (*currChild)->getMarginTop() + (*currChild)->getMarginBottom() + getVerticalSpacing();
                totalW = std::max(totalW, dim.x + dim.width);
                totalH = std::max(totalH, y);
            }
            // remove last spacing
            totalH -= getVerticalSpacing();
            // always expand height but width only if horizontal expand is enabled
            totalH = std::max(totalH, containerH);
            if (isHorizontalExpand()) {
                totalW = std::max(totalW, containerW);
            }
        } else if (mLayout == Horizontal && visibleChilds > 0) {
            currChild = mChildren.begin();
            endChildren = mChildren.end();
            for(; currChild != endChildren; ++currChild) {
                if (!(*currChild)->isVisible()) {
                    continue;
                }
                int rowH = layoutMax[layout];
                int layoutH = (*currChild)->getHeight() + (*currChild)->getMarginTop() + ((*currChild)->getMarginBottom() > 0 ? (*currChild)->getMarginBottom() : 0);
                Rectangle dim((*currChild)->getMarginLeft(), (*currChild)->getMarginTop(), (*currChild)->getWidth(), layoutH);

                // new row
                // negative right margin have no effect here
                if (x + (*currChild)->getMarginLeft() + dim.width + ((*currChild)->getMarginRight() > 0 ? (*currChild)->getMarginRight() : 0) > containerW) {
                    x = 0;
                    y += rowH + getVerticalSpacing();
                    ++layout;
                    rowH = layoutMax[layout];
                }
                dim.x += x;

                switch (getAlignment()) {
                  case Top:
                      dim.y += y;
                      break;
                  case Center:
                      dim.y += y + (rowH - layoutH) / 2;
                      break;
                  case Bottom:
                      dim.y += y + (rowH - layoutH);
                      break;
                  default:
                      throw FCN_EXCEPTION("Unknown alignment.");
                }

                (*currChild)->setDimension(dim);
                x += (*currChild)->getWidth() + (*currChild)->getMarginLeft() + (*currChild)->getMarginRight() + getHorizontalSpacing();
                totalW = std::max(totalW, x);
                totalH = std::max(totalH, dim.y + dim.height);

            }
            // remove last spacing
            totalW -= getHorizontalSpacing();
            // always expand width but height only if vertical expand is enabled
            totalW = std::max(totalW, containerW);
            if (isVerticalExpand()) {
                totalH = std::max(totalH, containerH);
            }
        }

        // resize container
        totalW += diffW;
        totalH += diffH;
        setSize(totalW, totalH);
    }

    void FlowContainer::setLayout(Container::LayoutPolicy policy) {
        if (policy == Circular) {
            throw FCN_EXCEPTION("Circular layout is not implemented for the FlowContainer.");
        } else {
            Container::setLayout(policy);
        }
    }

    void FlowContainer::resizeToContent(bool recursiv) {
        if (recursiv) {
            std::list<Widget*>::const_iterator currChild(mChildren.begin());
            std::list<Widget*>::const_iterator endChildren(mChildren.end());
            for(; currChild != endChildren; ++currChild) {
                if (!(*currChild)->isVisible()) {
                    continue;
                }
                (*currChild)->resizeToContent(recursiv);
            }
        }

        if (mLayout != Absolute) {
            if (getParent()) {
                setSize(getMinSize().getWidth(), getMinSize().getHeight());
            }
        }
    }

    void FlowContainer::expandContent(bool recursiv) {
        if (mLayout != Absolute) {
            adjustContent();
        }
        // not really needed
        if (recursiv) {
            std::list<Widget*>::const_iterator currChild(mChildren.begin());
            std::list<Widget*>::const_iterator endChildren(mChildren.end());
            for(; currChild != endChildren; ++currChild) {
                if (!(*currChild)->isVisible()) {
                    continue;
                }
                (*currChild)->expandContent(recursiv);
            }
        }
    }

}
<|endoftext|>


<commit_before>#include <iostream>
#include <cmath>
#include <fstream>
#include <map>
#include <sstream>
#include <stdexcept>

#include "cvrp.h"

namespace VrpSolver {

    void Cvrp::read_vrp(const std::string &infile) {
        VrpSolver::read_vrp(problem_, infile);

        // 車体数の設定(TSPLIB formatでは明示的に示されていないので
        // ファイル名から読み取る
        size_t i = infile.rfind('k');
        size_t j = infile.rfind('.');
        std::istringstream iss(infile.substr(i+1, j));
        iss >> num_vehicles_;
        if (!iss)
            throw std::runtime_error("error: can not read number of vehicles");
    }

    std::string Cvrp::name() const {
        return problem_->name_;
    }

    unsigned int Cvrp::dimension() const {
        return problem_->dimension_;
    }

    unsigned int Cvrp::capacity() const {
        return problem_->capacity_;
    }

    unsigned int Cvrp::demand(unsigned int node_id) const {
        if ((1 > node_id) || (node_id > problem_->dimension_))
            throw std::out_of_range("error: in Cvrp::demand");
        return problem_->customers_[node_id].demand();
    }

    unsigned int Cvrp::num_vehicles() const {
        return num_vehicles_;
    }

    unsigned int Cvrp::distance(unsigned int from, unsigned int to) const {
        if ((1 > from) || (from > problem_->dimension_) ||
            (1 > to) || (to > problem_->dimension_))
            throw std::out_of_range("error: in Cvrp::distance");

        const int index = (to > from) ? ((to-2)*(to-1)/2+(from-1)) :
                                        ((from-2)*(from-1)/2+(to-1));
        return problem_->distances_[index];
    }

    const DistanceList &Cvrp::distance_list() const {
        return problem_->distances_;
    }

    const CustomerList &Cvrp::customer_list() const {
        return problem_->customers_;
    }

    unsigned int distance(const DistanceList& dlist,
                          const Customer& c1, const Customer& c2) {
        const int from = c1.id();
        const int to   = c2.id();
        const int index = (to > from) ? ((to-2)*(to-1)/2+(from-1)) :
                                        ((from-2)*(from-1)/2+(to-1));
        return dlist[index];
    }

    // 文字列strからtrim_char文字列に含まれている文字を削除
    void trim(std::string& str, const std::string& trim_char) {
        size_t pos;
        while ((pos = str.find_first_of(trim_char)) != std::string::npos)
            str.erase(pos, 1);
    }

    // セミコロン以後の文字列(空白の直前まで)を読み取る
    std::string get_parameter(std::ifstream& ifs) {
        std::string param;
        ifs >> param;
        while (param == ":") ifs >> param; // ":"は読み飛ばす
        return param;
    }

    typedef std::string keyword;

    enum TsplibKeyword {
        // The specification part
        NAME, TYPE, COMMENT, DIMENSION, CAPACITY,
        EDGE_WEIGHT_TYPE, EDGE_WEIGHT_FORMAT, EDGE_DATA_FORMAT,
        NODE_COORD_TYPE, DISPLAY_DATA_TYPE, END_OF_FILE,

        // The data part
        NODE_COORD_SECTION, DEPOT_SECTION, DEMAND_SECTION,
        EDGE_DATA_SECTION, EDGE_WEIGHT_SECTION,
    };

    std::map<keyword, TsplibKeyword> keyword_map = {
        // The specification part
        { "NAME",                NAME },
        { "TYPE",                TYPE },
        { "COMMENT",             COMMENT },
        { "DIMENSION",           DIMENSION },
        { "CAPACITY",            CAPACITY },
        { "EDGE_WEIGHT_TYPE",    EDGE_WEIGHT_TYPE },
        { "EDGE_WEIGHT_FORMAT",  EDGE_WEIGHT_FORMAT },
        { "EDGE_DATA_FORMAT",    EDGE_DATA_FORMAT },
        { "NODE_COORD_TYPE",     NODE_COORD_TYPE },
        { "DISPLAY_DATA_TYPE",   DISPLAY_DATA_TYPE },
        { "EOF",                 END_OF_FILE },

        // The data part
        { "NODE_COORD_SECTION",  NODE_COORD_SECTION },
        { "DEPOT_SECTION",       DEPOT_SECTION },
        { "DEMAND_SECTION",      DEMAND_SECTION },
        { "EDGE_DATA_SECTION",   EDGE_DATA_SECTION },
        { "EDGE_WEIGHT_SECTION", EDGE_WEIGHT_SECTION }
    };

    enum EdgeWeightType {
        EXPLICIT, EUC_2D
    };

    std::map<keyword, EdgeWeightType> ew_type_map = {
        { "EXPLICIT", EXPLICIT },
        { "EUC_2D",   EUC_2D }
    };

    enum EdgeWeightFormat {
        LOWER_ROW
    };

    std::map<keyword, EdgeWeightFormat> ew_format_map = {
        { "LOWER_ROW", LOWER_ROW }
    };

    // infileから情報を読み取りCvrpクラスをセットアップする
    void read_vrp(Problem *problem, const std::string &infile) {
        std::ifstream ifs(infile.c_str());
        if (!ifs)
            throw std::runtime_error("error: can't open file " + infile);

        EdgeWeightType   edge_weight_type;
        EdgeWeightFormat edge_weight_format;

        while (ifs) {
            keyword tsp_keyword;
            ifs >> tsp_keyword;
            if (ifs.eof()) break;
            trim(tsp_keyword, " :");
            switch (keyword_map[tsp_keyword]) {

                // The specification part
                case NAME :
                    problem->name_ = get_parameter(ifs);
                    break;
                case TYPE :
                    {
                        std::string not_use;
                        getline(ifs, not_use);
                    }
                    break;
                case COMMENT :
                    {
                        std::string not_use;
                        getline(ifs, not_use);
                    }
                    break;
                case DIMENSION :
                    problem->dimension_ = stoi(get_parameter(ifs));
                    break;
                case CAPACITY :
                    problem->capacity_ = stoi(get_parameter(ifs));
                    break;
                case EDGE_WEIGHT_TYPE :
                    edge_weight_type = ew_type_map[get_parameter(ifs)];
                    break;
                case EDGE_WEIGHT_FORMAT :
                    edge_weight_format = ew_format_map[get_parameter(ifs)];
                    break;
                case EDGE_DATA_FORMAT :
                    {
                        std::string not_use;
                        getline(ifs, not_use);
                    }
                    break;
                case NODE_COORD_TYPE :
                    {
                        std::string not_use;
                        getline(ifs, not_use);
                    }
                    break;
                case DISPLAY_DATA_TYPE :
                    {
                        std::string not_use;
                        getline(ifs, not_use);
                    }
                    break;
                case END_OF_FILE :
                    // do nothing
                    break;

                // The data part
                case NODE_COORD_SECTION :
                    {
                        int n=0, m, x, y; // m do not use
                        for (int i=0; i != problem->dimension_; i++) {
                            ifs >> m >> x >> y;
                            std::pair<int,int> c(x,y);
                            problem->coords_.push_back(c);
                        }
                    }
                    break;
                case DEPOT_SECTION :
                    {
                        problem->depot_ = stoi(get_parameter(ifs));
                        if (stoi(get_parameter(ifs)) != -1)
                            throw std::runtime_error("error:"
                                    "can't handle multiple depots");
                    }
                    break;
                case DEMAND_SECTION :
                    {
                        problem->customers_.push_back(Customer(0,0)); // 0番目は使用しない
                        for (int i=1; i <= problem->dimension_; i++) {
                            unsigned int node_id, demand;
                            ifs >> node_id >> demand;
                            if (node_id != i)
                                throw std::runtime_error("error:"
                                        "DEMAND_SECTION format may be different");
                            problem->customers_.push_back(Customer(node_id, demand));
                        }
                    }
                    break;
                case EDGE_DATA_SECTION :
                    throw std::runtime_error("Sorry, can not handle 'EDGE_DATA_SECTION'");
                    break;
                case EDGE_WEIGHT_SECTION :
                    {
                        if (edge_weight_format != LOWER_ROW)
                            throw std::runtime_error("Sorry, can not handle except EDGE_WEIGHT_FORMAT == LOWER_ROW");
                        for (int i=0; i < problem->dimension_; i++) {
                            for (int j=0; j < i; j++) {
                                int distance;
                                ifs >> distance;
                                problem->distances_.push_back(distance);
                            }
                        }
                    }
                    break;

                default :
                    throw std::runtime_error("error: unknown keyword '" + tsp_keyword + "'");
                    break;
            }
        }

        // distancesの設定
        if (edge_weight_type != EXPLICIT) {
            auto& distances = problem->distances_;
            auto& coords    = problem->coords_;
            for (int i=0; i < problem->dimension_; i++) {
                for (int j=0; j < i; j++) {
                    int dx = coords[j].first  - coords[i].first;
                    int dy = coords[j].second - coords[i].second;
                    distances.push_back(floor(sqrt(dx*dx + dy*dy)+0.5));
                }
            }
        }
    }

} // namespace VrpSolver
<commit_msg>read_vrpをヘッダーから消したためにプロトタイプ宣言が必要になった<commit_after>#include <iostream>
#include <cmath>
#include <fstream>
#include <map>
#include <sstream>
#include <stdexcept>

#include "cvrp.h"

namespace VrpSolver {

    void read_vrp(Problem *, const std::string &infile);

    void Cvrp::read_vrp(const std::string &infile) {
        VrpSolver::read_vrp(problem_, infile);

        // 車体数の設定(TSPLIB formatでは明示的に示されていないので
        // ファイル名から読み取る
        size_t i = infile.rfind('k');
        size_t j = infile.rfind('.');
        std::istringstream iss(infile.substr(i+1, j));
        iss >> num_vehicles_;
        if (!iss)
            throw std::runtime_error("error: can not read number of vehicles");
    }

    std::string Cvrp::name() const {
        return problem_->name_;
    }

    unsigned int Cvrp::dimension() const {
        return problem_->dimension_;
    }

    unsigned int Cvrp::capacity() const {
        return problem_->capacity_;
    }

    unsigned int Cvrp::demand(unsigned int node_id) const {
        if ((1 > node_id) || (node_id > problem_->dimension_))
            throw std::out_of_range("error: in Cvrp::demand");
        return problem_->customers_[node_id].demand();
    }

    unsigned int Cvrp::num_vehicles() const {
        return num_vehicles_;
    }

    unsigned int Cvrp::distance(unsigned int from, unsigned int to) const {
        if ((1 > from) || (from > problem_->dimension_) ||
            (1 > to) || (to > problem_->dimension_))
            throw std::out_of_range("error: in Cvrp::distance");

        const int index = (to > from) ? ((to-2)*(to-1)/2+(from-1)) :
                                        ((from-2)*(from-1)/2+(to-1));
        return problem_->distances_[index];
    }

    const DistanceList &Cvrp::distance_list() const {
        return problem_->distances_;
    }

    const CustomerList &Cvrp::customer_list() const {
        return problem_->customers_;
    }

    unsigned int distance(const DistanceList& dlist,
                          const Customer& c1, const Customer& c2) {
        const int from = c1.id();
        const int to   = c2.id();
        const int index = (to > from) ? ((to-2)*(to-1)/2+(from-1)) :
                                        ((from-2)*(from-1)/2+(to-1));
        return dlist[index];
    }

    // 文字列strからtrim_char文字列に含まれている文字を削除
    void trim(std::string& str, const std::string& trim_char) {
        size_t pos;
        while ((pos = str.find_first_of(trim_char)) != std::string::npos)
            str.erase(pos, 1);
    }

    // セミコロン以後の文字列(空白の直前まで)を読み取る
    std::string get_parameter(std::ifstream& ifs) {
        std::string param;
        ifs >> param;
        while (param == ":") ifs >> param; // ":"は読み飛ばす
        return param;
    }

    typedef std::string keyword;

    enum TsplibKeyword {
        // The specification part
        NAME, TYPE, COMMENT, DIMENSION, CAPACITY,
        EDGE_WEIGHT_TYPE, EDGE_WEIGHT_FORMAT, EDGE_DATA_FORMAT,
        NODE_COORD_TYPE, DISPLAY_DATA_TYPE, END_OF_FILE,

        // The data part
        NODE_COORD_SECTION, DEPOT_SECTION, DEMAND_SECTION,
        EDGE_DATA_SECTION, EDGE_WEIGHT_SECTION,
    };

    std::map<keyword, TsplibKeyword> keyword_map = {
        // The specification part
        { "NAME",                NAME },
        { "TYPE",                TYPE },
        { "COMMENT",             COMMENT },
        { "DIMENSION",           DIMENSION },
        { "CAPACITY",            CAPACITY },
        { "EDGE_WEIGHT_TYPE",    EDGE_WEIGHT_TYPE },
        { "EDGE_WEIGHT_FORMAT",  EDGE_WEIGHT_FORMAT },
        { "EDGE_DATA_FORMAT",    EDGE_DATA_FORMAT },
        { "NODE_COORD_TYPE",     NODE_COORD_TYPE },
        { "DISPLAY_DATA_TYPE",   DISPLAY_DATA_TYPE },
        { "EOF",                 END_OF_FILE },

        // The data part
        { "NODE_COORD_SECTION",  NODE_COORD_SECTION },
        { "DEPOT_SECTION",       DEPOT_SECTION },
        { "DEMAND_SECTION",      DEMAND_SECTION },
        { "EDGE_DATA_SECTION",   EDGE_DATA_SECTION },
        { "EDGE_WEIGHT_SECTION", EDGE_WEIGHT_SECTION }
    };

    enum EdgeWeightType {
        EXPLICIT, EUC_2D
    };

    std::map<keyword, EdgeWeightType> ew_type_map = {
        { "EXPLICIT", EXPLICIT },
        { "EUC_2D",   EUC_2D }
    };

    enum EdgeWeightFormat {
        LOWER_ROW
    };

    std::map<keyword, EdgeWeightFormat> ew_format_map = {
        { "LOWER_ROW", LOWER_ROW }
    };

    // infileから情報を読み取りCvrpクラスをセットアップする
    void read_vrp(Problem *problem, const std::string &infile) {
        std::ifstream ifs(infile.c_str());
        if (!ifs)
            throw std::runtime_error("error: can't open file " + infile);

        EdgeWeightType   edge_weight_type;
        EdgeWeightFormat edge_weight_format;

        while (ifs) {
            keyword tsp_keyword;
            ifs >> tsp_keyword;
            if (ifs.eof()) break;
            trim(tsp_keyword, " :");
            switch (keyword_map[tsp_keyword]) {

                // The specification part
                case NAME :
                    problem->name_ = get_parameter(ifs);
                    break;
                case TYPE :
                    {
                        std::string not_use;
                        getline(ifs, not_use);
                    }
                    break;
                case COMMENT :
                    {
                        std::string not_use;
                        getline(ifs, not_use);
                    }
                    break;
                case DIMENSION :
                    problem->dimension_ = stoi(get_parameter(ifs));
                    break;
                case CAPACITY :
                    problem->capacity_ = stoi(get_parameter(ifs));
                    break;
                case EDGE_WEIGHT_TYPE :
                    edge_weight_type = ew_type_map[get_parameter(ifs)];
                    break;
                case EDGE_WEIGHT_FORMAT :
                    edge_weight_format = ew_format_map[get_parameter(ifs)];
                    break;
                case EDGE_DATA_FORMAT :
                    {
                        std::string not_use;
                        getline(ifs, not_use);
                    }
                    break;
                case NODE_COORD_TYPE :
                    {
                        std::string not_use;
                        getline(ifs, not_use);
                    }
                    break;
                case DISPLAY_DATA_TYPE :
                    {
                        std::string not_use;
                        getline(ifs, not_use);
                    }
                    break;
                case END_OF_FILE :
                    // do nothing
                    break;

                // The data part
                case NODE_COORD_SECTION :
                    {
                        int n=0, m, x, y; // m do not use
                        for (int i=0; i != problem->dimension_; i++) {
                            ifs >> m >> x >> y;
                            std::pair<int,int> c(x,y);
                            problem->coords_.push_back(c);
                        }
                    }
                    break;
                case DEPOT_SECTION :
                    {
                        problem->depot_ = stoi(get_parameter(ifs));
                        if (stoi(get_parameter(ifs)) != -1)
                            throw std::runtime_error("error:"
                                    "can't handle multiple depots");
                    }
                    break;
                case DEMAND_SECTION :
                    {
                        problem->customers_.push_back(Customer(0,0)); // 0番目は使用しない
                        for (int i=1; i <= problem->dimension_; i++) {
                            unsigned int node_id, demand;
                            ifs >> node_id >> demand;
                            if (node_id != i)
                                throw std::runtime_error("error:"
                                        "DEMAND_SECTION format may be different");
                            problem->customers_.push_back(Customer(node_id, demand));
                        }
                    }
                    break;
                case EDGE_DATA_SECTION :
                    throw std::runtime_error("Sorry, can not handle 'EDGE_DATA_SECTION'");
                    break;
                case EDGE_WEIGHT_SECTION :
                    {
                        if (edge_weight_format != LOWER_ROW)
                            throw std::runtime_error("Sorry, can not handle except EDGE_WEIGHT_FORMAT == LOWER_ROW");
                        for (int i=0; i < problem->dimension_; i++) {
                            for (int j=0; j < i; j++) {
                                int distance;
                                ifs >> distance;
                                problem->distances_.push_back(distance);
                            }
                        }
                    }
                    break;

                default :
                    throw std::runtime_error("error: unknown keyword '" + tsp_keyword + "'");
                    break;
            }
        }

        // distancesの設定
        if (edge_weight_type != EXPLICIT) {
            auto& distances = problem->distances_;
            auto& coords    = problem->coords_;
            for (int i=0; i < problem->dimension_; i++) {
                for (int j=0; j < i; j++) {
                    int dx = coords[j].first  - coords[i].first;
                    int dy = coords[j].second - coords[i].second;
                    distances.push_back(floor(sqrt(dx*dx + dy*dy)+0.5));
                }
            }
        }
    }

} // namespace VrpSolver
<|endoftext|>


<commit_before>#include <accumulate.hpp>
#include "helpers.hpp"

#include <vector>
#include <iterator>
#include <string>

#include "catch.hpp"

using iter::accumulate;
using itertest::BasicIterable;

using Vec = const std::vector<int>;
TEST_CASE("Simple sum", "[accumulate]") {
    Vec ns{1,2,3,4,5};
    auto a = accumulate(ns);

    Vec v(std::begin(a), std::end(a));
    Vec vc{1,3,6,10,15};
    REQUIRE( v == vc );
}

TEST_CASE("accumulate: With subtraction lambda", "[accumulate]") {
    Vec ns{5,4,3,2,1};
    auto a = accumulate(ns, [](int a, int b){return a - b; });

    Vec v(std::begin(a), std::end(a));
    Vec vc{5, 1, -2, -4, -5};
    REQUIRE( v == vc );
}

TEST_CASE("accumulate: with initializer_list works", "[accumulate]") {
    auto a = accumulate({1, 2, 3});
    Vec v(std::begin(a), std::end(a));
    Vec vc{1, 3, 6};

    REQUIRE( v == vc );
}

TEST_CASE("accumulate: binds reference when it should", "[accumulate]") {
    BasicIterable<int> bi{1, 2};
    accumulate(bi);
    REQUIRE_FALSE( bi.was_moved_from() );
}

TEST_CASE("accumulate: moves rvalues when it should", "[accumulate]") {
    BasicIterable<int> bi{1, 2};
    accumulate(std::move(bi));
    REQUIRE( bi.was_moved_from() );
}

TEST_CASE("accumulate: operator==", "[accumulate]") {
    Vec v;
    auto a = accumulate(v);
    REQUIRE( std::begin(a) == std::end(a) );
}

TEST_CASE("accumulate: postfix ++", "[accumulate]") {
    Vec ns{2,3};
    auto a = accumulate(ns);
    auto it = std::begin(a);
    it++;
    REQUIRE( *it == 5 );
}

TEST_CASE("accumulate: operator->", "[accumulate]") {
    Vec ns{7, 3};
    auto a = accumulate(ns);
    auto it = std::begin(a);
    const int *p = it.operator->();
    REQUIRE( *p == 7 );
}

TEST_CASE("accumulate: iterator meets requirements", "[accumulate]") {
    Vec ns{};
    auto a = accumulate(ns);
    REQUIRE( itertest::IsIterator<decltype(std::begin(a))>::value );
}
<commit_msg>tests accumulate with lambda iters are assignable<commit_after>#include <accumulate.hpp>
#include "helpers.hpp"

#include <vector>
#include <iterator>
#include <string>

#include "catch.hpp"

using iter::accumulate;
using itertest::BasicIterable;

using Vec = const std::vector<int>;
TEST_CASE("Simple sum", "[accumulate]") {
    Vec ns{1,2,3,4,5};
    auto a = accumulate(ns);

    Vec v(std::begin(a), std::end(a));
    Vec vc{1,3,6,10,15};
    REQUIRE( v == vc );
}

TEST_CASE("accumulate: With subtraction lambda", "[accumulate]") {
    Vec ns{5,4,3,2,1};
    auto a = accumulate(ns, [](int a, int b){return a - b; });

    Vec v(std::begin(a), std::end(a));
    Vec vc{5, 1, -2, -4, -5};
    REQUIRE( v == vc );
}

TEST_CASE("accumulate: with initializer_list works", "[accumulate]") {
    auto a = accumulate({1, 2, 3});
    Vec v(std::begin(a), std::end(a));
    Vec vc{1, 3, 6};

    REQUIRE( v == vc );
}

struct Integer {
    const int value;
    constexpr Integer(int i) : value{i} { }
    constexpr Integer operator+(Integer other) const noexcept {
        return {this->value + other.value};
    }
};

TEST_CASE("accumulate: intermidate type need not be default constructible",
        "[accumulate]") {
    std::vector<Integer> v = {{2}, {3}, {10}};
    auto a = accumulate(v, std::plus<Integer>{});
    auto it = std::begin(a);
}

TEST_CASE("accumulate: binds reference when it should", "[accumulate]") {
    BasicIterable<int> bi{1, 2};
    accumulate(bi);
    REQUIRE_FALSE( bi.was_moved_from() );
}

TEST_CASE("accumulate: moves rvalues when it should", "[accumulate]") {
    BasicIterable<int> bi{1, 2};
    accumulate(std::move(bi));
    REQUIRE( bi.was_moved_from() );
}

TEST_CASE("accumulate: operator==", "[accumulate]") {
    Vec v;
    auto a = accumulate(v);
    REQUIRE( std::begin(a) == std::end(a) );
}

TEST_CASE("accumulate: postfix ++", "[accumulate]") {
    Vec ns{2,3};
    auto a = accumulate(ns);
    auto it = std::begin(a);
    it++;
    REQUIRE( *it == 5 );
}

TEST_CASE("accumulate: operator->", "[accumulate]") {
    Vec ns{7, 3};
    auto a = accumulate(ns);
    auto it = std::begin(a);
    const int *p = it.operator->();
    REQUIRE( *p == 7 );
}

TEST_CASE("accumulate: iterator meets requirements", "[accumulate]") {
    Vec ns{};
    auto a = accumulate(ns, [](int a, int b) { return a + b; });
    auto it = std::begin(a);
    it = std::begin(a);
    REQUIRE( itertest::IsIterator<decltype(std::begin(a))>::value );
}
<|endoftext|>