<commit_before>/*******************************************************************************
 * Copyright (c) 2009-2016, MAV'RIC Development Team
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 ******************************************************************************/

/*******************************************************************************
 * \file    mavrimini.cpp
 *
 * \author  MAV'RIC Team
 *
 * \brief   Autopilot board based on STM32
 *
 ******************************************************************************/

#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>

#include "boards/mavrimini.hpp"

extern "C"
{
#include "util/print_util.h"
#include "hal/common/time_keeper.hpp"
}

static Serial* p_dbg_serial;
uint8_t serial2stream(stream_data_t data, uint8_t byte)
{
    p_dbg_serial->write(&byte);
    return 0;
}

static void clock_setup(void)
{
    // Set STM32 to 168 MHz
    rcc_clock_setup_hse_3v3(&hse_25mhz_3v3[CLOCK_3V3_168MHZ]);

    // Enable GPIO clock
    rcc_periph_clock_enable(RCC_GPIOA);
    rcc_periph_clock_enable(RCC_GPIOB);
    rcc_periph_clock_enable(RCC_GPIOC);
    rcc_periph_clock_enable(RCC_GPIOD);
}

Mavrimini::Mavrimini(mavrimini_conf_t config):
    green_led_gpio(config.green_led_gpio_config),
    red_led_gpio(config.red_led_gpio_config),
    green_led(green_led_gpio),
    red_led(red_led_gpio),
    file_flash(file_flash),
    serial_1(Serial_stm32(config.serial_1_config)),
    spektrum_satellite(serial_2, dsm_receiver_gpio, dsm_power_gpio),
    adc_battery(12.3f),
    battery(adc_battery),
    servo_0(pwm_0, config.servo_config[0]),
    servo_1(pwm_1, config.servo_config[1]),
    servo_2(pwm_2, config.servo_config[2]),
    servo_3(pwm_3, config.servo_config[3]),
    servo_4(pwm_4, config.servo_config[4]),
    servo_5(pwm_5, config.servo_config[5]),
    servo_6(pwm_6, config.servo_config[6]),
    servo_7(pwm_7, config.servo_config[7]),
    sim_model(servo_0, servo_1, servo_2, servo_3),
    sim(sim_model),
    imu(sim.accelerometer(), sim.gyroscope(), sim.magnetometer())
{}

bool Mavrimini::init(void)
{
    bool init_success = true;
    bool ret;

    // -------------------------------------------------------------------------
    // Init clock
    // -------------------------------------------------------------------------
    clock_setup();
    time_keeper_init();

    // -------------------------------------------------------------------------
    // Init LEDs
    // -------------------------------------------------------------------------
    ret = green_led_gpio.init();
    ret = red_led_gpio.init();
    green_led.on();
    red_led.on();

    // -------------------------------------------------------------------------
    // Init SERIAL1
    // -------------------------------------------------------------------------
    ret = serial_1.init();
    init_success &= ret;

    // -------------------------------------------------------------------------
    // Init SERIAL2
    // -------------------------------------------------------------------------
    ret = serial_2.init();
    init_success &= ret;

    // -------------------------------------------------------------------------
    // Init stream for USB debug stream TODO: remove
    // p_dbg_serial        = &serial_1;
    p_dbg_serial         = &serial_2;
    dbg_stream_.get     = NULL;
    dbg_stream_.put     = &serial2stream;
    dbg_stream_.flush   = NULL;
    dbg_stream_.buffer_empty = NULL;
    dbg_stream_.data    = NULL;
    print_util_dbg_print_init(&dbg_stream_);
    // -------------------------------------------------------------------------

    print_util_dbg_sep('%');
    p_dbg_serial->flush();
    print_util_dbg_sep('-');
    p_dbg_serial->flush();
    print_util_dbg_print("[MAVRIMINI] ...\r\n");
    p_dbg_serial->flush();
    print_util_dbg_sep('-');
    p_dbg_serial->flush();

    print_util_dbg_init_msg("[SERIAL1]", ret);
    print_util_dbg_init_msg("[SERIAL2]", ret);
    p_dbg_serial->flush();

    // -------------------------------------------------------------------------
    // Init Servos
    // -------------------------------------------------------------------------
    ret = pwm_0.init();
    print_util_dbg_init_msg("[PWM0]", ret);
    init_success &= ret;
    servo_0.failsafe();
    p_dbg_serial->flush();
    ret = pwm_1.init();
    print_util_dbg_init_msg("[PWM1]", ret);
    init_success &= ret;
    servo_1.failsafe();
    p_dbg_serial->flush();
    ret = pwm_2.init();
    print_util_dbg_init_msg("[PWM2]", ret);
    init_success &= ret;
    servo_2.failsafe();
    ret = pwm_3.init();
    print_util_dbg_init_msg("[PWM3]", ret);
    init_success &= ret;
    servo_3.failsafe();
    p_dbg_serial->flush();
    ret = pwm_4.init();
    print_util_dbg_init_msg("[PWM4]", ret);
    init_success &= ret;
    servo_4.failsafe();
    p_dbg_serial->flush();
    ret = pwm_5.init();
    print_util_dbg_init_msg("[PWM5]", ret);
    init_success &= ret;
    servo_5.failsafe();
    p_dbg_serial->flush();
    ret = pwm_6.init();
    print_util_dbg_init_msg("[PWM6]", ret);
    init_success &= ret;
    servo_6.failsafe();
    p_dbg_serial->flush();
    ret = pwm_7.init();
    print_util_dbg_init_msg("[PWM7]", ret);
    init_success &= ret;
    servo_7.failsafe();
    p_dbg_serial->flush();

    // -------------------------------------------------------------------------
    // Init spektrum_satelitte
    // -------------------------------------------------------------------------
    ret = spektrum_satellite.init();
    print_util_dbg_init_msg("[SAT]", ret);
    init_success &= ret;
    p_dbg_serial->flush();

    print_util_dbg_sep('-');
    p_dbg_serial->flush();
    print_util_dbg_init_msg("[MAVRIMINI]", init_success);
    p_dbg_serial->flush();
    print_util_dbg_sep('-');
    p_dbg_serial->flush();

    return init_success;
}<commit_msg>removed mistake with file_flash constructeur<commit_after>/*******************************************************************************
 * Copyright (c) 2009-2016, MAV'RIC Development Team
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * 3. Neither the name of the copyright holder nor the names of its contributors
 * may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 ******************************************************************************/

/*******************************************************************************
 * \file    mavrimini.cpp
 *
 * \author  MAV'RIC Team
 *
 * \brief   Autopilot board based on STM32
 *
 ******************************************************************************/

#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>

#include "boards/mavrimini.hpp"

extern "C"
{
#include "util/print_util.h"
#include "hal/common/time_keeper.hpp"
}

static Serial* p_dbg_serial;
uint8_t serial2stream(stream_data_t data, uint8_t byte)
{
    p_dbg_serial->write(&byte);
    return 0;
}

static void clock_setup(void)
{
    // Set STM32 to 168 MHz
    rcc_clock_setup_hse_3v3(&hse_25mhz_3v3[CLOCK_3V3_168MHZ]);

    // Enable GPIO clock
    rcc_periph_clock_enable(RCC_GPIOA);
    rcc_periph_clock_enable(RCC_GPIOB);
    rcc_periph_clock_enable(RCC_GPIOC);
    rcc_periph_clock_enable(RCC_GPIOD);
}

Mavrimini::Mavrimini(mavrimini_conf_t config):
    green_led_gpio(config.green_led_gpio_config),
    red_led_gpio(config.red_led_gpio_config),
    green_led(green_led_gpio),
    red_led(red_led_gpio),
    file_flash(),
    serial_1(Serial_stm32(config.serial_1_config)),
    spektrum_satellite(serial_2, dsm_receiver_gpio, dsm_power_gpio),
    adc_battery(12.3f),
    battery(adc_battery),
    servo_0(pwm_0, config.servo_config[0]),
    servo_1(pwm_1, config.servo_config[1]),
    servo_2(pwm_2, config.servo_config[2]),
    servo_3(pwm_3, config.servo_config[3]),
    servo_4(pwm_4, config.servo_config[4]),
    servo_5(pwm_5, config.servo_config[5]),
    servo_6(pwm_6, config.servo_config[6]),
    servo_7(pwm_7, config.servo_config[7]),
    sim_model(servo_0, servo_1, servo_2, servo_3),
    sim(sim_model),
    imu(sim.accelerometer(), sim.gyroscope(), sim.magnetometer())
{}

bool Mavrimini::init(void)
{
    bool init_success = true;
    bool ret;

    // -------------------------------------------------------------------------
    // Init clock
    // -------------------------------------------------------------------------
    clock_setup();
    time_keeper_init();

    // -------------------------------------------------------------------------
    // Init LEDs
    // -------------------------------------------------------------------------
    ret = green_led_gpio.init();
    ret = red_led_gpio.init();
    green_led.on();
    red_led.on();

    // -------------------------------------------------------------------------
    // Init SERIAL1
    // -------------------------------------------------------------------------
    ret = serial_1.init();
    init_success &= ret;

    // -------------------------------------------------------------------------
    // Init SERIAL2
    // -------------------------------------------------------------------------
    ret = serial_2.init();
    init_success &= ret;

    // -------------------------------------------------------------------------
    // Init stream for USB debug stream TODO: remove
    // p_dbg_serial        = &serial_1;
    p_dbg_serial         = &serial_2;
    dbg_stream_.get     = NULL;
    dbg_stream_.put     = &serial2stream;
    dbg_stream_.flush   = NULL;
    dbg_stream_.buffer_empty = NULL;
    dbg_stream_.data    = NULL;
    print_util_dbg_print_init(&dbg_stream_);
    // -------------------------------------------------------------------------

    print_util_dbg_sep('%');
    p_dbg_serial->flush();
    print_util_dbg_sep('-');
    p_dbg_serial->flush();
    print_util_dbg_print("[MAVRIMINI] ...\r\n");
    p_dbg_serial->flush();
    print_util_dbg_sep('-');
    p_dbg_serial->flush();

    print_util_dbg_init_msg("[SERIAL1]", ret);
    print_util_dbg_init_msg("[SERIAL2]", ret);
    p_dbg_serial->flush();

    // -------------------------------------------------------------------------
    // Init Servos
    // -------------------------------------------------------------------------
    ret = pwm_0.init();
    print_util_dbg_init_msg("[PWM0]", ret);
    init_success &= ret;
    servo_0.failsafe();
    p_dbg_serial->flush();
    ret = pwm_1.init();
    print_util_dbg_init_msg("[PWM1]", ret);
    init_success &= ret;
    servo_1.failsafe();
    p_dbg_serial->flush();
    ret = pwm_2.init();
    print_util_dbg_init_msg("[PWM2]", ret);
    init_success &= ret;
    servo_2.failsafe();
    ret = pwm_3.init();
    print_util_dbg_init_msg("[PWM3]", ret);
    init_success &= ret;
    servo_3.failsafe();
    p_dbg_serial->flush();
    ret = pwm_4.init();
    print_util_dbg_init_msg("[PWM4]", ret);
    init_success &= ret;
    servo_4.failsafe();
    p_dbg_serial->flush();
    ret = pwm_5.init();
    print_util_dbg_init_msg("[PWM5]", ret);
    init_success &= ret;
    servo_5.failsafe();
    p_dbg_serial->flush();
    ret = pwm_6.init();
    print_util_dbg_init_msg("[PWM6]", ret);
    init_success &= ret;
    servo_6.failsafe();
    p_dbg_serial->flush();
    ret = pwm_7.init();
    print_util_dbg_init_msg("[PWM7]", ret);
    init_success &= ret;
    servo_7.failsafe();
    p_dbg_serial->flush();

    // -------------------------------------------------------------------------
    // Init spektrum_satelitte
    // -------------------------------------------------------------------------
    ret = spektrum_satellite.init();
    print_util_dbg_init_msg("[SAT]", ret);
    init_success &= ret;
    p_dbg_serial->flush();

    print_util_dbg_sep('-');
    p_dbg_serial->flush();
    print_util_dbg_init_msg("[MAVRIMINI]", init_success);
    p_dbg_serial->flush();
    print_util_dbg_sep('-');
    p_dbg_serial->flush();

    return init_success;
}<|endoftext|>


<commit_before>#ifndef MOS_UTIL_H
#define MOS_UTIL_H

#include <fstream>
#include <functional>
#include <algorithm>
#include <vector>
#include <rapidjson/document.h>

namespace mos {
rapidjson::Document inline document(const std::string &path) {
  std::ifstream is(path);
  if (!is.good()) {
    throw std::runtime_error(path + " does not exist.");
  }
  std::ifstream file(path);
  std::string source((std::istreambuf_iterator<char>(file)),
                     std::istreambuf_iterator<char>());
  rapidjson::Document doc;
  doc.Parse(source.c_str());
  return doc;
}

/**
 * @brief Load text from file.
 * @param path Full path to the file.
 * @return String with all content.
 */
std::string inline text(const std::string path) {
  std::ifstream file(path);
  if (!file.is_open()) {
    throw std::runtime_error("Could not open " + path + ".");
  }
  std::string source((std::istreambuf_iterator<char>(file)),
                     std::istreambuf_iterator<char>());

  return source;
}

template <typename T>
/**
 * @brief operator +=
 * @param a
 * @param b
 * @return
 */
std::vector<T> &operator+=(std::vector<T> &a, const std::vector<T> &b) {
  a.insert(a.end(), b.begin(), b.end());
  return a;
}

template <typename T>
/**
 * @brief operator +=
 * @param aVector
 * @param aObject
 * @return
 */
std::vector<T> &operator+=(std::vector<T> &aVector, const T &aObject) {
  aVector.push_back(aObject);
  return aVector;
}

template <typename T>
/**
 * @brief operator +
 * @param a
 * @param b
 * @return
 */
std::vector<T> operator+(const std::vector<T> &a, const std::vector<T> &b) {
  assert(a.size() == b.size());

  std::vector<T> result;
  result.reserve(a.size());
  /*
std::transform(a.begin(), a.end(), b.begin(),
             std::back_inserter(result), std::plus<T>());*/
  return result;
}

template <typename T>
/**
 * @brief operator -
 * @param a
 * @param b
 * @return
 */
std::vector<T> operator-(const std::vector<T> &a, const std::vector<T> &b) {
  assert(a.size() == b.size());

  std::vector<T> result;
  result.reserve(a.size());
  /*
std::transform(a.begin(), a.end(), b.begin(),
             std::back_inserter(result), std::minus<T>());*/
  return result;
}

template <typename T>
/**
 * @brief operator *
 * @param a
 * @param b
 * @return
 */
std::vector<T> operator*(const std::vector<T> &a, const std::vector<T> &b) {
  assert(a.size() == b.size());

  std::vector<T> result;
  result.reserve(a.size());
  /*
std::transform(a.begin(), a.end(), b.begin(),
             std::back_inserter(result), std::multiplies<T>());*/
  return result;
}

template <typename T>
/**
 * @brief operator /
 * @param a
 * @param b
 * @return
 */
std::vector<T> operator/(const std::vector<T> &a, const std::vector<T> &b) {
  assert(a.size() == b.size());

  std::vector<T> result;
  result.reserve(a.size());
  /*
std::transform(a.begin(), a.end(), b.begin(),
             std::back_inserter(result), std::divides<T>());*/
  return result;
}
}

#endif /* MOS_UTIL_H */
<commit_msg>cleanup util<commit_after>#ifndef MOS_UTIL_H
#define MOS_UTIL_H

#include <string>
#include <rapidjson/document.h>

namespace mos {
/**
* @brief Load a json document.
* @param path Full path.
* @return Rapidjson document.
*/
rapidjson::Document document(const std::string &path);

/**
 * @brief Load text from file.
 * @param path Full path.
 * @return String with all content.
 */
std::string text(const std::string path);
}

#endif /* MOS_UTIL_H */
<|endoftext|>


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

#include "google/cloud/completion_queue.h"
#include "google/cloud/future.h"
#include "google/cloud/testing_util/assert_ok.h"
#include <google/bigtable/admin/v2/bigtable_table_admin.grpc.pb.h>
#include <google/bigtable/v2/bigtable.grpc.pb.h>
#include <gmock/gmock.h>
#include <chrono>
#include <memory>
#include <thread>

namespace google {
namespace cloud {
inline namespace GOOGLE_CLOUD_CPP_NS {
namespace {

class MockCompletionQueue : public internal::CompletionQueueImpl {
 public:
  using internal::CompletionQueueImpl::SimulateCompletion;
};

namespace btadmin = ::google::bigtable::admin::v2;
namespace btproto = ::google::bigtable::v2;
using ::testing::_;
using ::testing::StrictMock;

class MockClient {
 public:
  MOCK_METHOD3(
      AsyncGetTable,
      std::unique_ptr<grpc::ClientAsyncResponseReaderInterface<btadmin::Table>>(
          grpc::ClientContext*, btadmin::GetTableRequest const&,
          grpc::CompletionQueue* cq));

  MOCK_METHOD3(AsyncReadRows,
               std::unique_ptr<::grpc::ClientAsyncReaderInterface<
                   btproto::ReadRowsResponse>>(grpc::ClientContext*,
                                               btproto::ReadRowsRequest const&,
                                               grpc::CompletionQueue* cq));
};

class MockTableReader
    : public grpc::ClientAsyncResponseReaderInterface<btadmin::Table> {
 public:
  MOCK_METHOD0(StartCall, void());
  MOCK_METHOD1(ReadInitialMetadata, void(void*));
  MOCK_METHOD3(Finish, void(btadmin::Table*, grpc::Status*, void*));
};

class MockRowReader
    : public grpc::ClientAsyncReaderInterface<btproto::ReadRowsResponse> {
 public:
  MOCK_METHOD1(StartCall, void(void*));
  MOCK_METHOD1(ReadInitialMetadata, void(void*));
  MOCK_METHOD2(Read, void(btproto::ReadRowsResponse*, void*));
  MOCK_METHOD2(Finish, void(grpc::Status*, void*));
};

/// @test Verify that the basic functionality in a CompletionQueue works.
TEST(CompletionQueueTest, TimerSmokeTest) {
  CompletionQueue cq;
  std::thread t([&cq] { cq.Run(); });

  using ms = std::chrono::milliseconds;
  promise<void> wait_for_sleep;
  cq.MakeRelativeTimer(ms(2))
      .then([&wait_for_sleep](
                future<StatusOr<std::chrono::system_clock::time_point>>) {
        wait_for_sleep.set_value();
      })
      .get();

  auto f = wait_for_sleep.get_future();
  EXPECT_EQ(std::future_status::ready, f.wait_for(ms(0)));
  cq.Shutdown();
  t.join();
}

TEST(CompletionQueueTest, MockSmokeTest) {
  auto mock = std::make_shared<MockCompletionQueue>();

  CompletionQueue cq(mock);
  using ms = std::chrono::milliseconds;
  promise<void> wait_for_sleep;
  cq.MakeRelativeTimer(ms(20000)).then(
      [&wait_for_sleep](
          future<StatusOr<std::chrono::system_clock::time_point>>) {
        wait_for_sleep.set_value();
      });
  mock->SimulateCompletion(/*ok=*/true);

  auto f = wait_for_sleep.get_future();
  EXPECT_EQ(std::future_status::ready, f.wait_for(ms(0)));
  cq.Shutdown();
}

TEST(CompletionQueueTest, ShutdownWithPending) {
  using ms = std::chrono::milliseconds;

  future<void> timer;
  {
    CompletionQueue cq;
    std::thread runner([&cq] { cq.Run(); });
    timer = cq.MakeRelativeTimer(ms(20)).then(
        [](future<StatusOr<std::chrono::system_clock::time_point>> result) {
          // Timer still runs to completion after `Shutdown`.
          EXPECT_STATUS_OK(result.get().status());
        });
    EXPECT_EQ(std::future_status::timeout, timer.wait_for(ms(0)));
    cq.Shutdown();
    EXPECT_EQ(std::future_status::timeout, timer.wait_for(ms(0)));
    runner.join();
  }
  EXPECT_EQ(std::future_status::ready, timer.wait_for(ms(0)));
}

TEST(CompletionQueueTest, CanCancelAllEvents) {
  CompletionQueue cq;
  promise<void> done;
  std::thread runner([&cq, &done] {
    cq.Run();
    done.set_value();
  });
  for (int i = 0; i < 3; ++i) {
    using hours = std::chrono::hours;
    cq.MakeRelativeTimer(hours(1)).then(
        [](future<StatusOr<std::chrono::system_clock::time_point>> result) {
          // Cancelled timers return CANCELLED status.
          EXPECT_EQ(StatusCode::kCancelled, result.get().status().code());
        });
  }
  using ms = std::chrono::milliseconds;
  auto f = done.get_future();
  EXPECT_EQ(std::future_status::timeout, f.wait_for(ms(1)));
  cq.Shutdown();
  EXPECT_EQ(std::future_status::timeout, f.wait_for(ms(1)));
  cq.CancelAll();
  f.wait();
  EXPECT_TRUE(f.is_ready());
  runner.join();
}

TEST(CompletionQueueTest, MakeUnaryRpc) {
  using ms = std::chrono::milliseconds;

  auto mock_cq = std::make_shared<MockCompletionQueue>();
  CompletionQueue cq(mock_cq);

  auto mock_reader = absl::make_unique<MockTableReader>();
  EXPECT_CALL(*mock_reader, Finish(_, _, _))
      .WillOnce([](btadmin::Table* table, grpc::Status* status, void*) {
        table->set_name("test-table-name");
        *status = grpc::Status::OK;
      });
  MockClient mock_client;
  EXPECT_CALL(mock_client, AsyncGetTable(_, _, _))
      .WillOnce([&mock_reader](grpc::ClientContext*,
                               btadmin::GetTableRequest const& request,
                               grpc::CompletionQueue*) {
        EXPECT_EQ("test-table-name", request.name());
        // This looks like a double delete, but it is not because
        // std::unique_ptr<grpc::ClientAsyncResponseReaderInterface<T>> is
        // specialized to not delete. :shrug:
        return std::unique_ptr<
            grpc::ClientAsyncResponseReaderInterface<btadmin::Table>>(
            mock_reader.get());
      });

  std::thread runner([&cq] { cq.Run(); });

  btadmin::GetTableRequest request;
  request.set_name("test-table-name");
  future<void> done =
      cq.MakeUnaryRpc(
            [&mock_client](grpc::ClientContext* context,
                           btadmin::GetTableRequest const& request,
                           grpc::CompletionQueue* cq) {
              return mock_client.AsyncGetTable(context, request, cq);
            },
            request, absl::make_unique<grpc::ClientContext>())
          .then([](future<StatusOr<btadmin::Table>> f) {
            auto table = f.get();
            ASSERT_STATUS_OK(table);
            EXPECT_EQ("test-table-name", table->name());
          });

  mock_cq->SimulateCompletion(true);

  EXPECT_EQ(std::future_status::ready, done.wait_for(ms(0)));

  cq.Shutdown();
  runner.join();
}

TEST(CompletionQueueTest, MakeStreamingReadRpc) {
  auto mock_cq = std::make_shared<MockCompletionQueue>();
  CompletionQueue cq(mock_cq);

  auto mock_reader = absl::make_unique<MockRowReader>();
  EXPECT_CALL(*mock_reader, StartCall(_)).Times(1);
  EXPECT_CALL(*mock_reader, Read(_, _)).Times(2);
  EXPECT_CALL(*mock_reader, Finish(_, _)).Times(1);

  MockClient mock_client;
  EXPECT_CALL(mock_client, AsyncReadRows(_, _, _))
      .WillOnce([&mock_reader](grpc::ClientContext*,
                               btproto::ReadRowsRequest const& request,
                               grpc::CompletionQueue*) {
        EXPECT_EQ("test-table-name", request.table_name());
        return std::unique_ptr<
            grpc::ClientAsyncReaderInterface<btproto::ReadRowsResponse>>(
            mock_reader.release());
      });

  std::thread runner([&cq] { cq.Run(); });

  btproto::ReadRowsRequest request;
  request.set_table_name("test-table-name");

  int on_read_counter = 0;
  int on_finish_counter = 0;
  (void)cq.MakeStreamingReadRpc(
      [&mock_client](grpc::ClientContext* context,
                     btproto::ReadRowsRequest const& request,
                     grpc::CompletionQueue* cq) {
        return mock_client.AsyncReadRows(context, request, cq);
      },
      request, absl::make_unique<grpc::ClientContext>(),
      [&on_read_counter](btproto::ReadRowsResponse const&) {
        ++on_read_counter;
        return make_ready_future(true);
      },
      [&on_finish_counter](Status const&) { ++on_finish_counter; });

  // Simulate the OnStart() completion
  mock_cq->SimulateCompletion(true);
  // Simulate the first Read() completion
  mock_cq->SimulateCompletion(true);
  EXPECT_EQ(1, on_read_counter);
  EXPECT_EQ(0, on_finish_counter);

  // Simulate a Read() returning false
  mock_cq->SimulateCompletion(false);
  EXPECT_EQ(1, on_read_counter);
  EXPECT_EQ(0, on_finish_counter);

  // Simulate the Finish() call completing asynchronously
  mock_cq->SimulateCompletion(false);
  EXPECT_EQ(1, on_read_counter);
  EXPECT_EQ(1, on_finish_counter);

  cq.Shutdown();
  runner.join();
}

TEST(CompletionQueueTest, MakeRpcsAfterShutdown) {
  using ms = std::chrono::milliseconds;

  auto mock_cq = std::make_shared<MockCompletionQueue>();
  CompletionQueue cq(mock_cq);

  // Use `StrictMock` to enforce that there are no calls made on the client.
  StrictMock<MockClient> mock_client;
  std::thread runner([&cq] { cq.Run(); });
  cq.Shutdown();

  btadmin::GetTableRequest get_table_request;
  get_table_request.set_name("test-table-name");
  future<void> done =
      cq.MakeUnaryRpc(
            [&mock_client](grpc::ClientContext* context,
                           btadmin::GetTableRequest const& request,
                           grpc::CompletionQueue* cq) {
              return mock_client.AsyncGetTable(context, request, cq);
            },
            get_table_request, absl::make_unique<grpc::ClientContext>())
          .then([](future<StatusOr<btadmin::Table>> f) {
            EXPECT_EQ(StatusCode::kCancelled, f.get().status().code());
          });

  btproto::ReadRowsRequest read_request;
  read_request.set_table_name("test-table-name");
  (void)cq.MakeStreamingReadRpc(
      [&mock_client](grpc::ClientContext* context,
                     btproto::ReadRowsRequest const& request,
                     grpc::CompletionQueue* cq) {
        return mock_client.AsyncReadRows(context, request, cq);
      },
      read_request, absl::make_unique<grpc::ClientContext>(),
      [](btproto::ReadRowsResponse const&) {
        ADD_FAILURE() << "OnReadHandler unexpectedly called";
        return make_ready_future(true);
      },
      [](Status const& status) {
        EXPECT_EQ(StatusCode::kCancelled, status.code());
      });

  mock_cq->SimulateCompletion(true);
  EXPECT_EQ(std::future_status::ready, done.wait_for(ms(0)));
  runner.join();
}

TEST(CompletionQueueTest, RunAsync) {
  CompletionQueue cq;

  std::thread runner([&cq] { cq.Run(); });

  std::promise<void> done_promise;
  cq.RunAsync([&done_promise](CompletionQueue&) { done_promise.set_value(); });

  auto done = done_promise.get_future();
  done.get();

  cq.Shutdown();
  runner.join();
}

TEST(CompletionQueueTest, RunAsyncCompletionQueueDestroyed) {
  auto cq_impl = std::make_shared<MockCompletionQueue>();

  std::promise<void> done_promise;
  {
    CompletionQueue cq(cq_impl);
    cq.RunAsync([&done_promise](CompletionQueue& cq) {
      done_promise.set_value();
      cq.Shutdown();
    });
  }
  cq_impl->SimulateCompletion(true);

  done_promise.get_future().get();
}

// Sets up a timer that reschedules itself and verifies we can shut down
// cleanly whether we call `CancelAll()` on the queue first or not.
namespace {
using TimerFuture = future<StatusOr<std::chrono::system_clock::time_point>>;

void RunAndReschedule(CompletionQueue& cq, bool ok,
                      std::chrono::seconds duration) {
  if (ok) {
    cq.MakeRelativeTimer(duration).then([&cq, duration](TimerFuture result) {
      RunAndReschedule(cq, result.get().ok(), duration);
    });
  }
}
}  // namespace

TEST(CompletionQueueTest, ShutdownWithReschedulingTimer) {
  CompletionQueue cq;
  std::thread t([&cq] { cq.Run(); });

  RunAndReschedule(cq, /*ok=*/true, std::chrono::seconds(1));

  cq.Shutdown();
  t.join();
}

TEST(CompletionQueueTest, ShutdownWithFastReschedulingTimer) {
  auto constexpr kThreadCount = 32;
  auto constexpr kTimerCount = 100;
  CompletionQueue cq;
  std::vector<std::thread> threads(kThreadCount);
  std::generate_n(threads.begin(), threads.size(),
                  [&cq] { return std::thread([&cq] { cq.Run(); }); });

  for (int i = 0; i != kTimerCount; ++i) {
    RunAndReschedule(cq, /*ok=*/true, std::chrono::seconds(0));
  }

  promise<void> wait;
  cq.MakeRelativeTimer(std::chrono::milliseconds(1)).then([&wait](TimerFuture) {
    wait.set_value();
  });
  wait.get_future().get();
  cq.Shutdown();
  for (auto& t : threads) {
    t.join();
  }
}

TEST(CompletionQueueTest, CancelAndShutdownWithReschedulingTimer) {
  CompletionQueue cq;
  std::thread t([&cq] { cq.Run(); });

  RunAndReschedule(cq, /*ok=*/true, std::chrono::seconds(1));

  cq.CancelAll();
  cq.Shutdown();
  t.join();
}

TEST(CompletionQueueTest, CancelTimerSimple) {
  CompletionQueue cq;
  std::thread t([&cq] { cq.Run(); });

  using ms = std::chrono::milliseconds;
  auto fut = cq.MakeRelativeTimer(ms(20000));
  fut.cancel();
  auto tp = fut.get();
  EXPECT_FALSE(tp.ok()) << ", status=" << tp.status();
  cq.Shutdown();
  t.join();
}

TEST(CompletionQueueTest, CancelTimerContinuation) {
  CompletionQueue cq;
  std::thread t([&cq] { cq.Run(); });

  using ms = std::chrono::milliseconds;
  auto fut = cq.MakeRelativeTimer(ms(20000)).then(
      [](future<StatusOr<std::chrono::system_clock::time_point>> f) {
        return f.get().status();
      });
  fut.cancel();
  auto status = fut.get();
  EXPECT_FALSE(status.ok()) << ", status=" << status;
  cq.Shutdown();
  t.join();
}

}  // namespace
}  // namespace GOOGLE_CLOUD_CPP_NS
}  // namespace cloud
}  // namespace google
<commit_msg>fix: test with correct MockCompletionQueue (#4427)<commit_after>// Copyright 2019 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "google/cloud/completion_queue.h"
#include "google/cloud/future.h"
#include "google/cloud/testing_util/assert_ok.h"
#include "google/cloud/testing_util/mock_completion_queue.h"
#include <google/bigtable/admin/v2/bigtable_table_admin.grpc.pb.h>
#include <google/bigtable/v2/bigtable.grpc.pb.h>
#include <gmock/gmock.h>
#include <chrono>
#include <memory>
#include <thread>

namespace google {
namespace cloud {
inline namespace GOOGLE_CLOUD_CPP_NS {
namespace {

namespace btadmin = ::google::bigtable::admin::v2;
namespace btproto = ::google::bigtable::v2;
using ::google::cloud::testing_util::MockCompletionQueue;
using ::testing::_;
using ::testing::StrictMock;

class MockClient {
 public:
  MOCK_METHOD3(
      AsyncGetTable,
      std::unique_ptr<grpc::ClientAsyncResponseReaderInterface<btadmin::Table>>(
          grpc::ClientContext*, btadmin::GetTableRequest const&,
          grpc::CompletionQueue* cq));

  MOCK_METHOD3(AsyncReadRows,
               std::unique_ptr<::grpc::ClientAsyncReaderInterface<
                   btproto::ReadRowsResponse>>(grpc::ClientContext*,
                                               btproto::ReadRowsRequest const&,
                                               grpc::CompletionQueue* cq));
};

class MockTableReader
    : public grpc::ClientAsyncResponseReaderInterface<btadmin::Table> {
 public:
  MOCK_METHOD0(StartCall, void());
  MOCK_METHOD1(ReadInitialMetadata, void(void*));
  MOCK_METHOD3(Finish, void(btadmin::Table*, grpc::Status*, void*));
};

class MockRowReader
    : public grpc::ClientAsyncReaderInterface<btproto::ReadRowsResponse> {
 public:
  MOCK_METHOD1(StartCall, void(void*));
  MOCK_METHOD1(ReadInitialMetadata, void(void*));
  MOCK_METHOD2(Read, void(btproto::ReadRowsResponse*, void*));
  MOCK_METHOD2(Finish, void(grpc::Status*, void*));
};

/// @test Verify that the basic functionality in a CompletionQueue works.
TEST(CompletionQueueTest, TimerSmokeTest) {
  CompletionQueue cq;
  std::thread t([&cq] { cq.Run(); });

  using ms = std::chrono::milliseconds;
  promise<void> wait_for_sleep;
  cq.MakeRelativeTimer(ms(2))
      .then([&wait_for_sleep](
                future<StatusOr<std::chrono::system_clock::time_point>>) {
        wait_for_sleep.set_value();
      })
      .get();

  auto f = wait_for_sleep.get_future();
  EXPECT_EQ(std::future_status::ready, f.wait_for(ms(0)));
  cq.Shutdown();
  t.join();
}

TEST(CompletionQueueTest, MockSmokeTest) {
  auto mock = std::make_shared<MockCompletionQueue>();

  CompletionQueue cq(mock);
  using ms = std::chrono::milliseconds;
  promise<void> wait_for_sleep;
  cq.MakeRelativeTimer(ms(20000)).then(
      [&wait_for_sleep](
          future<StatusOr<std::chrono::system_clock::time_point>>) {
        wait_for_sleep.set_value();
      });
  mock->SimulateCompletion(/*ok=*/true);

  auto f = wait_for_sleep.get_future();
  EXPECT_EQ(std::future_status::ready, f.wait_for(ms(0)));
  cq.Shutdown();
}

TEST(CompletionQueueTest, ShutdownWithPending) {
  using ms = std::chrono::milliseconds;

  future<void> timer;
  {
    CompletionQueue cq;
    std::thread runner([&cq] { cq.Run(); });
    timer = cq.MakeRelativeTimer(ms(20)).then(
        [](future<StatusOr<std::chrono::system_clock::time_point>> result) {
          // Timer still runs to completion after `Shutdown`.
          EXPECT_STATUS_OK(result.get().status());
        });
    EXPECT_EQ(std::future_status::timeout, timer.wait_for(ms(0)));
    cq.Shutdown();
    EXPECT_EQ(std::future_status::timeout, timer.wait_for(ms(0)));
    runner.join();
  }
  EXPECT_EQ(std::future_status::ready, timer.wait_for(ms(0)));
}

TEST(CompletionQueueTest, CanCancelAllEvents) {
  CompletionQueue cq;
  promise<void> done;
  std::thread runner([&cq, &done] {
    cq.Run();
    done.set_value();
  });
  for (int i = 0; i < 3; ++i) {
    using hours = std::chrono::hours;
    cq.MakeRelativeTimer(hours(1)).then(
        [](future<StatusOr<std::chrono::system_clock::time_point>> result) {
          // Cancelled timers return CANCELLED status.
          EXPECT_EQ(StatusCode::kCancelled, result.get().status().code());
        });
  }
  using ms = std::chrono::milliseconds;
  auto f = done.get_future();
  EXPECT_EQ(std::future_status::timeout, f.wait_for(ms(1)));
  cq.Shutdown();
  EXPECT_EQ(std::future_status::timeout, f.wait_for(ms(1)));
  cq.CancelAll();
  f.wait();
  EXPECT_TRUE(f.is_ready());
  runner.join();
}

TEST(CompletionQueueTest, MakeUnaryRpc) {
  using ms = std::chrono::milliseconds;

  auto mock_cq = std::make_shared<MockCompletionQueue>();
  CompletionQueue cq(mock_cq);

  auto mock_reader = absl::make_unique<MockTableReader>();
  EXPECT_CALL(*mock_reader, Finish(_, _, _))
      .WillOnce([](btadmin::Table* table, grpc::Status* status, void*) {
        table->set_name("test-table-name");
        *status = grpc::Status::OK;
      });
  MockClient mock_client;
  EXPECT_CALL(mock_client, AsyncGetTable(_, _, _))
      .WillOnce([&mock_reader](grpc::ClientContext*,
                               btadmin::GetTableRequest const& request,
                               grpc::CompletionQueue*) {
        EXPECT_EQ("test-table-name", request.name());
        // This looks like a double delete, but it is not because
        // std::unique_ptr<grpc::ClientAsyncResponseReaderInterface<T>> is
        // specialized to not delete. :shrug:
        return std::unique_ptr<
            grpc::ClientAsyncResponseReaderInterface<btadmin::Table>>(
            mock_reader.get());
      });

  std::thread runner([&cq] { cq.Run(); });

  btadmin::GetTableRequest request;
  request.set_name("test-table-name");
  future<void> done =
      cq.MakeUnaryRpc(
            [&mock_client](grpc::ClientContext* context,
                           btadmin::GetTableRequest const& request,
                           grpc::CompletionQueue* cq) {
              return mock_client.AsyncGetTable(context, request, cq);
            },
            request, absl::make_unique<grpc::ClientContext>())
          .then([](future<StatusOr<btadmin::Table>> f) {
            auto table = f.get();
            ASSERT_STATUS_OK(table);
            EXPECT_EQ("test-table-name", table->name());
          });

  mock_cq->SimulateCompletion(true);

  EXPECT_EQ(std::future_status::ready, done.wait_for(ms(0)));

  cq.Shutdown();
  runner.join();
}

TEST(CompletionQueueTest, MakeStreamingReadRpc) {
  auto mock_cq = std::make_shared<MockCompletionQueue>();
  CompletionQueue cq(mock_cq);

  auto mock_reader = absl::make_unique<MockRowReader>();
  EXPECT_CALL(*mock_reader, StartCall(_)).Times(1);
  EXPECT_CALL(*mock_reader, Read(_, _)).Times(2);
  EXPECT_CALL(*mock_reader, Finish(_, _)).Times(1);

  MockClient mock_client;
  EXPECT_CALL(mock_client, AsyncReadRows(_, _, _))
      .WillOnce([&mock_reader](grpc::ClientContext*,
                               btproto::ReadRowsRequest const& request,
                               grpc::CompletionQueue*) {
        EXPECT_EQ("test-table-name", request.table_name());
        return std::unique_ptr<
            grpc::ClientAsyncReaderInterface<btproto::ReadRowsResponse>>(
            mock_reader.release());
      });

  std::thread runner([&cq] { cq.Run(); });

  btproto::ReadRowsRequest request;
  request.set_table_name("test-table-name");

  int on_read_counter = 0;
  int on_finish_counter = 0;
  (void)cq.MakeStreamingReadRpc(
      [&mock_client](grpc::ClientContext* context,
                     btproto::ReadRowsRequest const& request,
                     grpc::CompletionQueue* cq) {
        return mock_client.AsyncReadRows(context, request, cq);
      },
      request, absl::make_unique<grpc::ClientContext>(),
      [&on_read_counter](btproto::ReadRowsResponse const&) {
        ++on_read_counter;
        return make_ready_future(true);
      },
      [&on_finish_counter](Status const&) { ++on_finish_counter; });

  // Simulate the OnStart() completion
  mock_cq->SimulateCompletion(true);
  // Simulate the first Read() completion
  mock_cq->SimulateCompletion(true);
  EXPECT_EQ(1, on_read_counter);
  EXPECT_EQ(0, on_finish_counter);

  // Simulate a Read() returning false
  mock_cq->SimulateCompletion(false);
  EXPECT_EQ(1, on_read_counter);
  EXPECT_EQ(0, on_finish_counter);

  // Simulate the Finish() call completing asynchronously
  mock_cq->SimulateCompletion(false);
  EXPECT_EQ(1, on_read_counter);
  EXPECT_EQ(1, on_finish_counter);

  cq.Shutdown();
  runner.join();
}

TEST(CompletionQueueTest, MakeRpcsAfterShutdown) {
  using ms = std::chrono::milliseconds;

  auto mock_cq = std::make_shared<MockCompletionQueue>();
  CompletionQueue cq(mock_cq);

  // Use `StrictMock` to enforce that there are no calls made on the client.
  StrictMock<MockClient> mock_client;
  std::thread runner([&cq] { cq.Run(); });
  cq.Shutdown();

  btadmin::GetTableRequest get_table_request;
  get_table_request.set_name("test-table-name");
  future<void> done =
      cq.MakeUnaryRpc(
            [&mock_client](grpc::ClientContext* context,
                           btadmin::GetTableRequest const& request,
                           grpc::CompletionQueue* cq) {
              return mock_client.AsyncGetTable(context, request, cq);
            },
            get_table_request, absl::make_unique<grpc::ClientContext>())
          .then([](future<StatusOr<btadmin::Table>> f) {
            EXPECT_EQ(StatusCode::kCancelled, f.get().status().code());
          });

  btproto::ReadRowsRequest read_request;
  read_request.set_table_name("test-table-name");
  (void)cq.MakeStreamingReadRpc(
      [&mock_client](grpc::ClientContext* context,
                     btproto::ReadRowsRequest const& request,
                     grpc::CompletionQueue* cq) {
        return mock_client.AsyncReadRows(context, request, cq);
      },
      read_request, absl::make_unique<grpc::ClientContext>(),
      [](btproto::ReadRowsResponse const&) {
        ADD_FAILURE() << "OnReadHandler unexpectedly called";
        return make_ready_future(true);
      },
      [](Status const& status) {
        EXPECT_EQ(StatusCode::kCancelled, status.code());
      });

  mock_cq->SimulateCompletion(true);
  EXPECT_EQ(std::future_status::ready, done.wait_for(ms(0)));
  runner.join();
}

TEST(CompletionQueueTest, RunAsync) {
  CompletionQueue cq;

  std::thread runner([&cq] { cq.Run(); });

  std::promise<void> done_promise;
  cq.RunAsync([&done_promise](CompletionQueue&) { done_promise.set_value(); });

  auto done = done_promise.get_future();
  done.get();

  cq.Shutdown();
  runner.join();
}

TEST(CompletionQueueTest, RunAsyncCompletionQueueDestroyed) {
  auto cq_impl = std::make_shared<MockCompletionQueue>();

  std::promise<void> done_promise;
  {
    CompletionQueue cq(cq_impl);
    cq.RunAsync([&done_promise](CompletionQueue& cq) {
      done_promise.set_value();
      cq.Shutdown();
    });
  }
  cq_impl->SimulateCompletion(true);

  done_promise.get_future().get();
}

// Sets up a timer that reschedules itself and verifies we can shut down
// cleanly whether we call `CancelAll()` on the queue first or not.
namespace {
using TimerFuture = future<StatusOr<std::chrono::system_clock::time_point>>;

void RunAndReschedule(CompletionQueue& cq, bool ok,
                      std::chrono::seconds duration) {
  if (ok) {
    cq.MakeRelativeTimer(duration).then([&cq, duration](TimerFuture result) {
      RunAndReschedule(cq, result.get().ok(), duration);
    });
  }
}
}  // namespace

TEST(CompletionQueueTest, ShutdownWithReschedulingTimer) {
  CompletionQueue cq;
  std::thread t([&cq] { cq.Run(); });

  RunAndReschedule(cq, /*ok=*/true, std::chrono::seconds(1));

  cq.Shutdown();
  t.join();
}

TEST(CompletionQueueTest, ShutdownWithFastReschedulingTimer) {
  auto constexpr kThreadCount = 32;
  auto constexpr kTimerCount = 100;
  CompletionQueue cq;
  std::vector<std::thread> threads(kThreadCount);
  std::generate_n(threads.begin(), threads.size(),
                  [&cq] { return std::thread([&cq] { cq.Run(); }); });

  for (int i = 0; i != kTimerCount; ++i) {
    RunAndReschedule(cq, /*ok=*/true, std::chrono::seconds(0));
  }

  promise<void> wait;
  cq.MakeRelativeTimer(std::chrono::milliseconds(1)).then([&wait](TimerFuture) {
    wait.set_value();
  });
  wait.get_future().get();
  cq.Shutdown();
  for (auto& t : threads) {
    t.join();
  }
}

TEST(CompletionQueueTest, CancelAndShutdownWithReschedulingTimer) {
  CompletionQueue cq;
  std::thread t([&cq] { cq.Run(); });

  RunAndReschedule(cq, /*ok=*/true, std::chrono::seconds(1));

  cq.CancelAll();
  cq.Shutdown();
  t.join();
}

TEST(CompletionQueueTest, CancelTimerSimple) {
  CompletionQueue cq;
  std::thread t([&cq] { cq.Run(); });

  using ms = std::chrono::milliseconds;
  auto fut = cq.MakeRelativeTimer(ms(20000));
  fut.cancel();
  auto tp = fut.get();
  EXPECT_FALSE(tp.ok()) << ", status=" << tp.status();
  cq.Shutdown();
  t.join();
}

TEST(CompletionQueueTest, CancelTimerContinuation) {
  CompletionQueue cq;
  std::thread t([&cq] { cq.Run(); });

  using ms = std::chrono::milliseconds;
  auto fut = cq.MakeRelativeTimer(ms(20000)).then(
      [](future<StatusOr<std::chrono::system_clock::time_point>> f) {
        return f.get().status();
      });
  fut.cancel();
  auto status = fut.get();
  EXPECT_FALSE(status.ok()) << ", status=" << status;
  cq.Shutdown();
  t.join();
}

}  // namespace
}  // namespace GOOGLE_CLOUD_CPP_NS
}  // namespace cloud
}  // namespace google
<|endoftext|>


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

/**
 * @file    file.hpp
 * @author  Patrick Flick <patrick.flick@gmail.com>
 * @brief   Block decompose and distribute file as string on MPI communicator.
 *
 * This is not a substitute for MPI_File functionality.
 * TODO:
 * - [ ] Implement proper MPI_File functions for parallel reading of files
 */

#ifndef MXX_FILE_HPP
#define MXX_FILE_HPP

#include <mpi.h>

// C++ includes
#include <string>
#include <fstream>
#include <streambuf>
#include <sstream>
#include <iostream>
#include <iomanip>

// mxx includes
#include "partition.hpp"

namespace mxx {

std::ifstream::pos_type get_filesize(const char* filename)
{
    std::ifstream in(filename, std::ifstream::ate | std::ifstream::binary);
    return in.tellg();
}

class rangebuf: public std::streambuf {
public:
    rangebuf(std::streampos start,
                    size_t size,
                    std::streambuf* sbuf):
        size_(size), sbuf_(sbuf), buf_(new char[64])
    {
        sbuf->pubseekpos(start, std::ios_base::in);
    }
    int underflow() {
        size_t r(this->sbuf_->sgetn(this->buf_,
            std::min<size_t>(sizeof(this->buf_), this->size_)));
        this->size_ -= r;
        this->setg(this->buf_, this->buf_, this->buf_ + r);
        return this->gptr() == this->egptr()
            ? traits_type::eof()
            : traits_type::to_int_type(*this->gptr());
    }

    ~rangebuf()
    {
        delete [] this->buf_;
    }
protected:
    size_t size_;
    std::streambuf* sbuf_;
    char* buf_;
};

struct file {
protected:
    MPI_File handle;
    std::string filename;
    bool isopen;

    void clear() {
        handle = MPI_FILE_NULL;
        filename = "";
        isopen = false;
    }
public:
    file() : handle(MPI_FILE_NULL), filename(), isopen(false) {}

    file(file&& o) : handle(o.handle), filename(o.filename), isopen(o.isopen) {
        o.clear();
    }

    file(const std::string& filename) : handle(MPI_FILE_NULL), filename(filename), isopen(false) {}

    file(const std::string& filename, int mode) : file(filename) {
        open(mode);
    }

    void open(const std::string& filename, int mode) {
        this->filename = filename;
        MPI_File_open(MPI_COMM_SELF, filename.c_str(), mode, MPI_INFO_NULL, &handle);
        this->isopen = true;
    }

    void open(int mode) {
        open(this->filename, mode);
    }

    void close() {
        if (isopen) {
            MPI_File_close(&handle);
            isopen = false;
            handle = MPI_FILE_NULL;
        }
    }

    static void delete_file(const std::string& filename) {
        MPI_File_delete(filename.c_str(), MPI_INFO_NULL);
    }

    size_t get_size() {
        MPI_Offset s;
        MPI_File_get_size(handle, &s);
        return s;
    }

    void set_size(size_t size) {
        MPI_File_set_size(handle, size);
    }

    template <typename T>
    void read_at(MPI_Offset offset, T* out, size_t count) {
        mxx::datatype dt = mxx::get_datatype<T>().contiguous(count);
        MPI_File_read_at(handle, offset, out, 1, dt.type(), MPI_STATUS_IGNORE);
    }

    template <typename T>
    void write_at(MPI_Offset offset, T* data, size_t count) {
        mxx::datatype dt = mxx::get_datatype<T>().contiguous(count);
        MPI_File_write_at(handle, offset, data, 1, dt.type(), MPI_STATUS_IGNORE);
    }

    // TODO:

    // iread_at
    // iwrite_at
    //
    // read_at_all
    // write_at_all
    //
    // iread_at_all
    // iwrite_at_all
    //
    // read
    // write
    //
    // read_all
    // write_all
    //
    // iread
    // iwrite
    //
    // iread_all
    // iwrite_all
    //
    //
    //
    // seek_set
    // seek_cur
    // seek_end
    //
    // get_pos // `etype` units
    // get_byte_offset
    //
    //
    //

    virtual ~file() {
        close();
    }
};

struct coll_file : public file {
    const mxx::comm& comm;

    coll_file(const std::string& filename, const mxx::comm& comm)
        : file(filename), comm(comm) {}

    template <typename T>
    void read_ordered(size_t count, T* out) {
        mxx::datatype dt = mxx::get_datatype<T>().contiguous(count);
        MPI_File_read_ordered(handle, out, 1, dt.type(), MPI_STATUS_IGNORE);
    }

    // collective, blocking
    // simple ordered write without a need for a file view
    template <typename T>
    void write_ordered(const T* buf, size_t count) {
        mxx::datatype dt = mxx::get_datatype<T>().contiguous(count);
        MPI_File_write_ordered(handle, const_cast<T*>(buf), 1, dt.type(), MPI_STATUS_IGNORE);
    }

    void open(int mode) {
        MPI_File_open(comm, &filename[0], mode, MPI_INFO_NULL, &handle);
        this->isopen = true;
    }
};

std::string file_block_decompose(const char* filename, MPI_Comm comm = MPI_COMM_WORLD, std::size_t max_local_size = 0)
{
    // TODO: handle error if file doesn't exist

    // get size of input file
    std::size_t file_size = get_filesize(filename);

    // get communication parameters
    int p, rank;
    MPI_Comm_size(comm, &p);
    MPI_Comm_rank(comm, &rank);

    // restrict max local size (assuming that it is the same parameter on each
    // processor)
    if (max_local_size > 0 && file_size / p > max_local_size)
        file_size = p*max_local_size;
    blk_dist part(file_size, p, rank);
    // block decompose
    std::size_t local_size = part.local_size();
    std::size_t offset = part.eprefix_size();

    // open file
    std::ifstream t(filename);
    // wrap in our custom range buffer (of type std::streambuf)
    rangebuf rb(offset, local_size, t.rdbuf());

    // read file (range) buffer into string stream
    std::stringstream ss;
    ss << &rb;

    std::string local_str(ss.str());

    return local_str;
}

template <typename T>
void write_ordered(const std::string& filename, const T* buf, size_t count, const mxx::comm& comm) {
    MPI_File handle;
    MPI_File_open(comm, const_cast<char*>(&filename[0]), MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &handle);
    mxx::datatype dt = mxx::get_datatype<T>();
    MPI_File_write_ordered(handle, const_cast<T*>(buf), count, dt.type(), MPI_STATUS_IGNORE);
    MPI_File_close(&handle);
}
template <typename T>
void write_ordered(const std::string& filename, const std::vector<T>& data, const mxx::comm& comm) {
    write_ordered(filename, data.data(), data.size(), comm);
}

template <typename _Iterator>
void write_files(const std::string& filename, _Iterator begin, _Iterator end, MPI_Comm comm = MPI_COMM_WORLD)
{
    // get MPI Communicator properties
    int rank, p;
    MPI_Comm_size(comm, &p);
    MPI_Comm_rank(comm, &rank);

    // get max rank string length:
    std::stringstream sslen;
    sslen << p;
    int rank_slen = sslen.str().size();

    // concat rank at end of filename
    std::stringstream ss;
    ss << filename << "." << std::setfill('0') << std::setw(rank_slen) << p << "." << std::setfill('0') << std::setw(rank_slen) << rank;

    // open file with stream
    //std::cerr << "writing to file " << ss.str() << std::endl;
    std::ofstream outfs(ss.str());

    // write the content into the file, sep by newline
    while (begin != end)
    {
        outfs << *(begin++) << std::endl;
    }
    outfs.close();
}

} // namespace mxx

#endif // MXX_FILE_HPP
<commit_msg>fix const char error in File_open/File_delete<commit_after>/*
 * Copyright 2015 Georgia Institute of Technology
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * @file    file.hpp
 * @author  Patrick Flick <patrick.flick@gmail.com>
 * @brief   Block decompose and distribute file as string on MPI communicator.
 *
 * This is not a substitute for MPI_File functionality.
 * TODO:
 * - [ ] Implement proper MPI_File functions for parallel reading of files
 */

#ifndef MXX_FILE_HPP
#define MXX_FILE_HPP

#include <mpi.h>

// C++ includes
#include <string>
#include <fstream>
#include <streambuf>
#include <sstream>
#include <iostream>
#include <iomanip>

// mxx includes
#include "partition.hpp"

namespace mxx {

std::ifstream::pos_type get_filesize(const char* filename)
{
    std::ifstream in(filename, std::ifstream::ate | std::ifstream::binary);
    return in.tellg();
}

class rangebuf: public std::streambuf {
public:
    rangebuf(std::streampos start,
                    size_t size,
                    std::streambuf* sbuf):
        size_(size), sbuf_(sbuf), buf_(new char[64])
    {
        sbuf->pubseekpos(start, std::ios_base::in);
    }
    int underflow() {
        size_t r(this->sbuf_->sgetn(this->buf_,
            std::min<size_t>(sizeof(this->buf_), this->size_)));
        this->size_ -= r;
        this->setg(this->buf_, this->buf_, this->buf_ + r);
        return this->gptr() == this->egptr()
            ? traits_type::eof()
            : traits_type::to_int_type(*this->gptr());
    }

    ~rangebuf()
    {
        delete [] this->buf_;
    }
protected:
    size_t size_;
    std::streambuf* sbuf_;
    char* buf_;
};

struct file {
protected:
    MPI_File handle;
    std::string filename;
    bool isopen;

    void clear() {
        handle = MPI_FILE_NULL;
        filename = "";
        isopen = false;
    }
public:
    file() : handle(MPI_FILE_NULL), filename(), isopen(false) {}

    file(file&& o) : handle(o.handle), filename(o.filename), isopen(o.isopen) {
        o.clear();
    }

    file(const std::string& filename) : handle(MPI_FILE_NULL), filename(filename), isopen(false) {}

    file(const std::string& filename, int mode) : file(filename) {
        open(mode);
    }

    void open(const std::string& filename, int mode) {
        this->filename = filename;
        MPI_File_open(MPI_COMM_SELF, &filename[0], mode, MPI_INFO_NULL, &handle);
        this->isopen = true;
    }

    void open(int mode) {
        open(this->filename, mode);
    }

    void close() {
        if (isopen) {
            MPI_File_close(&handle);
            isopen = false;
            handle = MPI_FILE_NULL;
        }
    }

    static void delete_file(const std::string& filename) {
        MPI_File_delete(&filename[0], MPI_INFO_NULL);
    }

    size_t get_size() {
        MPI_Offset s;
        MPI_File_get_size(handle, &s);
        return s;
    }

    void set_size(size_t size) {
        MPI_File_set_size(handle, size);
    }

    template <typename T>
    void read_at(MPI_Offset offset, T* out, size_t count) {
        mxx::datatype dt = mxx::get_datatype<T>().contiguous(count);
        MPI_File_read_at(handle, offset, out, 1, dt.type(), MPI_STATUS_IGNORE);
    }

    template <typename T>
    void write_at(MPI_Offset offset, T* data, size_t count) {
        mxx::datatype dt = mxx::get_datatype<T>().contiguous(count);
        MPI_File_write_at(handle, offset, data, 1, dt.type(), MPI_STATUS_IGNORE);
    }

    // TODO:

    // iread_at
    // iwrite_at
    //
    // read_at_all
    // write_at_all
    //
    // iread_at_all
    // iwrite_at_all
    //
    // read
    // write
    //
    // read_all
    // write_all
    //
    // iread
    // iwrite
    //
    // iread_all
    // iwrite_all
    //
    //
    //
    // seek_set
    // seek_cur
    // seek_end
    //
    // get_pos // `etype` units
    // get_byte_offset
    //
    //
    //

    virtual ~file() {
        close();
    }
};

struct coll_file : public file {
    const mxx::comm& comm;

    coll_file(const std::string& filename, const mxx::comm& comm)
        : file(filename), comm(comm) {}

    template <typename T>
    void read_ordered(size_t count, T* out) {
        mxx::datatype dt = mxx::get_datatype<T>().contiguous(count);
        MPI_File_read_ordered(handle, out, 1, dt.type(), MPI_STATUS_IGNORE);
    }

    // collective, blocking
    // simple ordered write without a need for a file view
    template <typename T>
    void write_ordered(const T* buf, size_t count) {
        mxx::datatype dt = mxx::get_datatype<T>().contiguous(count);
        MPI_File_write_ordered(handle, const_cast<T*>(buf), 1, dt.type(), MPI_STATUS_IGNORE);
    }

    void open(int mode) {
        MPI_File_open(comm, &filename[0], mode, MPI_INFO_NULL, &handle);
        this->isopen = true;
    }
};

std::string file_block_decompose(const char* filename, MPI_Comm comm = MPI_COMM_WORLD, std::size_t max_local_size = 0)
{
    // TODO: handle error if file doesn't exist

    // get size of input file
    std::size_t file_size = get_filesize(filename);

    // get communication parameters
    int p, rank;
    MPI_Comm_size(comm, &p);
    MPI_Comm_rank(comm, &rank);

    // restrict max local size (assuming that it is the same parameter on each
    // processor)
    if (max_local_size > 0 && file_size / p > max_local_size)
        file_size = p*max_local_size;
    blk_dist part(file_size, p, rank);
    // block decompose
    std::size_t local_size = part.local_size();
    std::size_t offset = part.eprefix_size();

    // open file
    std::ifstream t(filename);
    // wrap in our custom range buffer (of type std::streambuf)
    rangebuf rb(offset, local_size, t.rdbuf());

    // read file (range) buffer into string stream
    std::stringstream ss;
    ss << &rb;

    std::string local_str(ss.str());

    return local_str;
}

template <typename T>
void write_ordered(const std::string& filename, const T* buf, size_t count, const mxx::comm& comm) {
    MPI_File handle;
    MPI_File_open(comm, const_cast<char*>(&filename[0]), MPI_MODE_CREATE | MPI_MODE_WRONLY, MPI_INFO_NULL, &handle);
    mxx::datatype dt = mxx::get_datatype<T>();
    MPI_File_write_ordered(handle, const_cast<T*>(buf), count, dt.type(), MPI_STATUS_IGNORE);
    MPI_File_close(&handle);
}
template <typename T>
void write_ordered(const std::string& filename, const std::vector<T>& data, const mxx::comm& comm) {
    write_ordered(filename, data.data(), data.size(), comm);
}

template <typename _Iterator>
void write_files(const std::string& filename, _Iterator begin, _Iterator end, MPI_Comm comm = MPI_COMM_WORLD)
{
    // get MPI Communicator properties
    int rank, p;
    MPI_Comm_size(comm, &p);
    MPI_Comm_rank(comm, &rank);

    // get max rank string length:
    std::stringstream sslen;
    sslen << p;
    int rank_slen = sslen.str().size();

    // concat rank at end of filename
    std::stringstream ss;
    ss << filename << "." << std::setfill('0') << std::setw(rank_slen) << p << "." << std::setfill('0') << std::setw(rank_slen) << rank;

    // open file with stream
    //std::cerr << "writing to file " << ss.str() << std::endl;
    std::ofstream outfs(ss.str());

    // write the content into the file, sep by newline
    while (begin != end)
    {
        outfs << *(begin++) << std::endl;
    }
    outfs.close();
}

} // namespace mxx

#endif // MXX_FILE_HPP
<|endoftext|>


<commit_before>#pragma once

#include <algorithm>
#include <cmath>
#include <cstdint>
#include <tuple>
#include <vector>

namespace kdbush {

template <std::uint8_t I, typename T>
struct nth {
    inline static typename std::tuple_element<I, T>::type get(const T &t) {
        return std::get<I>(t);
    }
};

template <typename TPoint, typename TIndex = std::size_t>
class KDBush {

public:
    using TNumber = decltype(nth<0, TPoint>::get(std::declval<TPoint>()));

    static const std::uint8_t defaultNodeSize = 64;

    KDBush(const std::vector<TPoint> &points_, const std::uint8_t nodeSize_ = defaultNodeSize)
        : KDBush(std::begin(points_), std::end(points_), nodeSize_) {
    }

    template <typename TPointIter>
    KDBush(TPointIter points_begin,
           TPointIter points_end,
           const std::uint8_t nodeSize_ = defaultNodeSize)
        : nodeSize(nodeSize_) {

        const TIndex size = std::distance(points_begin, points_end);

        points.reserve(size);
        ids.reserve(size);

        for (TIndex i = 0; i < size; i++) {
            const auto p = *(points_begin + i);
            points.emplace_back(nth<0, TPoint>::get(p), nth<1, TPoint>::get(p));
            ids.push_back(i);
        }

        sortKD(0, size - 1, 0);
    }

    template <typename TOutputIter>
    void range(const TNumber minX,
               const TNumber minY,
               const TNumber maxX,
               const TNumber maxY,
               TOutputIter out) {
        range(minX, minY, maxX, maxY, out, 0, ids.size() - 1, 0);
    }

    template <typename TOutputIter>
    void within(const TNumber qx, const TNumber qy, const TNumber r, TOutputIter out) {
        within(qx, qy, r, out, 0, ids.size() - 1, 0);
    }

private:
    std::vector<TIndex> ids;
    std::vector<std::pair<TNumber, TNumber>> points;
    std::uint8_t nodeSize;

    template <typename TOutputIter>
    void range(const TNumber minX,
               const TNumber minY,
               const TNumber maxX,
               const TNumber maxY,
               TOutputIter out,
               const TIndex left,
               const TIndex right,
               const std::uint8_t axis) {

        if (right - left <= nodeSize) {
            for (auto i = left; i <= right; i++) {
                const TNumber x = std::get<0>(points[i]);
                const TNumber y = std::get<1>(points[i]);
                if (x >= minX && x <= maxX && y >= minY && y <= maxY) *out++ = ids[i];
            }
            return;
        }

        const TIndex m = (left + right) >> 1;
        const TNumber x = std::get<0>(points[m]);
        const TNumber y = std::get<1>(points[m]);

        if (x >= minX && x <= maxX && y >= minY && y <= maxY) *out++ = ids[m];

        if (axis == 0 ? minX <= x : minY <= y)
            range(minX, minY, maxX, maxY, out, left, m - 1, (axis + 1) % 2);

        if (axis == 0 ? maxX >= x : maxY >= y)
            range(minX, minY, maxX, maxY, out, m + 1, right, (axis + 1) % 2);
    }

    template <typename TOutputIter>
    void within(const TNumber qx,
                const TNumber qy,
                const TNumber r,
                TOutputIter out,
                const TIndex left,
                const TIndex right,
                const std::uint8_t axis) {

        const TNumber r2 = r * r;

        if (right - left <= nodeSize) {
            for (auto i = left; i <= right; i++) {
                const TNumber x = std::get<0>(points[i]);
                const TNumber y = std::get<1>(points[i]);
                if (sqDist(x, y, qx, qy) <= r2) *out++ = ids[i];
            }
            return;
        }

        const TIndex m = (left + right) >> 1;
        const TNumber x = std::get<0>(points[m]);
        const TNumber y = std::get<1>(points[m]);

        if (sqDist(x, y, qx, qy) <= r2) *out++ = ids[m];

        if (axis == 0 ? qx - r <= x : qy - r <= y)
            within(qx, qy, r, out, left, m - 1, (axis + 1) % 2);

        if (axis == 0 ? qx + r >= x : qy + r >= y)
            within(qx, qy, r, out, m + 1, right, (axis + 1) % 2);
    }

    void sortKD(const TIndex left, const TIndex right, const std::uint8_t axis) {
        if (right - left <= nodeSize) return;
        const TIndex m = (left + right) >> 1;
        if (axis == 0) {
            select<0>(m, left, right);
        } else {
            select<1>(m, left, right);
        }
        sortKD(left, m - 1, (axis + 1) % 2);
        sortKD(m + 1, right, (axis + 1) % 2);
    }

    template <std::uint8_t axis>
    void select(const TIndex k, TIndex left, TIndex right) {

        while (right > left) {
            if (right - left > 600) {
                const TIndex n = right - left + 1;
                const TIndex m = k - left + 1;
                const double z = log(n);
                const double s = 0.5 * exp(2 * z / 3);
                const double sd = 0.5 * sqrt(z * s * (n - s) / n) * (2 * m < n ? -1 : 1);
                const TIndex newLeft = std::max(left, TIndex(k - m * s / n + sd));
                const TIndex newRight = std::min(right, TIndex(k + (n - m) * s / n + sd));
                select<axis>(k, newLeft, newRight);
            }

            const TNumber t = std::get<axis>(points[k]);
            TIndex i = left;
            TIndex j = right;

            swapItem(left, k);
            if (std::get<axis>(points[right]) > t) swapItem(left, right);

            while (i < j) {
                swapItem(i, j);
                i++;
                j--;
                while (std::get<axis>(points[i]) < t) i++;
                while (std::get<axis>(points[j]) > t) j--;
            }

            if (std::get<axis>(points[left]) == t)
                swapItem(left, j);
            else {
                j++;
                swapItem(j, right);
            }

            if (j <= k) left = j + 1;
            if (k <= j) right = j - 1;
        }
    }

    void swapItem(const TIndex i, const TIndex j) {
        std::iter_swap(ids.begin() + i, ids.begin() + j);
        std::iter_swap(points.begin() + i, points.begin() + j);
    }

    TNumber sqDist(const TNumber ax, const TNumber ay, const TNumber bx, const TNumber by) {
        const TNumber dx = ax - bx;
        const TNumber dy = ay - by;
        return dx * dx + dy * dy;
    }
};

} // namespace kdbush
<commit_msg>add a static assert for point components<commit_after>#pragma once

#include <algorithm>
#include <cmath>
#include <cstdint>
#include <tuple>
#include <vector>

namespace kdbush {

template <std::uint8_t I, typename T>
struct nth {
    inline static typename std::tuple_element<I, T>::type get(const T &t) {
        return std::get<I>(t);
    }
};

template <typename TPoint, typename TIndex = std::size_t>
class KDBush {

public:
    using TNumber = decltype(nth<0, TPoint>::get(std::declval<TPoint>()));
    static_assert(
        std::is_same<TNumber, decltype(nth<1, TPoint>::get(std::declval<TPoint>()))>::value,
        "point component types must be identical");

    static const std::uint8_t defaultNodeSize = 64;

    KDBush(const std::vector<TPoint> &points_, const std::uint8_t nodeSize_ = defaultNodeSize)
        : KDBush(std::begin(points_), std::end(points_), nodeSize_) {
    }

    template <typename TPointIter>
    KDBush(TPointIter points_begin,
           TPointIter points_end,
           const std::uint8_t nodeSize_ = defaultNodeSize)
        : nodeSize(nodeSize_) {

        const TIndex size = std::distance(points_begin, points_end);

        points.reserve(size);
        ids.reserve(size);

        for (TIndex i = 0; i < size; i++) {
            const auto p = *(points_begin + i);
            points.emplace_back(nth<0, TPoint>::get(p), nth<1, TPoint>::get(p));
            ids.push_back(i);
        }

        sortKD(0, size - 1, 0);
    }

    template <typename TOutputIter>
    void range(const TNumber minX,
               const TNumber minY,
               const TNumber maxX,
               const TNumber maxY,
               TOutputIter out) {
        range(minX, minY, maxX, maxY, out, 0, ids.size() - 1, 0);
    }

    template <typename TOutputIter>
    void within(const TNumber qx, const TNumber qy, const TNumber r, TOutputIter out) {
        within(qx, qy, r, out, 0, ids.size() - 1, 0);
    }

private:
    std::vector<TIndex> ids;
    std::vector<std::pair<TNumber, TNumber>> points;
    std::uint8_t nodeSize;

    template <typename TOutputIter>
    void range(const TNumber minX,
               const TNumber minY,
               const TNumber maxX,
               const TNumber maxY,
               TOutputIter out,
               const TIndex left,
               const TIndex right,
               const std::uint8_t axis) {

        if (right - left <= nodeSize) {
            for (auto i = left; i <= right; i++) {
                const TNumber x = std::get<0>(points[i]);
                const TNumber y = std::get<1>(points[i]);
                if (x >= minX && x <= maxX && y >= minY && y <= maxY) *out++ = ids[i];
            }
            return;
        }

        const TIndex m = (left + right) >> 1;
        const TNumber x = std::get<0>(points[m]);
        const TNumber y = std::get<1>(points[m]);

        if (x >= minX && x <= maxX && y >= minY && y <= maxY) *out++ = ids[m];

        if (axis == 0 ? minX <= x : minY <= y)
            range(minX, minY, maxX, maxY, out, left, m - 1, (axis + 1) % 2);

        if (axis == 0 ? maxX >= x : maxY >= y)
            range(minX, minY, maxX, maxY, out, m + 1, right, (axis + 1) % 2);
    }

    template <typename TOutputIter>
    void within(const TNumber qx,
                const TNumber qy,
                const TNumber r,
                TOutputIter out,
                const TIndex left,
                const TIndex right,
                const std::uint8_t axis) {

        const TNumber r2 = r * r;

        if (right - left <= nodeSize) {
            for (auto i = left; i <= right; i++) {
                const TNumber x = std::get<0>(points[i]);
                const TNumber y = std::get<1>(points[i]);
                if (sqDist(x, y, qx, qy) <= r2) *out++ = ids[i];
            }
            return;
        }

        const TIndex m = (left + right) >> 1;
        const TNumber x = std::get<0>(points[m]);
        const TNumber y = std::get<1>(points[m]);

        if (sqDist(x, y, qx, qy) <= r2) *out++ = ids[m];

        if (axis == 0 ? qx - r <= x : qy - r <= y)
            within(qx, qy, r, out, left, m - 1, (axis + 1) % 2);

        if (axis == 0 ? qx + r >= x : qy + r >= y)
            within(qx, qy, r, out, m + 1, right, (axis + 1) % 2);
    }

    void sortKD(const TIndex left, const TIndex right, const std::uint8_t axis) {
        if (right - left <= nodeSize) return;
        const TIndex m = (left + right) >> 1;
        if (axis == 0) {
            select<0>(m, left, right);
        } else {
            select<1>(m, left, right);
        }
        sortKD(left, m - 1, (axis + 1) % 2);
        sortKD(m + 1, right, (axis + 1) % 2);
    }

    template <std::uint8_t axis>
    void select(const TIndex k, TIndex left, TIndex right) {

        while (right > left) {
            if (right - left > 600) {
                const TIndex n = right - left + 1;
                const TIndex m = k - left + 1;
                const double z = log(n);
                const double s = 0.5 * exp(2 * z / 3);
                const double sd = 0.5 * sqrt(z * s * (n - s) / n) * (2 * m < n ? -1 : 1);
                const TIndex newLeft = std::max(left, TIndex(k - m * s / n + sd));
                const TIndex newRight = std::min(right, TIndex(k + (n - m) * s / n + sd));
                select<axis>(k, newLeft, newRight);
            }

            const TNumber t = std::get<axis>(points[k]);
            TIndex i = left;
            TIndex j = right;

            swapItem(left, k);
            if (std::get<axis>(points[right]) > t) swapItem(left, right);

            while (i < j) {
                swapItem(i, j);
                i++;
                j--;
                while (std::get<axis>(points[i]) < t) i++;
                while (std::get<axis>(points[j]) > t) j--;
            }

            if (std::get<axis>(points[left]) == t)
                swapItem(left, j);
            else {
                j++;
                swapItem(j, right);
            }

            if (j <= k) left = j + 1;
            if (k <= j) right = j - 1;
        }
    }

    void swapItem(const TIndex i, const TIndex j) {
        std::iter_swap(ids.begin() + i, ids.begin() + j);
        std::iter_swap(points.begin() + i, points.begin() + j);
    }

    TNumber sqDist(const TNumber ax, const TNumber ay, const TNumber bx, const TNumber by) {
        const TNumber dx = ax - bx;
        const TNumber dy = ay - by;
        return dx * dx + dy * dy;
    }
};

} // namespace kdbush
<|endoftext|>


<commit_before>/*
 *  Traditional K&R Hello World program done in C++.
 */
#include <iostream>
#include <thread>

auto hw = "  Hello World!";
auto gw = "Goodbye World!";

void hello()
{
    using namespace std;

    cout << hw+2 << endl;
}

int main()
{
    std::thread t(hello);
    t.join();
    std::cout << gw << '\n';

    return 0;
}

/*
 *  Notes:
 *
 *  1. Type infarence via auto keyword
 *  2. IOSTREAM library
 *  3. Use of namespaces
 *  4. Still lets you "fiddle the bits" with
 *     pointers to chracter arrays
 *  5. Unlike ANSI C, main's prototype indicates
 *     that it takes no arguments.
 *  6. Function overloading makes << easy to deal with
 *  7. Built in std::thread library even with base compile
 *  8. Currently not specifying any sort of "standard"
 *  8. Need to compile with
 *       g++ -Wall -pthread hw.cpp -o hw
 *     otherwise linker error with pthread_create
 *     undefined.  Got the -pthread from the pthread_create
 *     Linux, not POSIX, manpage.
 */

<commit_msg>ToInfinityAndBeyond/hw.cpp change<commit_after>/*
 *  Traditional K&R Hello World program done in C++.
 */
#include <iostream>
#include <thread>

auto hw {"  Hello World!"};
auto gw {"Goodbye World!"};

void hello()
{
    using namespace std;

    cout << hw+2 << endl;
}

int main()
{
    std::thread t(hello);
    t.join();
    std::cout << gw << '\n';

    return 0;
}

/*
 *  Notes:
 *
 *  1. Type infarence via auto keyword
 *  2. IOSTREAM library
 *  3. Uniform Initialization turns off Fortran-like automatic
 *     data converion.
 *  4. Use of namespaces
 *  5. Still lets you "fiddle the bits" with
 *     pointers to chracter arrays
 *  6. Unlike ANSI C, main's prototype indicates
 *     that it takes no arguments.
 *  7. Function overloading makes << easy to deal with
 *  8. Built in std::thread library even with base compile
 *  9. Currently not specifying any sort of "standard"
 * 10. Need to compile with
 *       g++ -Wall -pthread hw.cpp -o hw
 *     otherwise linker error with pthread_create
 *     undefined.  Got the -pthread from the pthread_create
 *     Linux, not POSIX, manpage.
 */

<|endoftext|>