<commit_before>/** @file
    @brief Implementation

    @date 2015

    @author
    Sensics, Inc.
    <http://sensics.com/osvr>
*/

// Copyright 2015 Sensics, Inc.
//
// All rights reserved.
//
// (Final version intended to be licensed under
// the Apache License, Version 2.0)

// Internal Includes
#include <osvr/PluginKit/PluginKit.h>
#include <osvr/PluginKit/AnalogInterfaceC.h>
#include <osvr/PluginKit/ButtonInterfaceC.h>
#include <osvr/PluginKit/TrackerInterfaceC.h>

// Library/third-party includes
// - none

// Standard includes
#include <iostream>

OSVR_MessageType dummyMessage;

class MultipleSyncDevice {
  public:
    MultipleSyncDevice(OSVR_DeviceToken d, OSVR_AnalogDeviceInterface analog,
                       OSVR_ButtonDeviceInterface button,
                       OSVR_TrackerDeviceInterface tracker)
        : m_dev(d), m_analog(analog), m_button(button), m_tracker(tracker),
          m_myVal(0), m_buttonPressed(false) {}

    /// Trampoline: C-compatible callback bouncing into a member function.
    static OSVR_ReturnCode update(void *userData) {
        return static_cast<MultipleSyncDevice *>(userData)->m_update();
    }

  private:
    OSVR_ReturnCode m_update() {
        /// Make up some dummy data that changes to report.
        m_myVal = (m_myVal + 0.1);
        if (m_myVal > 10.0) {
            m_myVal = 0;
        }

        /// Report the value of channel 0
        osvrDeviceAnalogSetValue(m_dev, m_analog, m_myVal, 0);

        /// Toggle the button 0
        m_buttonPressed = !m_buttonPressed;
        osvrDeviceButtonSetValue(
            m_dev, m_button,
            m_buttonPressed ? OSVR_BUTTON_PRESSED : OSVR_BUTTON_NOT_PRESSED, 0);

        /// Report the identity pose for sensor 0
        OSVR_PoseState pose;
        osvrPose3SetIdentity(&pose);
        osvrDeviceTrackerSendPose(m_dev, m_tracker, &pose, 0);
        return OSVR_RETURN_SUCCESS;
    }
    OSVR_DeviceToken m_dev;
    OSVR_AnalogDeviceInterface m_analog;
    OSVR_ButtonDeviceInterface m_button;
    OSVR_TrackerDeviceInterface m_tracker;
    double m_myVal;
    bool m_buttonPressed;
};

OSVR_PLUGIN(com_osvr_example_AnalogSync) {
    /// Create the initialization options
    OSVR_DeviceInitOptions opts = osvrDeviceCreateInitOptions(ctx);

    /// Indicate that we'll want 1 analog channel.
    OSVR_AnalogDeviceInterface analog;
    osvrDeviceAnalogConfigure(opts, &analog, 1);

    /// Indicate that we'll want 2 buttons.
    OSVR_ButtonDeviceInterface button;
    osvrDeviceButtonConfigure(opts, &button, 2);

    /// Indicate that we'll report tracking too.
    OSVR_TrackerDeviceInterface tracker;
    osvrDeviceTrackerConfigure(opts, &tracker);

    /// Create the sync device token with the options
    OSVR_DeviceToken d;
    osvrDeviceSyncInitWithOptions(ctx, "MySyncDevice", opts, &d);
    MultipleSyncDevice *myDevice = osvr::pluginkit::registerObjectForDeletion(
        ctx, new MultipleSyncDevice(d, analog, button, tracker));

    osvrDeviceSyncRegisterUpdateCallback(d, &MultipleSyncDevice::update,
                                         myDevice);
    return OSVR_RETURN_SUCCESS;
}
<commit_msg>Change structure of multiple sync plugin.<commit_after>/** @file
    @brief Implementation

    @date 2015

    @author
    Sensics, Inc.
    <http://sensics.com/osvr>
*/

// Copyright 2015 Sensics, Inc.
//
// All rights reserved.
//
// (Final version intended to be licensed under
// the Apache License, Version 2.0)

// Internal Includes
#include <osvr/PluginKit/PluginKit.h>
#include <osvr/PluginKit/AnalogInterfaceC.h>
#include <osvr/PluginKit/ButtonInterfaceC.h>
#include <osvr/PluginKit/TrackerInterfaceC.h>

// Library/third-party includes
// - none

// Standard includes
#include <iostream>

OSVR_MessageType dummyMessage;

class MultipleSyncDevice {
  public:
    MultipleSyncDevice(OSVR_PluginRegContext ctx)
        : m_myVal(0), m_buttonPressed(false) {
        /// Create the initialization options
        OSVR_DeviceInitOptions opts = osvrDeviceCreateInitOptions(ctx);

        /// Indicate that we'll want 1 analog channel.
        osvrDeviceAnalogConfigure(opts, &m_analog, 1);

        /// Indicate that we'll want 1 button.
        osvrDeviceButtonConfigure(opts, &m_button, 1);

        /// Indicate that we'll report tracking too.
        osvrDeviceTrackerConfigure(opts, &m_tracker);

        /// Create the sync device token with the options
        osvrDeviceSyncInitWithOptions(ctx, "MySyncDevice", opts, &m_dev);

        /// Register update callback
        osvrDeviceSyncRegisterUpdateCallback(m_dev, &MultipleSyncDevice::update,
                                             this);
    }

    /// Trampoline: C-compatible callback bouncing into a member function.
    static OSVR_ReturnCode update(void *userData) {
        return static_cast<MultipleSyncDevice *>(userData)->m_update();
    }

  private:
    OSVR_ReturnCode m_update() {
        /// Make up some dummy data that changes to report.
        m_myVal = (m_myVal + 0.1);
        if (m_myVal > 10.0) {
            m_myVal = 0;
        }

        /// Report the value of channel 0
        osvrDeviceAnalogSetValue(m_dev, m_analog, m_myVal, 0);

        /// Toggle the button 0
        m_buttonPressed = !m_buttonPressed;
        osvrDeviceButtonSetValue(
            m_dev, m_button,
            m_buttonPressed ? OSVR_BUTTON_PRESSED : OSVR_BUTTON_NOT_PRESSED, 0);

        /// Report the identity pose for sensor 0
        OSVR_PoseState pose;
        osvrPose3SetIdentity(&pose);
        osvrDeviceTrackerSendPose(m_dev, m_tracker, &pose, 0);
        return OSVR_RETURN_SUCCESS;
    }
    OSVR_DeviceToken m_dev;
    OSVR_AnalogDeviceInterface m_analog;
    OSVR_ButtonDeviceInterface m_button;
    OSVR_TrackerDeviceInterface m_tracker;
    double m_myVal;
    bool m_buttonPressed;
};

OSVR_PLUGIN(com_osvr_example_MultipleSync) {

    /// Create device object.
    MultipleSyncDevice *myDevice = osvr::pluginkit::registerObjectForDeletion(
        ctx, new MultipleSyncDevice(ctx));

    return OSVR_RETURN_SUCCESS;
}
<|endoftext|>


<commit_before>/******************************************************************************
 *                    _   _____   __________                                  *
 *                   | | / / _ | / __/_  __/     Visibility                   *
 *                   | |/ / __ |_\ \  / /          Across                     *
 *                   |___/_/ |_/___/ /_/       Space and Time                 *
 *                                                                            *
 * This file is part of VAST. It is subject to the license terms in the       *
 * LICENSE file found in the top-level directory of this distribution and at  *
 * http://vast.io/license. No part of VAST, including this file, may be       *
 * copied, modified, propagated, or distributed except according to the terms *
 * contained in the LICENSE file.                                             *
 ******************************************************************************/

#define SUITE meta_index

#include "vast/meta_index.hpp"

#include "vast/test/test.hpp"
#include "vast/test/fixtures/actor_system.hpp"

#include "vast/default_table_slice_builder.hpp"
#include "vast/synopsis.hpp"
#include "vast/synopsis_factory.hpp"
#include "vast/table_slice.hpp"
#include "vast/table_slice_builder.hpp"
#include "vast/uuid.hpp"
#include "vast/view.hpp"

#include "vast/concept/parseable/to.hpp"
#include "vast/concept/parseable/vast/expression.hpp"

#include "vast/detail/overload.hpp"

using namespace vast;

using std::literals::operator""s;

namespace {

constexpr size_t num_partitions = 4;
constexpr size_t num_events_per_parttion = 25;

const timestamp epoch;

timestamp get_timestamp(caf::optional<data_view> element){
  return materialize(caf::get<view<timestamp>>(*element));
}

// Builds a chain of events that are 1s apart, where consecutive chunks of
// num_events_per_type events have the same type.
struct generator {
  id offset;

  explicit generator(std::string name, size_t first_event_id)
    : offset(first_event_id) {
    layout = record_type{
      {"timestamp", timestamp_type{}.attributes({{"time"}})},
      {"content", string_type{}}
    }.name(std::move(name));
  }

  table_slice_ptr operator()(size_t num) {
    auto builder = default_table_slice_builder::make(layout);
    for (size_t i = 0; i < num; ++i) {
      timestamp ts = epoch + std::chrono::seconds(i + offset);
      builder->add(make_data_view(ts));
      builder->add(make_data_view("foo"));
    }
    auto slice = builder->finish();
    slice.unshared().offset(offset);
    offset += num;
    return slice;
  }

  record_type layout;
};

// A closed interval of time.
struct interval {
  timestamp from;
  timestamp to;
};

struct mock_partition {
  mock_partition(std::string name, uuid uid, size_t num) : id(std::move(uid)) {
    generator g{std::move(name), num_events_per_parttion * num};
    slice = g(num_events_per_parttion);
    range.from = get_timestamp(slice->at(0, 0));
    range.to = get_timestamp(slice->at(slice->rows() - 1, 0));
  }

  uuid id;
  table_slice_ptr slice;
  interval range;
};

struct fixture {
  fixture() {
    MESSAGE("register synopsis factory");
    factory<synopsis>::initialize();
    MESSAGE("generate " << num_partitions << " UUIDs for the partitions");
    for (size_t i = 0; i < num_partitions; ++i)
      ids.emplace_back(uuid::random());
    std::sort(ids.begin(), ids.end());
    // Sanity check random UUID generation.
    for (size_t i = 0; i < num_partitions; ++i)
      for (size_t j = 0; j < num_partitions; ++j)
        if (i != j && ids[i] == ids[j])
          FAIL("ID " << i << " and " << j << " are equal!");
    MESSAGE("generate events and add events to the partition index");
    std::vector<mock_partition> mock_partitions;
    for (size_t i = 0; i < num_partitions; ++i) {
      auto name = i % 2 == 0 ? "foo"s : "foobar"s;
      auto& part = mock_partitions.emplace_back(std::move(name), ids[i], i);
      meta_idx.add(part.id, *part.slice);
    }
    MESSAGE("verify generated timestamps");
    {
      auto& p0 = mock_partitions[0];
      CHECK_EQUAL(p0.range.from, epoch);
      CHECK_EQUAL(p0.range.to, epoch + 24s);
      auto& p1 = mock_partitions[1];
      CHECK_EQUAL(p1.range.from, epoch + 25s);
      CHECK_EQUAL(p1.range.to, epoch + 49s);
      auto& p2 = mock_partitions[2];
      CHECK_EQUAL(p2.range.from, epoch + 50s);
      CHECK_EQUAL(p2.range.to, epoch + 74s);
      auto& p3 = mock_partitions[3];
      CHECK_EQUAL(p3.range.from, epoch + 75s);
      CHECK_EQUAL(p3.range.to, epoch + 99s);
    }
    MESSAGE("run test");
  }

  auto slice(size_t first, size_t last) const {
    std::vector<uuid> result;
    if (first < ids.size())
      for (; first != std::min(last, ids.size()); ++first)
        result.emplace_back(ids[first]);
    std::sort(result.begin(), result.end());
    return result;
  }

  auto slice(size_t index) const {
    return slice(index, index + 1);
  }

  auto attr_time_query(std::string_view hhmmss) {
    std::string q = "&time == 1970-01-01+";
    q += hhmmss;
    q += ".0";
    return meta_idx.lookup(unbox(to<expression>(q)));
  }

  auto empty() const {
    return slice(ids.size());
  }

  auto lookup(std::string_view expr) {
    auto result = meta_idx.lookup(unbox(to<expression>(expr)));
    std::sort(result.begin(), result.end());
    return result;
  }

  auto attr_time_query(std::string_view hhmmss_from, std::string_view hhmmss_to) {
    std::string q = "&time >= 1970-01-01+";
    q += hhmmss_from;
    q += ".0";
    q += " && &time <= 1970-01-01+";
    q += hhmmss_to;
    q += ".0";
    return lookup(q);
  }

  // Our unit-under-test.
  meta_index meta_idx;

  // Partition IDs.
  std::vector<uuid> ids;
};

} // namespace <anonymous>

FIXTURE_SCOPE(meta_index_tests, fixture)

TEST(attribute extractor - &time) {
  MESSAGE("check whether point queries return correct slices");
  CHECK_EQUAL(attr_time_query("00:00:00"), slice(0));
  CHECK_EQUAL(attr_time_query("00:00:24"), slice(0));
  CHECK_EQUAL(attr_time_query("00:00:25"), slice(1));
  CHECK_EQUAL(attr_time_query("00:00:49"), slice(1));
  CHECK_EQUAL(attr_time_query("00:00:50"), slice(2));
  CHECK_EQUAL(attr_time_query("00:01:14"), slice(2));
  CHECK_EQUAL(attr_time_query("00:01:15"), slice(3));
  CHECK_EQUAL(attr_time_query("00:01:39"), slice(3));
  CHECK_EQUAL(attr_time_query("00:01:40"), empty());
  MESSAGE("check whether time-range queries return correct slices");
  CHECK_EQUAL(attr_time_query("00:00:01", "00:00:10"), slice(0));
  CHECK_EQUAL(attr_time_query("00:00:10", "00:00:30"), slice(0, 2));
}

TEST(attribute extractor - &type) {
  auto foo = std::vector<uuid>{ids[0], ids[2]};
  auto foobar = std::vector<uuid>{ids[1], ids[3]};
  CHECK_EQUAL(lookup("&type == \"foo\""), foo);
  CHECK_EQUAL(lookup("&type == \"bar\""), empty());
  CHECK_EQUAL(lookup("&type != \"foo\""), foobar);
  CHECK_EQUAL(lookup("&type ~ /f.o/"), foo);
  CHECK_EQUAL(lookup("&type ~ /f.*/"), ids);
  CHECK_EQUAL(lookup("&type ~ /x/"), empty());
  CHECK_EQUAL(lookup("&type !~ /x/"), ids);
}

FIXTURE_SCOPE_END()

FIXTURE_SCOPE(metaidx_serialization_tests, fixtures::deterministic_actor_system)

TEST(serialization) {
  meta_index meta_idx;
  auto part = mock_partition{"foo", uuid::random(), 42};
  meta_idx.add(part.id, *part.slice);
  CHECK_ROUNDTRIP(meta_idx);
}

TEST(meta index with boolean synopsis) {
  MESSAGE("generate slice data and add it to the meta index");
  meta_index meta_idx;
  auto layout = record_type{{"x", boolean_type{}}};
  auto builder = default_table_slice_builder::make(layout);
  CHECK(builder->add(make_data_view(true)));
  auto slice = builder->finish();
  REQUIRE(slice != nullptr);
  auto id1 = uuid::random();
  meta_idx.add(id1, *slice);
  CHECK(builder->add(make_data_view(false)));
  slice = builder->finish();
  REQUIRE(slice != nullptr);
  auto id2 = uuid::random();
  meta_idx.add(id2, *slice);
  MESSAGE("test custom synopsis");
  auto all = std::vector<uuid>{id1, id2};
  std::sort(all.begin(), all.end());
  auto expected1 = std::vector<uuid>{id1};
  auto expected2 = std::vector<uuid>{id2};
  auto lookup = [&](std::string_view expr) {
    return meta_idx.lookup(unbox(to<expression>(expr)));
  };
  // Check by field name field.
  CHECK_EQUAL(lookup("x == T"), expected1);
  CHECK_EQUAL(lookup("x != F"), expected1);
  CHECK_EQUAL(lookup("x == F"), expected2);
  CHECK_EQUAL(lookup("x != T"), expected2);
  // Same as above, different extractor.
  CHECK_EQUAL(lookup(":bool == T"), expected1);
  CHECK_EQUAL(lookup(":bool != F"), expected1);
  CHECK_EQUAL(lookup(":bool == F"), expected2);
  CHECK_EQUAL(lookup(":bool != T"), expected2);
  // Invalid schema: y does not a valid field
  CHECK_EQUAL(lookup("y == T"), all);
  CHECK_EQUAL(lookup("y != F"), all);
  CHECK_EQUAL(lookup("y == F"), all);
  CHECK_EQUAL(lookup("y != T"), all);
  MESSAGE("perform serialization");
  CHECK_ROUNDTRIP(meta_idx);
}

TEST(option setting and retrieval) {
  meta_index meta_idx;
  auto& opts = meta_idx.factory_options();
  put(opts, "foo", 42);
  auto x = caf::get_if<caf::config_value::integer>(&opts["foo"]);
  CHECK_EQUAL(unbox(x), 42);
}

FIXTURE_SCOPE_END()
<commit_msg>Make meta_index test fail by inserting caf::none_t<commit_after>/******************************************************************************
 *                    _   _____   __________                                  *
 *                   | | / / _ | / __/_  __/     Visibility                   *
 *                   | |/ / __ |_\ \  / /          Across                     *
 *                   |___/_/ |_/___/ /_/       Space and Time                 *
 *                                                                            *
 * This file is part of VAST. It is subject to the license terms in the       *
 * LICENSE file found in the top-level directory of this distribution and at  *
 * http://vast.io/license. No part of VAST, including this file, may be       *
 * copied, modified, propagated, or distributed except according to the terms *
 * contained in the LICENSE file.                                             *
 ******************************************************************************/

#define SUITE meta_index

#include "vast/meta_index.hpp"

#include "vast/test/test.hpp"
#include "vast/test/fixtures/actor_system.hpp"

#include "vast/default_table_slice_builder.hpp"
#include "vast/synopsis.hpp"
#include "vast/synopsis_factory.hpp"
#include "vast/table_slice.hpp"
#include "vast/table_slice_builder.hpp"
#include "vast/uuid.hpp"
#include "vast/view.hpp"

#include "vast/concept/parseable/to.hpp"
#include "vast/concept/parseable/vast/expression.hpp"

#include "vast/detail/overload.hpp"

using namespace vast;

using std::literals::operator""s;

namespace {

constexpr size_t num_partitions = 4;
constexpr size_t num_events_per_parttion = 25;

const timestamp epoch;

timestamp get_timestamp(caf::optional<data_view> element){
  return materialize(caf::get<view<timestamp>>(*element));
}

// Builds a chain of events that are 1s apart, where consecutive chunks of
// num_events_per_type events have the same type.
struct generator {
  id offset;

  explicit generator(std::string name, size_t first_event_id)
    : offset(first_event_id) {
    layout = record_type{
      {"timestamp", timestamp_type{}.attributes({{"time"}})},
      {"content", string_type{}}
    }.name(std::move(name));
  }

  table_slice_ptr operator()(size_t num) {
    auto builder = default_table_slice_builder::make(layout);
    for (size_t i = 0; i < num; ++i) {
      timestamp ts = epoch + std::chrono::seconds(i + offset);
      builder->add(make_data_view(ts));
      builder->add(make_data_view("foo"));
    }
    auto slice = builder->finish();
    slice.unshared().offset(offset);
    offset += num;
    return slice;
  }

  record_type layout;
};

// A closed interval of time.
struct interval {
  timestamp from;
  timestamp to;
};

struct mock_partition {
  mock_partition(std::string name, uuid uid, size_t num) : id(std::move(uid)) {
    generator g{std::move(name), num_events_per_parttion * num};
    slice = g(num_events_per_parttion);
    range.from = get_timestamp(slice->at(0, 0));
    range.to = get_timestamp(slice->at(slice->rows() - 1, 0));
  }

  uuid id;
  table_slice_ptr slice;
  interval range;
};

struct fixture {
  fixture() {
    MESSAGE("register synopsis factory");
    factory<synopsis>::initialize();
    MESSAGE("generate " << num_partitions << " UUIDs for the partitions");
    for (size_t i = 0; i < num_partitions; ++i)
      ids.emplace_back(uuid::random());
    std::sort(ids.begin(), ids.end());
    // Sanity check random UUID generation.
    for (size_t i = 0; i < num_partitions; ++i)
      for (size_t j = 0; j < num_partitions; ++j)
        if (i != j && ids[i] == ids[j])
          FAIL("ID " << i << " and " << j << " are equal!");
    MESSAGE("generate events and add events to the partition index");
    std::vector<mock_partition> mock_partitions;
    for (size_t i = 0; i < num_partitions; ++i) {
      auto name = i % 2 == 0 ? "foo"s : "foobar"s;
      auto& part = mock_partitions.emplace_back(std::move(name), ids[i], i);
      meta_idx.add(part.id, *part.slice);
    }
    MESSAGE("verify generated timestamps");
    {
      auto& p0 = mock_partitions[0];
      CHECK_EQUAL(p0.range.from, epoch);
      CHECK_EQUAL(p0.range.to, epoch + 24s);
      auto& p1 = mock_partitions[1];
      CHECK_EQUAL(p1.range.from, epoch + 25s);
      CHECK_EQUAL(p1.range.to, epoch + 49s);
      auto& p2 = mock_partitions[2];
      CHECK_EQUAL(p2.range.from, epoch + 50s);
      CHECK_EQUAL(p2.range.to, epoch + 74s);
      auto& p3 = mock_partitions[3];
      CHECK_EQUAL(p3.range.from, epoch + 75s);
      CHECK_EQUAL(p3.range.to, epoch + 99s);
    }
    MESSAGE("run test");
  }

  auto slice(size_t first, size_t last) const {
    std::vector<uuid> result;
    if (first < ids.size())
      for (; first != std::min(last, ids.size()); ++first)
        result.emplace_back(ids[first]);
    std::sort(result.begin(), result.end());
    return result;
  }

  auto slice(size_t index) const {
    return slice(index, index + 1);
  }

  auto attr_time_query(std::string_view hhmmss) {
    std::string q = "&time == 1970-01-01+";
    q += hhmmss;
    q += ".0";
    return meta_idx.lookup(unbox(to<expression>(q)));
  }

  auto empty() const {
    return slice(ids.size());
  }

  auto lookup(std::string_view expr) {
    auto result = meta_idx.lookup(unbox(to<expression>(expr)));
    std::sort(result.begin(), result.end());
    return result;
  }

  auto attr_time_query(std::string_view hhmmss_from, std::string_view hhmmss_to) {
    std::string q = "&time >= 1970-01-01+";
    q += hhmmss_from;
    q += ".0";
    q += " && &time <= 1970-01-01+";
    q += hhmmss_to;
    q += ".0";
    return lookup(q);
  }

  // Our unit-under-test.
  meta_index meta_idx;

  // Partition IDs.
  std::vector<uuid> ids;
};

} // namespace <anonymous>

FIXTURE_SCOPE(meta_index_tests, fixture)

TEST(attribute extractor - &time) {
  MESSAGE("check whether point queries return correct slices");
  CHECK_EQUAL(attr_time_query("00:00:00"), slice(0));
  CHECK_EQUAL(attr_time_query("00:00:24"), slice(0));
  CHECK_EQUAL(attr_time_query("00:00:25"), slice(1));
  CHECK_EQUAL(attr_time_query("00:00:49"), slice(1));
  CHECK_EQUAL(attr_time_query("00:00:50"), slice(2));
  CHECK_EQUAL(attr_time_query("00:01:14"), slice(2));
  CHECK_EQUAL(attr_time_query("00:01:15"), slice(3));
  CHECK_EQUAL(attr_time_query("00:01:39"), slice(3));
  CHECK_EQUAL(attr_time_query("00:01:40"), empty());
  MESSAGE("check whether time-range queries return correct slices");
  CHECK_EQUAL(attr_time_query("00:00:01", "00:00:10"), slice(0));
  CHECK_EQUAL(attr_time_query("00:00:10", "00:00:30"), slice(0, 2));
}

TEST(attribute extractor - &type) {
  auto foo = std::vector<uuid>{ids[0], ids[2]};
  auto foobar = std::vector<uuid>{ids[1], ids[3]};
  CHECK_EQUAL(lookup("&type == \"foo\""), foo);
  CHECK_EQUAL(lookup("&type == \"bar\""), empty());
  CHECK_EQUAL(lookup("&type != \"foo\""), foobar);
  CHECK_EQUAL(lookup("&type ~ /f.o/"), foo);
  CHECK_EQUAL(lookup("&type ~ /f.*/"), ids);
  CHECK_EQUAL(lookup("&type ~ /x/"), empty());
  CHECK_EQUAL(lookup("&type !~ /x/"), ids);
}

FIXTURE_SCOPE_END()

FIXTURE_SCOPE(metaidx_serialization_tests, fixtures::deterministic_actor_system)

TEST(serialization) {
  meta_index meta_idx;
  auto part = mock_partition{"foo", uuid::random(), 42};
  meta_idx.add(part.id, *part.slice);
  CHECK_ROUNDTRIP(meta_idx);
}

TEST(meta index with boolean synopsis) {
  MESSAGE("generate slice data and add it to the meta index");
  meta_index meta_idx;
  auto layout = record_type{{"x", boolean_type{}}};
  auto builder = default_table_slice_builder::make(layout);
  CHECK(builder->add(make_data_view(true)));
  auto slice = builder->finish();
  REQUIRE(slice != nullptr);
  auto id1 = uuid::random();
  meta_idx.add(id1, *slice);
  CHECK(builder->add(make_data_view(false)));
  slice = builder->finish();
  REQUIRE(slice != nullptr);
  auto id2 = uuid::random();
  meta_idx.add(id2, *slice);
  CHECK(builder->add(make_data_view(caf::none_t{})));
  slice = builder->finish();
  REQUIRE(slice != nullptr);
  auto id3 = uuid::random();
  meta_idx.add(id3, *slice);
  MESSAGE("test custom synopsis");
  auto lookup = [&](std::string_view expr) {
    return meta_idx.lookup(unbox(to<expression>(expr)));
  };
  auto expected1 = std::vector<uuid>{id1};
  auto expected2 = std::vector<uuid>{id2};
  // Check by field name field.
  CHECK_EQUAL(lookup("x == T"), expected1);
  CHECK_EQUAL(lookup("x != F"), expected1);
  CHECK_EQUAL(lookup("x == F"), expected2);
  CHECK_EQUAL(lookup("x != T"), expected2);
  // Same as above, different extractor.
  CHECK_EQUAL(lookup(":bool == T"), expected1);
  CHECK_EQUAL(lookup(":bool != F"), expected1);
  CHECK_EQUAL(lookup(":bool == F"), expected2);
  CHECK_EQUAL(lookup(":bool != T"), expected2);
  auto all = std::vector<uuid>{id1, id2, id3};
  std::sort(all.begin(), all.end());
  // Invalid schema: y does not a valid field
  CHECK_EQUAL(lookup("y == T"), all);
  CHECK_EQUAL(lookup("y != F"), all);
  CHECK_EQUAL(lookup("y == F"), all);
  CHECK_EQUAL(lookup("y != T"), all);
  MESSAGE("perform serialization");
  CHECK_ROUNDTRIP(meta_idx);
}

TEST(option setting and retrieval) {
  meta_index meta_idx;
  auto& opts = meta_idx.factory_options();
  put(opts, "foo", 42);
  auto x = caf::get_if<caf::config_value::integer>(&opts["foo"]);
  CHECK_EQUAL(unbox(x), 42);
}

FIXTURE_SCOPE_END()
<|endoftext|>


<commit_before>/******************************************************************************
 *                    _   _____   __________                                  *
 *                   | | / / _ | / __/_  __/     Visibility                   *
 *                   | |/ / __ |_\ \  / /          Across                     *
 *                   |___/_/ |_/___/ /_/       Space and Time                 *
 *                                                                            *
 * This file is part of VAST. It is subject to the license terms in the       *
 * LICENSE file found in the top-level directory of this distribution and at  *
 * http://vast.io/license. No part of VAST, including this file, may be       *
 * copied, modified, propagated, or distributed except according to the terms *
 * contained in the LICENSE file.                                             *
 ******************************************************************************/

#pragma once

#include "vast/concept/hashable/uhash.hpp"
#include "vast/concept/hashable/xxhash.hpp"
#include "vast/data.hpp"
#include "vast/detail/assert.hpp"
#include "vast/detail/steady_map.hpp"
#include "vast/detail/type_traits.hpp"
#include "vast/value_index.hpp"
#include "vast/view.hpp"

#include <caf/deserializer.hpp>
#include <caf/expected.hpp>
#include <caf/optional.hpp>
#include <caf/serializer.hpp>

#include <algorithm>
#include <array>
#include <cstring>
#include <string>
#include <type_traits>
#include <unordered_set>
#include <vector>

namespace vast {

/// An index that only supports equality lookup by hashing its data. The
/// hash_index computes a digest of the input data and concatenates all digests
/// in a sequence. Optionally, it chops off the values after a fixed number of
/// bytes for a more space-efficient representation, at the cost of more false
/// positives. A separate "satellite structure" keeps track of hash collision
/// to make the index exact. The additional state to build this satellite
/// structure only exists during the construction of the index. Upon
/// descruction, this extra state ceases to exist and it will not be possible
/// to append further values when deserializing an existing index.
template <size_t Bytes>
class hash_index : public value_index {
  static_assert(Bytes > 0);
  static_assert(Bytes <= 8);

  /// The maximum number of hash rounds to try to find a new digest.
  static constexpr size_t max_hash_rounds = 32;

public:
  using hasher_type = xxhash64;
  using digest_type = std::array<byte, Bytes>;

  static_assert(sizeof(hasher_type::result_type) >= sizeof(digest_type),
                "number of chosen bytes exceeds underlying digest size");

  /// Computes a chopped digest from arbitrary data.
  /// @param x The data to hash.
  /// @param seed The seed to use during the hash.
  /// @returns The chopped digest.
  static digest_type hash(data_view x, size_t seed = 0) {
    digest_type result;
    auto digest = uhash<hasher_type>{seed}(x);
    std::memcpy(result.data(), &digest, Bytes);
    return result;
  }

  /// Constructs a hash index for a particular type and digest cutoff.
  /// @param t The type associated with this index.
  /// @param digest_bytes The number of bytes to keep of a hash digest.
  explicit hash_index(vast::type t) : value_index{std::move(t)} {
  }

  caf::error serialize(caf::serializer& sink) const override {
    // Prune unneeded seeds.
    decltype(seeds_) non_null_seeds;
    for (auto& [k, v] : seeds_)
      if (v > 0)
        non_null_seeds.emplace(k, v);
    return caf::error::eval([&] { return value_index::serialize(sink); },
                            [&] { return sink(digests_, non_null_seeds); });
  }

  caf::error deserialize(caf::deserializer& source) override {
    return caf::error::eval([&] { return value_index::deserialize(source); },
                            [&] { return source(digests_, seeds_); });
  }

private:
  struct key {
    friend bool operator==(key x, digest_type y) {
      return std::memcmp(x.bytes.data(), y.data(), y.size()) == 0;
    }

    friend bool operator!=(key x, digest_type y) {
      return !(x == y);
    }

    friend bool operator==(digest_type x, key y) {
      return y == x;
    }

    friend bool operator!=(digest_type x, key y) {
      return !(x == y);
    }

    friend bool operator==(key x, key y) {
      return x == y.bytes;
    }

    friend bool operator!=(key x, key y) {
      return !(x == y);
    }

    digest_type bytes;
  };

  struct key_hasher {
    auto operator()(const key& x) const {
      auto result = uint64_t{0};
      std::memcpy(&result, x.bytes.data(), x.bytes.size());
      return result;
    }
  };

  // TODO: remove this function as of C++20.
  auto find_seed(data_view x) const {
    auto pred = [&](const auto& xs) { return is_equal(xs.first, x); };
    auto& xs = as_vector(seeds_);
    return std::find_if(xs.begin(), xs.end(), pred);
  }

  // Retrieves the unique digest for a given input or generates a new one.
  caf::optional<key> make_digest(data_view x) {
    size_t i = 0;
    do {
      // Compute a hash digest.
      auto digest = hash(x, i);
      auto k = key{digest};
      // If we have never seen this digest before, we're adding it to the list
      // of seen digests and are done.
      if (unique_digests_.count(k) == 0) {
        auto materialized = materialize(x);
        VAST_ASSERT(seeds_.count(materialized) == 0);
        seeds_.emplace(std::move(materialized), i);
        unique_digests_.insert(k);
        return k;
      };
      // If we have seen the digest, check whether we also have a known
      // preimage.
      if (auto it = find_seed(x); it != seeds_.end())
        return key{hash(x, it->second)};
      // Try the next hash function.
      ++i;
    } while (i < max_hash_rounds);
    return caf::none;
  }

  /// Locates the digest for a given input.
  key find_digest(data_view x) const {
    auto i = find_seed(x);
    return key{i != seeds_.end() ? hash(x, i->second) : hash(x, 0)};
  }

  bool append_impl(data_view x, id) override {
    // After we deserialize the index, we can no longer append data.
    if (immutable())
      return false;
    auto digest = make_digest(x);
    if (!digest)
      return false;
    digests_.push_back(digest->bytes);
    return true;
  }

  caf::expected<ids>
  lookup_impl(relational_operator op, data_view x) const override {
    VAST_ASSERT(rank(this->mask()) == digests_.size());
    // Implementation of the one-pass search algorithm that computes the
    // resulting ID set. The predicate depends on the operator and RHS.
    auto scan = [&](auto predicate) -> ids {
      ewah_bitmap result;
      auto rng = select(this->mask());
      if (rng.done())
        return result;
      for (size_t i = 0, last_match = 0; i < digests_.size(); ++i) {
        if (predicate(digests_[i])) {
          auto digests_since_last_match = i - last_match;
          if (digests_since_last_match > 0)
            rng.next(digests_since_last_match);
          result.append_bits(false, rng.get() - result.size());
          result.append_bit(true);
          last_match = i;
        }
      }
      return result;
    };
    if (op == equal || op == not_equal) {
      auto k = find_digest(x);
      auto eq = [=](const digest_type& digest) { return k == digest; };
      auto ne = [=](const digest_type& digest) { return k != digest; };
      return op == equal ? scan(eq) : scan(ne);
    }
    if (op == in || op == not_in) {
      // Ensure that the RHS is a list of strings.
      auto keys = caf::visit(
        detail::overload([&](auto xs) -> caf::expected<std::vector<key>> {
          using view_type = decltype(xs);
          if constexpr (detail::is_any_v<view_type, view<set>, view<vector>>) {
            std::vector<key> result;
            result.reserve(xs.size());
            for (auto x : xs)
              result.emplace_back(find_digest(x));
            return result;
          } else {
            return make_error(ec::type_clash, "expected set or vector on RHS",
                              materialize(x));
          }
        }),
        x);
      if (!keys)
        return keys.error();
      // We're good to go with: create the set predicates an run the scan.
      auto in_pred = [&](const digest_type& digest) {
        auto cmp = [=](auto& k) { return k == digest; };
        return std::any_of(keys->begin(), keys->end(), cmp);
      };
      auto not_in_pred = [&](const digest_type& digest) {
        auto cmp = [=](auto& k) { return k == digest; };
        return std::none_of(keys->begin(), keys->end(), cmp);
      };
      return op == in ? scan(in_pred) : scan(not_in_pred);
    }
    return make_error(ec::unsupported_operator, op);
  }

  bool immutable() const {
    return unique_digests_.empty() && !digests_.empty();
  }

  std::vector<digest_type> digests_;
  std::unordered_set<key, key_hasher> unique_digests_;

  // TODO: Once we can use C++20 and have a standard library that implements
  // key equivalcne properly, we can switch to std::unordered_map.
  detail::steady_map<data, size_t> seeds_;
};

} // namespace vast
<commit_msg>Replace obsolete do-while loop with for loop<commit_after>/******************************************************************************
 *                    _   _____   __________                                  *
 *                   | | / / _ | / __/_  __/     Visibility                   *
 *                   | |/ / __ |_\ \  / /          Across                     *
 *                   |___/_/ |_/___/ /_/       Space and Time                 *
 *                                                                            *
 * This file is part of VAST. It is subject to the license terms in the       *
 * LICENSE file found in the top-level directory of this distribution and at  *
 * http://vast.io/license. No part of VAST, including this file, may be       *
 * copied, modified, propagated, or distributed except according to the terms *
 * contained in the LICENSE file.                                             *
 ******************************************************************************/

#pragma once

#include "vast/concept/hashable/uhash.hpp"
#include "vast/concept/hashable/xxhash.hpp"
#include "vast/data.hpp"
#include "vast/detail/assert.hpp"
#include "vast/detail/steady_map.hpp"
#include "vast/detail/type_traits.hpp"
#include "vast/value_index.hpp"
#include "vast/view.hpp"

#include <caf/deserializer.hpp>
#include <caf/expected.hpp>
#include <caf/optional.hpp>
#include <caf/serializer.hpp>

#include <algorithm>
#include <array>
#include <cstring>
#include <string>
#include <type_traits>
#include <unordered_set>
#include <vector>

namespace vast {

/// An index that only supports equality lookup by hashing its data. The
/// hash_index computes a digest of the input data and concatenates all digests
/// in a sequence. Optionally, it chops off the values after a fixed number of
/// bytes for a more space-efficient representation, at the cost of more false
/// positives. A separate "satellite structure" keeps track of hash collision
/// to make the index exact. The additional state to build this satellite
/// structure only exists during the construction of the index. Upon
/// descruction, this extra state ceases to exist and it will not be possible
/// to append further values when deserializing an existing index.
template <size_t Bytes>
class hash_index : public value_index {
  static_assert(Bytes > 0);
  static_assert(Bytes <= 8);

  /// The maximum number of hash rounds to try to find a new digest.
  static constexpr size_t max_hash_rounds = 32;

public:
  using hasher_type = xxhash64;
  using digest_type = std::array<byte, Bytes>;

  static_assert(sizeof(hasher_type::result_type) >= sizeof(digest_type),
                "number of chosen bytes exceeds underlying digest size");

  /// Computes a chopped digest from arbitrary data.
  /// @param x The data to hash.
  /// @param seed The seed to use during the hash.
  /// @returns The chopped digest.
  static digest_type hash(data_view x, size_t seed = 0) {
    digest_type result;
    auto digest = uhash<hasher_type>{seed}(x);
    std::memcpy(result.data(), &digest, Bytes);
    return result;
  }

  /// Constructs a hash index for a particular type and digest cutoff.
  /// @param t The type associated with this index.
  /// @param digest_bytes The number of bytes to keep of a hash digest.
  explicit hash_index(vast::type t) : value_index{std::move(t)} {
  }

  caf::error serialize(caf::serializer& sink) const override {
    // Prune unneeded seeds.
    decltype(seeds_) non_null_seeds;
    for (auto& [k, v] : seeds_)
      if (v > 0)
        non_null_seeds.emplace(k, v);
    return caf::error::eval([&] { return value_index::serialize(sink); },
                            [&] { return sink(digests_, non_null_seeds); });
  }

  caf::error deserialize(caf::deserializer& source) override {
    return caf::error::eval([&] { return value_index::deserialize(source); },
                            [&] { return source(digests_, seeds_); });
  }

private:
  struct key {
    friend bool operator==(key x, digest_type y) {
      return std::memcmp(x.bytes.data(), y.data(), y.size()) == 0;
    }

    friend bool operator!=(key x, digest_type y) {
      return !(x == y);
    }

    friend bool operator==(digest_type x, key y) {
      return y == x;
    }

    friend bool operator!=(digest_type x, key y) {
      return !(x == y);
    }

    friend bool operator==(key x, key y) {
      return x == y.bytes;
    }

    friend bool operator!=(key x, key y) {
      return !(x == y);
    }

    digest_type bytes;
  };

  struct key_hasher {
    auto operator()(const key& x) const {
      auto result = uint64_t{0};
      std::memcpy(&result, x.bytes.data(), x.bytes.size());
      return result;
    }
  };

  // TODO: remove this function as of C++20.
  auto find_seed(data_view x) const {
    auto pred = [&](const auto& xs) { return is_equal(xs.first, x); };
    auto& xs = as_vector(seeds_);
    return std::find_if(xs.begin(), xs.end(), pred);
  }

  // Retrieves the unique digest for a given input or generates a new one.
  caf::optional<key> make_digest(data_view x) {
    for (size_t i = 0; i < max_hash_rounds; ++i) {
      // Compute a hash digest.
      auto digest = hash(x, i);
      auto k = key{digest};
      // If we have never seen this digest before, we're adding it to the list
      // of seen digests and are done.
      if (unique_digests_.count(k) == 0) {
        auto materialized = materialize(x);
        VAST_ASSERT(seeds_.count(materialized) == 0);
        seeds_.emplace(std::move(materialized), i);
        unique_digests_.insert(k);
        return k;
      };
      // If we have seen the digest, check whether we also have a known
      // preimage.
      if (auto it = find_seed(x); it != seeds_.end())
        return key{hash(x, it->second)};
    }
    return caf::none;
  }

  /// Locates the digest for a given input.
  key find_digest(data_view x) const {
    auto i = find_seed(x);
    return key{i != seeds_.end() ? hash(x, i->second) : hash(x, 0)};
  }

  bool append_impl(data_view x, id) override {
    // After we deserialize the index, we can no longer append data.
    if (immutable())
      return false;
    auto digest = make_digest(x);
    if (!digest)
      return false;
    digests_.push_back(digest->bytes);
    return true;
  }

  caf::expected<ids>
  lookup_impl(relational_operator op, data_view x) const override {
    VAST_ASSERT(rank(this->mask()) == digests_.size());
    // Implementation of the one-pass search algorithm that computes the
    // resulting ID set. The predicate depends on the operator and RHS.
    auto scan = [&](auto predicate) -> ids {
      ewah_bitmap result;
      auto rng = select(this->mask());
      if (rng.done())
        return result;
      for (size_t i = 0, last_match = 0; i < digests_.size(); ++i) {
        if (predicate(digests_[i])) {
          auto digests_since_last_match = i - last_match;
          if (digests_since_last_match > 0)
            rng.next(digests_since_last_match);
          result.append_bits(false, rng.get() - result.size());
          result.append_bit(true);
          last_match = i;
        }
      }
      return result;
    };
    if (op == equal || op == not_equal) {
      auto k = find_digest(x);
      auto eq = [=](const digest_type& digest) { return k == digest; };
      auto ne = [=](const digest_type& digest) { return k != digest; };
      return op == equal ? scan(eq) : scan(ne);
    }
    if (op == in || op == not_in) {
      // Ensure that the RHS is a list of strings.
      auto keys = caf::visit(
        detail::overload([&](auto xs) -> caf::expected<std::vector<key>> {
          using view_type = decltype(xs);
          if constexpr (detail::is_any_v<view_type, view<set>, view<vector>>) {
            std::vector<key> result;
            result.reserve(xs.size());
            for (auto x : xs)
              result.emplace_back(find_digest(x));
            return result;
          } else {
            return make_error(ec::type_clash, "expected set or vector on RHS",
                              materialize(x));
          }
        }),
        x);
      if (!keys)
        return keys.error();
      // We're good to go with: create the set predicates an run the scan.
      auto in_pred = [&](const digest_type& digest) {
        auto cmp = [=](auto& k) { return k == digest; };
        return std::any_of(keys->begin(), keys->end(), cmp);
      };
      auto not_in_pred = [&](const digest_type& digest) {
        auto cmp = [=](auto& k) { return k == digest; };
        return std::none_of(keys->begin(), keys->end(), cmp);
      };
      return op == in ? scan(in_pred) : scan(not_in_pred);
    }
    return make_error(ec::unsupported_operator, op);
  }

  bool immutable() const {
    return unique_digests_.empty() && !digests_.empty();
  }

  std::vector<digest_type> digests_;
  std::unordered_set<key, key_hasher> unique_digests_;

  // TODO: Once we can use C++20 and have a standard library that implements
  // key equivalcne properly, we can switch to std::unordered_map.
  detail::steady_map<data, size_t> seeds_;
};

} // namespace vast
<|endoftext|>


<commit_before>// TRENTO: Reduced Thickness Event-by-event Nuclear Topology
// Copyright 2015 Jonah E. Bernhard, J. Scott Moreland
// MIT License

#include "event.h"

#include <iomanip>
#include <iostream>

#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/program_options/variables_map.hpp>

namespace trento {

Event::Event(int grid_steps)
    : TA(boost::extents[grid_steps][grid_steps]),
      TB(boost::extents[grid_steps][grid_steps]),
      TR(boost::extents[grid_steps][grid_steps])
{}

namespace {

std::ostream& operator<<(std::ostream& os, const Event& event) {
  using std::fixed;
  using std::setprecision;
  using std::setw;
  using std::scientific;

  os << setprecision(10)       << event.num
     << setw(14) << fixed      << event.impact_param
     << setw(5)                << event.npart
     << setw(18) << scientific << event.multiplicity
     << fixed;

  for (const auto& ecc : event.eccentricity)
    os << setw(14)             << ecc.second;

  return os << '\n';
}

void write_text_file(const fs::path& path, const Event& event) {
  fs::ofstream ofs{path};
  ofs << std::setprecision(10) << std::fixed
      << "# event "   << event.num          << '\n'
      << "# b     = " << event.impact_param << '\n'
      << "# npart = " << event.npart        << '\n'
      << "# mult  = " << event.multiplicity << '\n';

  for (const auto& ecc : event.eccentricity)
    ofs << "# e" << ecc.first << "    = " << ecc.second << '\n';

  // XXX: improve this?
  ofs << std::scientific;
  for (const auto& row : event.TR) {
    for (const auto& elem : row) {
      ofs << elem << ' ';
    }
    ofs << '\n';
  }
}

}  // unnamed namespace

OutputFunctionVector create_output_functions(const VarMap& var_map) {
  OutputFunctionVector output_functions;

  auto num_events = var_map["number-events"].as<int>();
  auto event_num_width = static_cast<int>(std::ceil(std::log10(num_events)));

  // Write to stdout unless the quiet option was specified.
  if (!var_map["quiet"].as<bool>()) {
    output_functions.emplace_back(
      [event_num_width](const Event& event) {
        std::cout << std::setw(event_num_width) << event;
      }
    );
  }

  if (var_map.count("output")) {
    auto output_path = var_map["output"].as<fs::path>();
    if (output_path.has_extension() && (
          output_path.extension() == ".hdf5" ||
          output_path.extension() == ".hd5"  ||
          output_path.extension() == ".h5"
          )) {
      throw std::runtime_error{"HDF5 not implemented yet"};  // TODO
    } else {
      if (fs::exists(output_path)) {
        if (!fs::is_empty(output_path)) {
          throw std::runtime_error{"output directory '" + output_path.string() +
                                   "' must be empty"};
        }
      } else {
        fs::create_directories(output_path);
      }
      output_functions.emplace_back(
        [output_path, event_num_width](const Event& event) {
          std::ostringstream padded_fname{};
          padded_fname << std::setw(event_num_width) << std::setfill('0')
                       << event.num << ".dat";
          write_text_file(output_path / padded_fname.str(), event);
        }
      );
    }
  }

  return output_functions;
}

}  // namespace trento
<commit_msg>Modify text output numerical format.<commit_after>// TRENTO: Reduced Thickness Event-by-event Nuclear Topology
// Copyright 2015 Jonah E. Bernhard, J. Scott Moreland
// MIT License

#include "event.h"

#include <iomanip>
#include <iostream>

#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/program_options/variables_map.hpp>

namespace trento {

Event::Event(int grid_steps)
    : TA(boost::extents[grid_steps][grid_steps]),
      TB(boost::extents[grid_steps][grid_steps]),
      TR(boost::extents[grid_steps][grid_steps])
{}

namespace {

std::ostream& operator<<(std::ostream& os, const Event& event) {
  using std::fixed;
  using std::setprecision;
  using std::setw;
  using std::scientific;

  os << setprecision(10)       << event.num
     << setw(14) << fixed      << event.impact_param
     << setw(5)                << event.npart
     << setw(18) << scientific << event.multiplicity
     << fixed;

  for (const auto& ecc : event.eccentricity)
    os << setw(14)             << ecc.second;

  return os << '\n';
}

void write_text_file(const fs::path& path, const Event& event) {
  fs::ofstream ofs{path};
  ofs << std::setprecision(10)
      << "# event "   << event.num          << '\n'
      << "# b     = " << event.impact_param << '\n'
      << "# npart = " << event.npart        << '\n'
      << "# mult  = " << event.multiplicity << '\n';

  for (const auto& ecc : event.eccentricity)
    ofs << "# e" << ecc.first << "    = " << ecc.second << '\n';

  // XXX: improve this?
  for (const auto& row : event.TR) {
    for (const auto& elem : row) {
      ofs << elem << ' ';
    }
    ofs << '\n';
  }
}

}  // unnamed namespace

OutputFunctionVector create_output_functions(const VarMap& var_map) {
  OutputFunctionVector output_functions;

  auto num_events = var_map["number-events"].as<int>();
  auto event_num_width = static_cast<int>(std::ceil(std::log10(num_events)));

  // Write to stdout unless the quiet option was specified.
  if (!var_map["quiet"].as<bool>()) {
    output_functions.emplace_back(
      [event_num_width](const Event& event) {
        std::cout << std::setw(event_num_width) << event;
      }
    );
  }

  if (var_map.count("output")) {
    auto output_path = var_map["output"].as<fs::path>();
    if (output_path.has_extension() && (
          output_path.extension() == ".hdf5" ||
          output_path.extension() == ".hd5"  ||
          output_path.extension() == ".h5"
          )) {
      throw std::runtime_error{"HDF5 not implemented yet"};  // TODO
    } else {
      if (fs::exists(output_path)) {
        if (!fs::is_empty(output_path)) {
          throw std::runtime_error{"output directory '" + output_path.string() +
                                   "' must be empty"};
        }
      } else {
        fs::create_directories(output_path);
      }
      output_functions.emplace_back(
        [output_path, event_num_width](const Event& event) {
          std::ostringstream padded_fname{};
          padded_fname << std::setw(event_num_width) << std::setfill('0')
                       << event.num << ".dat";
          write_text_file(output_path / padded_fname.str(), event);
        }
      );
    }
  }

  return output_functions;
}

}  // namespace trento
<|endoftext|>


<commit_before>// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/fml/synchronization/count_down_latch.h"

#include <chrono>
#include <thread>

#include "flutter/fml/build_config.h"
#include "flutter/fml/thread.h"
#include "flutter/testing/testing.h"

namespace fml {

TEST(CountDownLatchTest, CanWaitOnZero) {
  CountDownLatch latch(0);
  latch.Wait();
}

#if OS_FUCHSIA
#define CanWait DISABLED_CanWait
#else
#define CanWait CanWait
#endif
TEST(CountDownLatchTest, CanWait) {
  fml::Thread thread("test_thread");
  const size_t count = 100;
  size_t current_count = 0;
  CountDownLatch latch(count);
  auto decrement_latch_on_thread = [runner = thread.GetTaskRunner(), &latch,
                                    &current_count]() {
    runner->PostTask([&latch, &current_count]() {
      std::this_thread::sleep_for(std::chrono::microseconds(100));
      current_count++;
      latch.CountDown();
    });
  };
  for (size_t i = 0; i < count; ++i) {
    decrement_latch_on_thread();
  }
  latch.Wait();
  ASSERT_EQ(current_count, count);
}

}  // namespace fml
<commit_msg>Re-enable Fuchsia CanWait test (#31095)<commit_after>// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/fml/synchronization/count_down_latch.h"

#include <chrono>
#include <thread>

#include "flutter/fml/build_config.h"
#include "flutter/fml/thread.h"
#include "flutter/testing/testing.h"

namespace fml {

TEST(CountDownLatchTest, CanWaitOnZero) {
  CountDownLatch latch(0);
  latch.Wait();
}

TEST(CountDownLatchTest, CanWait) {
  fml::Thread thread("test_thread");
  const size_t count = 100;
  size_t current_count = 0;
  CountDownLatch latch(count);
  auto decrement_latch_on_thread = [runner = thread.GetTaskRunner(), &latch,
                                    &current_count]() {
    runner->PostTask([&latch, &current_count]() {
      std::this_thread::sleep_for(std::chrono::microseconds(100));
      current_count++;
      latch.CountDown();
    });
  };
  for (size_t i = 0; i < count; ++i) {
    decrement_latch_on_thread();
  }
  latch.Wait();
  ASSERT_EQ(current_count, count);
}

}  // namespace fml
<|endoftext|>


<commit_before>// -*- mode: c++; coding: utf-8 -*-
/*! @file exact.cpp
    @brief Inplementation of ExactModel class
*/
#include "exact.hpp"
#include "util.hpp"

#include <functional>

#include <json.hpp>

#include <wtl/debug.hpp>
#include <wtl/exception.hpp>
#include <wtl/iostr.hpp>
#include <wtl/zfstream.hpp>
#include <wtl/algorithm.hpp>
#include <wtl/numeric.hpp>
#include <wtl/math.hpp>
#include <wtl/os.hpp>

namespace likeligrid {

const std::vector<double> ExactModel::STEPS_ = {0.4, 0.2, 0.1, 0.05, 0.02, 0.01};
const std::vector<size_t> ExactModel::BREAKS_ = {5, 5, 5, 5, 6, 5};
bool ExactModel::SIGINT_RAISED_ = false;

ExactModel::ExactModel(const std::string& infile, const size_t max_sites):
    ExactModel(wtl::izfstream(infile), max_sites) {HERE;}

ExactModel::ExactModel(std::istream&& ist, const size_t max_sites):
    ExactModel(ist, max_sites) {HERE;}

ExactModel::ExactModel(std::istream& ist, const size_t max_sites) {HERE;
    nlohmann::json jso;
    ist >> jso;
    names_ = jso["pathway"].get<std::vector<std::string>>();
    const size_t npath = names_.size();
    annot_.reserve(npath);
    for (const std::string& s: jso["annotation"]) {
        annot_.emplace_back(s);
    }
    std::cerr << "annot_: " << annot_ << std::endl;

    const size_t nsam = jso["sample"].size();
    std::vector<bits_t> all_genotypes;
    all_genotypes.reserve(nsam);
    for (const std::string& s: jso["sample"]) {
        all_genotypes.emplace_back(s);
    }

    genot_.reserve(nsam);
    const size_t ngene = all_genotypes.at(0).size();
    nsam_with_s_.assign(ngene + 1, 0);  // at most
    std::valarray<double> s_gene(ngene);
    for (const auto& bits: all_genotypes) {
        const size_t s = bits.count();
        ++nsam_with_s_[s];
        if (s > max_sites) continue;
        genot_.push_back(bits);
        for (bits_t::size_type j=0; j<bits.size(); ++j) {
            if (bits[j]) ++s_gene[j];
        }
    }
    wtl::rstrip(&nsam_with_s_);
    std::cerr << "Original N_s: " << nsam_with_s_ << std::endl;
    if (max_sites + 1 < nsam_with_s_.size()) {
        nsam_with_s_.resize(max_sites + 1);
        std::cerr << "Using N_s: " << nsam_with_s_ << std::endl;
    } else {
        std::cerr << "Note: -s is too large" << std::endl;
    }
    w_gene_ = s_gene / s_gene.sum();
    std::cerr << "s_gene : " << s_gene << std::endl;
    std::cerr << "w_gene_: " << w_gene_ << std::endl;

    mle_params_.resize(annot_.size());
    mle_params_ = 1.2;
}

void ExactModel::run_fout() {HERE;
    const std::string outfile = init_meta();
    std::cerr << "mle_params_: " << mle_params_ << std::endl;
    if (outfile.empty()) return;
    const auto axes = make_vicinity(mle_params_, BREAKS_.at(stage_), 2.0 * STEPS_.at(stage_));
    for (size_t j=0; j<names_.size(); ++j) {
        std::cerr << names_[j] << ": " << axes[j] << std::endl;
    }
    {
        wtl::ozfstream fout(outfile, std::ios::out | std::ios::app);
        std::cerr << "Writing: " << outfile << std::endl;
        run_impl(fout, wtl::itertools::product(axes));
    }
    run_fout();
}

void ExactModel::run_cout() {HERE;
    std::cerr << "mle_params_: " << mle_params_ << std::endl;
    if (stage_ >= STEPS_.size()) return;
    const auto axes = make_vicinity(mle_params_, BREAKS_.at(stage_), 2.0 * STEPS_.at(stage_));
    for (size_t j=0; j<names_.size(); ++j) {
        std::cerr << names_[j] << ": " << axes[j] << std::endl;
    }
    {
        std::stringstream sst;
        run_impl(sst, wtl::itertools::product(axes));
        std::cout << sst.str();
        read_results(sst);
    }
    ++stage_;
    run_cout();
}

void ExactModel::search_limits() const {HERE;
    {
        const std::vector<std::valarray<double>> axes(names_.size(), wtl::lin_spaced(200, 2.0, 0.01));
        wtl::ozfstream fout("uniaxis.tsv.gz");
        run_impl(fout, wtl::itertools::uniaxis(axes, mle_params_));
    }
    for (const auto& p: find_intersections(*this)) {
        std::cerr << p.first << ": " << p.second << std::endl;
        const auto axes = make_vicinity(p.second, 5, 0.02);
        wtl::ozfstream fout("limit-" + p.first + ".tsv.gz");
        //TODO: if exists
        run_impl(fout, wtl::itertools::product(axes));
    }
}

void ExactModel::run_impl(std::ostream& ost, wtl::itertools::Generator<std::valarray<double>>&& gen) const {HERE;
    std::cerr << skip_ << " to " << gen.max_count() << std::endl;
    if (skip_ == 0) {
        ost << "##max_count=" << gen.max_count() << "\n";
        ost << "##max_sites=" << nsam_with_s_.size() - 1 << "\n";
        ost << "##step=" << STEPS_.at(stage_) << "\n";
        ost << "loglik\t" << wtl::join(names_, "\t") << "\n";
    }
    auto buffer = wtl::make_oss();
    for (const auto& th_path: gen(skip_)) {
        buffer << calc_loglik(th_path) << "\t"
               << wtl::str_join(th_path, "\t") << "\n";
        if (gen.count() % 100 == 0) {  // snapshot for long run
            std::cerr << "*" << std::flush;
            ost << buffer.str();
            ost.flush();
            buffer.str("");
        }
        if (SIGINT_RAISED_) {throw wtl::KeyboardInterrupt();}
    }
    std::cerr << "-\n";
    ost << buffer.str();
}

inline std::vector<bits_t> breakdown(const bits_t& bits) {
    std::vector<bits_t> singles;
    singles.reserve(bits.count());
    const bits_t one(bits.size(), 1);
    for (bits_t::size_type i=0; i<bits.size(); ++i) {
        if (bits[i]) singles.emplace_back(one << i);
    }
    return singles;
}

inline double slice_prod(const std::valarray<double>& coefs, const bits_t& bits) {
    double p = 1.0;
    for (bits_t::size_type i=0; i<bits.size(); ++i) {
        if (bits[i]) p *= coefs[i];
    }
    return p;
}

class Denoms {
  public:
    Denoms() = delete;
    Denoms(const std::valarray<double>& w_gene,
        const std::valarray<double>& th_path,
        const std::vector<bits_t>& annot,
        const size_t max_sites):
        w_gene_(w_gene),
        th_path_(th_path),
        annot_(annot),
        max_sites_(max_sites),
        denoms_(max_sites + 1)
    {
        const size_t ngene = w_gene.size();
        effects_.reserve(ngene);
        for (bits_t::size_type j=0; j<ngene; ++j) {
            effects_.emplace_back(translate(j));
        }
        // std::cerr << "effects_: " << effects_ << std::endl;
        mutate(bits_t(ngene, 0), bits_t(annot.size(), 0), 1.0);
        // std::cerr << "denoms_: " << denoms_ << std::endl;
    }
    const std::valarray<double> log() const {return std::log(denoms_);}

    double lnp_sample(const bits_t& genotype) const {
        double p = 0.0;
        const double p_basic = slice_prod(w_gene_, genotype);
        auto mut_route = breakdown(genotype);
        do {
            p += p_basic * discount(mut_route);
        } while (std::next_permutation(mut_route.begin(), mut_route.end()));
        return std::log(p);
    }

  private:
    void mutate(const bits_t& genotype, const bits_t& pathtype, const double anc_p) {
        const auto s = genotype.count() + 1;
        for (bits_t::size_type j=0; j<genotype.size(); ++j) {
            if (genotype[j]) continue;
            const bits_t& mut_path = effects_[j];
            double p = anc_p;
            p *= w_gene_[j];
            p *= discount_if_subset(pathtype, mut_path);
            denoms_[s] += p;
            if (s < max_sites_) {
                mutate(bits_t(genotype).set(j), pathtype | mut_path, p);
            }
        }
    }

    double discount_if_subset(const bits_t& pathtype, const bits_t& mut_path) const {
        double p = 1.0;
        for (bits_t::size_type i=0; i<mut_path.size(); ++i) {
            if (mut_path[i]) {
                if (pathtype[i]) {
                    p *= th_path_[i];
                } else {
                    return 1.0;
                }
            }
        }
        return p;
    }

    double discount(const std::vector<bits_t>& mut_route) const {
        double p = 1.0;
        const auto npath = annot_.size();
        bits_t pathtype(npath, 0);
        for (const auto& mut_gene: mut_route) {
            const auto& mut_path = effects_[mut_gene.find_first()];
            p *= discount_if_subset(pathtype, mut_path);
            pathtype |= mut_path;
        }
        return p;
    }

    bits_t translate(const bits_t::size_type& mut_idx) const {
        bits_t mut_path(annot_.size(), 0);
        for (size_t j=0; j<annot_.size(); ++j) {
            mut_path.set(j, annot_[j][mut_idx]);
        }
        return mut_path;
    }
    const std::valarray<double>& w_gene_;
    const std::valarray<double>& th_path_;
    const std::vector<bits_t>& annot_;
    const size_t max_sites_;
    std::valarray<double> denoms_;
    std::vector<bits_t> effects_;
};

double ExactModel::calc_loglik(const std::valarray<double>& th_path) const {
    const size_t max_sites = nsam_with_s_.size() - 1;
    Denoms subcalc(w_gene_, th_path, annot_, max_sites);
    double loglik = 0.0;
    for (const auto& genotype: genot_) {
        loglik += subcalc.lnp_sample(genotype);
    }
    const auto lnD = subcalc.log();
    // std::cout << "lnD: " << lnD << std::endl;
    // -inf, 0, D2, D3, ...
    for (size_t s=2; s<=max_sites; ++s) {
        loglik -= nsam_with_s_[s] * lnD[s];
    }
    return loglik;
}

std::string ExactModel::init_meta() {HERE;
    if (stage_ >= STEPS_.size()) return "";
    auto oss = wtl::make_oss(2, std::ios::fixed);
    oss << "grid-" << STEPS_.at(stage_) << ".tsv.gz";
    std::string outfile = oss.str();
    try {
        wtl::izfstream ist(outfile);
        std::cerr << "Reading: " << outfile << std::endl;
        read_results(ist);
        if (skip_ == 0) {
            ++stage_;
            outfile = init_meta();
        }
    } catch (std::ios::failure& e) {
        if (errno != 2) throw;
    }
    return outfile;
}

void ExactModel::read_results(std::istream& ist) {HERE;
    size_t max_count;
    double step;
    std::tie(max_count, std::ignore, step) = read_metadata(ist);
    stage_ = guess_stage(STEPS_, step);
    std::vector<std::string> colnames;
    std::valarray<double> mle_params;
    std::tie(skip_, colnames, mle_params) = read_body(ist);
    if (skip_ == max_count) {  // is complete file
        skip_ = 0;
        mle_params_.swap(mle_params);
    }
    if (names_ != colnames) {
        std::ostringstream oss;
        oss << "Contradiction in column names:\n"
            << "genotype file: " << names_ << "\n"
            << "result file:" << colnames;
        throw std::runtime_error(oss.str());
    }
}

void ExactModel::unit_test() {HERE;
    std::stringstream sst;
    sst <<
R"({
  "pathway": ["A", "B"],
  "annotation": ["0011", "1100"],
  "sample": ["0011", "0101", "1001", "0110", "1010", "1100"]
})";
    ExactModel model(sst, 4);
    model.run(false);
}

} // namespace likeligrid
<commit_msg>Reduce bits-to-indices translation<commit_after>// -*- mode: c++; coding: utf-8 -*-
/*! @file exact.cpp
    @brief Inplementation of ExactModel class
*/
#include "exact.hpp"
#include "util.hpp"

#include <functional>

#include <json.hpp>

#include <wtl/debug.hpp>
#include <wtl/exception.hpp>
#include <wtl/iostr.hpp>
#include <wtl/zfstream.hpp>
#include <wtl/algorithm.hpp>
#include <wtl/numeric.hpp>
#include <wtl/math.hpp>
#include <wtl/os.hpp>

namespace likeligrid {

const std::vector<double> ExactModel::STEPS_ = {0.4, 0.2, 0.1, 0.05, 0.02, 0.01};
const std::vector<size_t> ExactModel::BREAKS_ = {5, 5, 5, 5, 6, 5};
bool ExactModel::SIGINT_RAISED_ = false;

ExactModel::ExactModel(const std::string& infile, const size_t max_sites):
    ExactModel(wtl::izfstream(infile), max_sites) {HERE;}

ExactModel::ExactModel(std::istream&& ist, const size_t max_sites):
    ExactModel(ist, max_sites) {HERE;}

ExactModel::ExactModel(std::istream& ist, const size_t max_sites) {HERE;
    nlohmann::json jso;
    ist >> jso;
    names_ = jso["pathway"].get<std::vector<std::string>>();
    const size_t npath = names_.size();
    annot_.reserve(npath);
    for (const std::string& s: jso["annotation"]) {
        annot_.emplace_back(s);
    }
    std::cerr << "annot_: " << annot_ << std::endl;

    const size_t nsam = jso["sample"].size();
    std::vector<bits_t> all_genotypes;
    all_genotypes.reserve(nsam);
    for (const std::string& s: jso["sample"]) {
        all_genotypes.emplace_back(s);
    }

    genot_.reserve(nsam);
    const size_t ngene = all_genotypes.at(0).size();
    nsam_with_s_.assign(ngene + 1, 0);  // at most
    std::valarray<double> s_gene(ngene);
    for (const auto& bits: all_genotypes) {
        const size_t s = bits.count();
        ++nsam_with_s_[s];
        if (s > max_sites) continue;
        genot_.push_back(bits);
        for (bits_t::size_type j=0; j<bits.size(); ++j) {
            if (bits[j]) ++s_gene[j];
        }
    }
    wtl::rstrip(&nsam_with_s_);
    std::cerr << "Original N_s: " << nsam_with_s_ << std::endl;
    if (max_sites + 1 < nsam_with_s_.size()) {
        nsam_with_s_.resize(max_sites + 1);
        std::cerr << "Using N_s: " << nsam_with_s_ << std::endl;
    } else {
        std::cerr << "Note: -s is too large" << std::endl;
    }
    w_gene_ = s_gene / s_gene.sum();
    std::cerr << "s_gene : " << s_gene << std::endl;
    std::cerr << "w_gene_: " << w_gene_ << std::endl;

    mle_params_.resize(annot_.size());
    mle_params_ = 1.2;
}

void ExactModel::run_fout() {HERE;
    const std::string outfile = init_meta();
    std::cerr << "mle_params_: " << mle_params_ << std::endl;
    if (outfile.empty()) return;
    const auto axes = make_vicinity(mle_params_, BREAKS_.at(stage_), 2.0 * STEPS_.at(stage_));
    for (size_t j=0; j<names_.size(); ++j) {
        std::cerr << names_[j] << ": " << axes[j] << std::endl;
    }
    {
        wtl::ozfstream fout(outfile, std::ios::out | std::ios::app);
        std::cerr << "Writing: " << outfile << std::endl;
        run_impl(fout, wtl::itertools::product(axes));
    }
    run_fout();
}

void ExactModel::run_cout() {HERE;
    std::cerr << "mle_params_: " << mle_params_ << std::endl;
    if (stage_ >= STEPS_.size()) return;
    const auto axes = make_vicinity(mle_params_, BREAKS_.at(stage_), 2.0 * STEPS_.at(stage_));
    for (size_t j=0; j<names_.size(); ++j) {
        std::cerr << names_[j] << ": " << axes[j] << std::endl;
    }
    {
        std::stringstream sst;
        run_impl(sst, wtl::itertools::product(axes));
        std::cout << sst.str();
        read_results(sst);
    }
    ++stage_;
    run_cout();
}

void ExactModel::search_limits() const {HERE;
    {
        const std::vector<std::valarray<double>> axes(names_.size(), wtl::lin_spaced(200, 2.0, 0.01));
        wtl::ozfstream fout("uniaxis.tsv.gz");
        run_impl(fout, wtl::itertools::uniaxis(axes, mle_params_));
    }
    for (const auto& p: find_intersections(*this)) {
        std::cerr << p.first << ": " << p.second << std::endl;
        const auto axes = make_vicinity(p.second, 5, 0.02);
        wtl::ozfstream fout("limit-" + p.first + ".tsv.gz");
        //TODO: if exists
        run_impl(fout, wtl::itertools::product(axes));
    }
}

void ExactModel::run_impl(std::ostream& ost, wtl::itertools::Generator<std::valarray<double>>&& gen) const {HERE;
    std::cerr << skip_ << " to " << gen.max_count() << std::endl;
    if (skip_ == 0) {
        ost << "##max_count=" << gen.max_count() << "\n";
        ost << "##max_sites=" << nsam_with_s_.size() - 1 << "\n";
        ost << "##step=" << STEPS_.at(stage_) << "\n";
        ost << "loglik\t" << wtl::join(names_, "\t") << "\n";
    }
    auto buffer = wtl::make_oss();
    for (const auto& th_path: gen(skip_)) {
        buffer << calc_loglik(th_path) << "\t"
               << wtl::str_join(th_path, "\t") << "\n";
        if (gen.count() % 100 == 0) {  // snapshot for long run
            std::cerr << "*" << std::flush;
            ost << buffer.str();
            ost.flush();
            buffer.str("");
        }
        if (SIGINT_RAISED_) {throw wtl::KeyboardInterrupt();}
    }
    std::cerr << "-\n";
    ost << buffer.str();
}

inline std::valarray<size_t> to_indices(const bits_t& bits) {
    std::valarray<size_t> indices(bits.count());
    for (size_t i=0, j=0; j<bits.size(); ++j) {
        if (bits[j]) {
            indices[i] = j;
            ++i;
        }
    }
    return indices;
}

inline double slice_prod(const std::valarray<double>& coefs, const bits_t& bits) {
    double p = 1.0;
    for (bits_t::size_type i=0; i<bits.size(); ++i) {
        if (bits[i]) p *= coefs[i];
    }
    return p;
}

class Denoms {
  public:
    Denoms() = delete;
    Denoms(const std::valarray<double>& w_gene,
        const std::valarray<double>& th_path,
        const std::vector<bits_t>& annot,
        const size_t max_sites):
        w_gene_(w_gene),
        th_path_(th_path),
        annot_(annot),
        max_sites_(max_sites),
        denoms_(max_sites + 1)
    {
        const size_t ngene = w_gene.size();
        effects_.reserve(ngene);
        for (bits_t::size_type j=0; j<ngene; ++j) {
            effects_.emplace_back(translate(j));
        }
        // std::cerr << "effects_: " << effects_ << std::endl;
        mutate(bits_t(ngene, 0), bits_t(annot.size(), 0), 1.0);
        // std::cerr << "denoms_: " << denoms_ << std::endl;
    }
    const std::valarray<double> log() const {return std::log(denoms_);}

    double lnp_sample(const bits_t& genotype) const {
        double p = 0.0;
        const double p_basic = slice_prod(w_gene_, genotype);
        auto mut_route = to_indices(genotype);
        do {
            p += p_basic * discount(mut_route);
        } while (std::next_permutation(std::begin(mut_route), std::end(mut_route)));
        return std::log(p);
    }

  private:
    void mutate(const bits_t& genotype, const bits_t& pathtype, const double anc_p) {
        const auto s = genotype.count() + 1;
        for (bits_t::size_type j=0; j<genotype.size(); ++j) {
            if (genotype[j]) continue;
            const bits_t& mut_path = effects_[j];
            double p = anc_p;
            p *= w_gene_[j];
            p *= discount_if_subset(pathtype, mut_path);
            denoms_[s] += p;
            if (s < max_sites_) {
                mutate(bits_t(genotype).set(j), pathtype | mut_path, p);
            }
        }
    }

    double discount_if_subset(const bits_t& pathtype, const bits_t& mut_path) const {
        double p = 1.0;
        for (bits_t::size_type i=0; i<mut_path.size(); ++i) {
            if (mut_path[i]) {
                if (pathtype[i]) {
                    p *= th_path_[i];
                } else {
                    return 1.0;
                }
            }
        }
        return p;
    }

    double discount(const std::valarray<size_t>& mut_route) const {
        double p = 1.0;
        const auto npath = annot_.size();
        bits_t pathtype(npath, 0);
        for (const auto j: mut_route) {
            const auto& mut_path = effects_[j];
            p *= discount_if_subset(pathtype, mut_path);
            pathtype |= mut_path;
        }
        return p;
    }

    bits_t translate(const bits_t::size_type& mut_idx) const {
        bits_t mut_path(annot_.size(), 0);
        for (size_t j=0; j<annot_.size(); ++j) {
            mut_path.set(j, annot_[j][mut_idx]);
        }
        return mut_path;
    }
    const std::valarray<double>& w_gene_;
    const std::valarray<double>& th_path_;
    const std::vector<bits_t>& annot_;
    const size_t max_sites_;
    std::valarray<double> denoms_;
    std::vector<bits_t> effects_;
};

double ExactModel::calc_loglik(const std::valarray<double>& th_path) const {
    const size_t max_sites = nsam_with_s_.size() - 1;
    Denoms subcalc(w_gene_, th_path, annot_, max_sites);
    double loglik = 0.0;
    for (const auto& genotype: genot_) {
        loglik += subcalc.lnp_sample(genotype);
    }
    const auto lnD = subcalc.log();
    // std::cout << "lnD: " << lnD << std::endl;
    // -inf, 0, D2, D3, ...
    for (size_t s=2; s<=max_sites; ++s) {
        loglik -= nsam_with_s_[s] * lnD[s];
    }
    return loglik;
}

std::string ExactModel::init_meta() {HERE;
    if (stage_ >= STEPS_.size()) return "";
    auto oss = wtl::make_oss(2, std::ios::fixed);
    oss << "grid-" << STEPS_.at(stage_) << ".tsv.gz";
    std::string outfile = oss.str();
    try {
        wtl::izfstream ist(outfile);
        std::cerr << "Reading: " << outfile << std::endl;
        read_results(ist);
        if (skip_ == 0) {
            ++stage_;
            outfile = init_meta();
        }
    } catch (std::ios::failure& e) {
        if (errno != 2) throw;
    }
    return outfile;
}

void ExactModel::read_results(std::istream& ist) {HERE;
    size_t max_count;
    double step;
    std::tie(max_count, std::ignore, step) = read_metadata(ist);
    stage_ = guess_stage(STEPS_, step);
    std::vector<std::string> colnames;
    std::valarray<double> mle_params;
    std::tie(skip_, colnames, mle_params) = read_body(ist);
    if (skip_ == max_count) {  // is complete file
        skip_ = 0;
        mle_params_.swap(mle_params);
    }
    if (names_ != colnames) {
        std::ostringstream oss;
        oss << "Contradiction in column names:\n"
            << "genotype file: " << names_ << "\n"
            << "result file:" << colnames;
        throw std::runtime_error(oss.str());
    }
}

void ExactModel::unit_test() {HERE;
    std::stringstream sst;
    sst <<
R"({
  "pathway": ["A", "B"],
  "annotation": ["0011", "1100"],
  "sample": ["0011", "0101", "1001", "0110", "1010", "1100"]
})";
    ExactModel model(sst, 4);
    model.run(false);
}

} // namespace likeligrid
<|endoftext|>


<commit_before>#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
#define BOOST_TEST_MODULE parse JSON

#include <arpa/inet.h>
#include <boost/test/unit_test.hpp>
#include <sys/uio.h>

#include <cstring>

#include "cJSON.h"
#include "parse.h"
#include "peer.h"
#include "state.h"

static char wrong_json[] =   "{\"id\": 7384,\"method\": add\",\"params\":{\"path\": \"foo/bar/state\",\"value\": 123}}";
static char remove_without_path[] = "{\"id\": 7384,\"method\": \"remove\",\"params\":{\"value\": 123}}";
static char fetch_without_id[] = "{\"id\": 7384,\"method\": \"fetch\",\"params\":{\"path\": {\"startsWith\": \"person\"}}}";
static char correct_fetch[] = "{\"id\": 7384,\"method\": \"fetch\",\"params\":{\"id\": \"123456\",\"path\": {\"startsWith\": \"person\"}}}";
static char path_no_string[] = "{\"id\": 7384,\"method\": \"add\",\"params\":{\"path\": 123,\"value\": 123}}";
static char no_value[] = "{\"id\": 7384,\"method\": \"add\",\"params\":{\"path\": \"foo/bar/state\"}}";
static char no_params[] = "{\"id\": 7384,\"method\": \"add\"}";

static const int ADD_WITHOUT_PATH = 1;
static const int PATH_NO_STRING = 2;
static const int NO_VALUE = 3;
static const int NO_PARAMS = 4;
static const int UNSUPPORTED_METHOD = 5;
static const int REMOVE_WITHOUT_PATH = 6;
static const int FETCH_WITHOUT_ID = 7;
static const int CORRECT_FETCH = 7;

static char readback_buffer[10000];
static char *readback_buffer_ptr = readback_buffer;

extern "C" {

	int fake_read(int fd, void *buf, size_t count)
	{
		return 0;
	}

	int fake_send(int fd, void *buf, size_t count, int flags)
	{
		return 0;
	}

	static int copy_iov(const struct iovec *iov, int iovcnt)
	{
		int count = 0;
		for (int i = 0; i < iovcnt; ++i) {
			memcpy(readback_buffer_ptr, iov[i].iov_base, iov[i].iov_len);
			readback_buffer_ptr += iov[i].iov_len;
			count += iov[i].iov_len;
		}
		return count;
	}

	int fake_writev(int fd, const struct iovec *iov, int iovcnt)
	{
		int count = copy_iov(iov, iovcnt);
		return count;
	}
}

struct F {
	F(int fd)
	{
		create_state_hashtable();
		p = alloc_peer(fd);

		readback_buffer_ptr = readback_buffer;
		std::memset(readback_buffer, 0x00, sizeof(readback_buffer));
	}
	~F()
	{
		free_peer(p);
		delete_state_hashtable();
	}

	struct peer *p;
};

static void check_invalid_message(const char *buffer, int code, const char *message_string)
{
	uint32_t len;
	const char *readback_ptr = buffer;
	memcpy(&len, readback_ptr, sizeof(len));
	len = ntohl(len);
	readback_ptr += sizeof(len);

	const char *end_parse;
	cJSON *root = cJSON_ParseWithOpts(readback_ptr, &end_parse, 0);
	BOOST_CHECK(root != NULL);

	uint32_t parsed_length = end_parse - readback_ptr;
	BOOST_CHECK(parsed_length == len);

	cJSON *error = cJSON_GetObjectItem(root, "error");
	BOOST_REQUIRE(error != NULL);

	cJSON *code_object = cJSON_GetObjectItem(error, "code");
	BOOST_REQUIRE(code_object != NULL);
	BOOST_CHECK(code_object->type == cJSON_Number);
	BOOST_CHECK(code_object->valueint == code);

	cJSON *message = cJSON_GetObjectItem(error, "message");
	BOOST_REQUIRE(message != NULL);
	BOOST_CHECK(message->type == cJSON_String);
	BOOST_CHECK(strcmp(message->valuestring, message_string) == 0);

	cJSON_Delete(root);
}

static void check_invalid_params_message(const char *buffer)
{
	check_invalid_message(buffer, -32602, "Invalid params");
}

static void check_method_not_found_message(const char *buffer)
{
	check_invalid_message(buffer, -32601, "Method not found");
}

static cJSON *create_correct_json()
{
	cJSON *root = cJSON_CreateObject();
	cJSON_AddNumberToObject(root, "id", 7384);
	cJSON_AddStringToObject(root, "method", "add");

	cJSON *params = cJSON_CreateObject();
	cJSON_AddStringToObject(params, "path", "/foo/bar/state/");
	cJSON_AddNumberToObject(params, "value", 123);
	cJSON_AddItemToObject(root, "params", params);
	return root;
}

static cJSON *create_two_method_json()
{
	cJSON *array = cJSON_CreateArray();
	cJSON *method_1 = create_correct_json();
	cJSON *method_2 = create_correct_json();
	cJSON_AddItemToArray(array, method_1);
	cJSON_AddItemToArray(array, method_2);
	return array;
}

static cJSON *create_json_no_method()
{
	cJSON *root = cJSON_CreateObject();
	cJSON_AddNumberToObject(root, "id", 7384);
	cJSON_AddStringToObject(root, "meth", "add");

	cJSON *params = cJSON_CreateObject();
	cJSON_AddStringToObject(params, "path", "/foo/bar/state/");
	cJSON_AddNumberToObject(params, "value", 123);
	cJSON_AddItemToObject(root, "params", params);
	return root;
}

static cJSON *create_json_no_string_method()
{
	cJSON *root = cJSON_CreateObject();
	cJSON_AddNumberToObject(root, "id", 7384);
	cJSON_AddNumberToObject(root, "method", 123);

	cJSON *params = cJSON_CreateObject();
	cJSON_AddStringToObject(params, "path", "/foo/bar/state/");
	cJSON_AddNumberToObject(params, "value", 123);
	cJSON_AddItemToObject(root, "params", params);
	return root;
}

static cJSON *create_json_unsupported_method()
{
	cJSON *root = cJSON_CreateObject();
	cJSON_AddNumberToObject(root, "id", 7384);
	cJSON_AddStringToObject(root, "method", "horst");

	cJSON *params = cJSON_CreateObject();
	cJSON_AddStringToObject(params, "path", "/foo/bar/state/");
	cJSON_AddNumberToObject(params, "value", 123);
	cJSON_AddItemToObject(root, "params", params);
	return root;
}

static cJSON *create_add_without_path()
{
	cJSON *root = cJSON_CreateObject();
	cJSON_AddNumberToObject(root, "id", 7384);
	cJSON_AddStringToObject(root, "method", "add");

	cJSON *params = cJSON_CreateObject();
	cJSON_AddNumberToObject(params, "value", 123);
	cJSON_AddItemToObject(root, "params", params);
	return root;
}

static void check_invalid_request_message(const char *buffer)
{
	check_invalid_message(buffer, -32600, "Invalid Request");
}

BOOST_AUTO_TEST_CASE(parse_correct_json)
{
	F f(-1);
	cJSON *correct_json = create_correct_json();
	char *unformatted_json = cJSON_PrintUnformatted(correct_json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json), f.p);
	cJSON_free(unformatted_json);
	cJSON_Delete(correct_json);
	BOOST_CHECK(ret == 0);
}

BOOST_AUTO_TEST_CASE(length_too_long)
{
	cJSON *correct_json = create_correct_json();
	char *unformatted_json = cJSON_PrintUnformatted(correct_json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json) + 1, NULL);
	cJSON_free(unformatted_json);
	cJSON_Delete(correct_json);

	BOOST_CHECK(ret == -1);
}

BOOST_AUTO_TEST_CASE(length_too_short)
{
	cJSON *correct_json = create_correct_json();
	char *unformatted_json = cJSON_PrintUnformatted(correct_json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json) - 1, NULL);
	cJSON_free(unformatted_json);
	cJSON_Delete(correct_json);

	BOOST_CHECK(ret == -1);
}

BOOST_AUTO_TEST_CASE(two_method)
{
	F f(-1);
	cJSON *array = create_two_method_json();
	char *unformatted_json = cJSON_PrintUnformatted(array);
	int ret = parse_message(unformatted_json, strlen(unformatted_json), f.p);
	cJSON_free(unformatted_json);
	cJSON_Delete(array);
	BOOST_CHECK(ret == 0);
}

BOOST_AUTO_TEST_CASE(wrong_array)
{
	const int numbers[2] = {1,2};
	cJSON *root = cJSON_CreateIntArray(numbers, 2);
	char *unformatted_json = cJSON_PrintUnformatted(root);
	int ret = parse_message(unformatted_json, strlen(unformatted_json), NULL);
	cJSON_free(unformatted_json);
	cJSON_Delete(root);

	BOOST_CHECK(ret == -1);
}

BOOST_AUTO_TEST_CASE(add_without_path_test)
{
	F f(ADD_WITHOUT_PATH);

	cJSON *json = create_add_without_path();
	char *unformatted_json = cJSON_PrintUnformatted(json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json), f.p);
	cJSON_free(unformatted_json);
	cJSON_Delete(json);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(path_no_string_test)
{
	F f(PATH_NO_STRING);
	int ret = parse_message(path_no_string, strlen(path_no_string), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(no_value_test)
{
	F f(NO_VALUE);
	int ret = parse_message(no_value, strlen(no_value), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(no_params_test)
{
	F f(NO_PARAMS);
	int ret = parse_message(no_params, strlen(no_params), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(unsupported_method)
{
	F f(UNSUPPORTED_METHOD);

	cJSON *json = create_json_unsupported_method();
	char *unformatted_json = cJSON_PrintUnformatted(json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json), f.p);
	cJSON_free(unformatted_json);
	cJSON_Delete(json);
	BOOST_CHECK(ret == 0);

	check_method_not_found_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(no_method)
{
	F f(UNSUPPORTED_METHOD);
	cJSON *json = create_json_no_method();
	char *unformatted_json = cJSON_PrintUnformatted(json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json), f.p);
	cJSON_free(unformatted_json);
	cJSON_Delete(json);
	BOOST_CHECK(ret == 0);

	check_invalid_request_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(no_string_method)
{
	F f(UNSUPPORTED_METHOD);
	cJSON *json = create_json_no_string_method();
	char *unformatted_json = cJSON_PrintUnformatted(json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json), f.p);
	cJSON_free(unformatted_json);
	cJSON_Delete(json);
	BOOST_CHECK(ret == 0);

	check_invalid_request_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(parse_wrong_json)
{
	int ret = parse_message(wrong_json, strlen(wrong_json), NULL);
	BOOST_CHECK(ret == -1);
}

BOOST_AUTO_TEST_CASE(remove_without_path_test)
{
	F f(REMOVE_WITHOUT_PATH);
	int ret = parse_message(remove_without_path, strlen(remove_without_path), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(fetch_without_id_test)
{
	F f(FETCH_WITHOUT_ID);
	int ret = parse_message(fetch_without_id, strlen(fetch_without_id), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(correct_fetch_test)
{
	F f(CORRECT_FETCH);
	int ret = parse_message(correct_fetch, strlen(correct_fetch), f.p);
	BOOST_CHECK(ret == 0);
}
<commit_msg>Use cJSON to create a remove message without a path.<commit_after>#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MAIN
#define BOOST_TEST_MODULE parse JSON

#include <arpa/inet.h>
#include <boost/test/unit_test.hpp>
#include <sys/uio.h>

#include <cstring>

#include "cJSON.h"
#include "parse.h"
#include "peer.h"
#include "state.h"

static char wrong_json[] =   "{\"id\": 7384,\"method\": add\",\"params\":{\"path\": \"foo/bar/state\",\"value\": 123}}";
static char fetch_without_id[] = "{\"id\": 7384,\"method\": \"fetch\",\"params\":{\"path\": {\"startsWith\": \"person\"}}}";
static char correct_fetch[] = "{\"id\": 7384,\"method\": \"fetch\",\"params\":{\"id\": \"123456\",\"path\": {\"startsWith\": \"person\"}}}";
static char path_no_string[] = "{\"id\": 7384,\"method\": \"add\",\"params\":{\"path\": 123,\"value\": 123}}";
static char no_value[] = "{\"id\": 7384,\"method\": \"add\",\"params\":{\"path\": \"foo/bar/state\"}}";
static char no_params[] = "{\"id\": 7384,\"method\": \"add\"}";

static const int ADD_WITHOUT_PATH = 1;
static const int PATH_NO_STRING = 2;
static const int NO_VALUE = 3;
static const int NO_PARAMS = 4;
static const int UNSUPPORTED_METHOD = 5;
static const int REMOVE_WITHOUT_PATH = 6;
static const int FETCH_WITHOUT_ID = 7;
static const int CORRECT_FETCH = 7;

static char readback_buffer[10000];
static char *readback_buffer_ptr = readback_buffer;

extern "C" {

	int fake_read(int fd, void *buf, size_t count)
	{
		return 0;
	}

	int fake_send(int fd, void *buf, size_t count, int flags)
	{
		return 0;
	}

	static int copy_iov(const struct iovec *iov, int iovcnt)
	{
		int count = 0;
		for (int i = 0; i < iovcnt; ++i) {
			memcpy(readback_buffer_ptr, iov[i].iov_base, iov[i].iov_len);
			readback_buffer_ptr += iov[i].iov_len;
			count += iov[i].iov_len;
		}
		return count;
	}

	int fake_writev(int fd, const struct iovec *iov, int iovcnt)
	{
		int count = copy_iov(iov, iovcnt);
		return count;
	}
}

struct F {
	F(int fd)
	{
		create_state_hashtable();
		p = alloc_peer(fd);

		readback_buffer_ptr = readback_buffer;
		std::memset(readback_buffer, 0x00, sizeof(readback_buffer));
	}
	~F()
	{
		free_peer(p);
		delete_state_hashtable();
	}

	struct peer *p;
};

static void check_invalid_message(const char *buffer, int code, const char *message_string)
{
	uint32_t len;
	const char *readback_ptr = buffer;
	memcpy(&len, readback_ptr, sizeof(len));
	len = ntohl(len);
	readback_ptr += sizeof(len);

	const char *end_parse;
	cJSON *root = cJSON_ParseWithOpts(readback_ptr, &end_parse, 0);
	BOOST_CHECK(root != NULL);

	uint32_t parsed_length = end_parse - readback_ptr;
	BOOST_CHECK(parsed_length == len);

	cJSON *error = cJSON_GetObjectItem(root, "error");
	BOOST_REQUIRE(error != NULL);

	cJSON *code_object = cJSON_GetObjectItem(error, "code");
	BOOST_REQUIRE(code_object != NULL);
	BOOST_CHECK(code_object->type == cJSON_Number);
	BOOST_CHECK(code_object->valueint == code);

	cJSON *message = cJSON_GetObjectItem(error, "message");
	BOOST_REQUIRE(message != NULL);
	BOOST_CHECK(message->type == cJSON_String);
	BOOST_CHECK(strcmp(message->valuestring, message_string) == 0);

	cJSON_Delete(root);
}

static void check_invalid_params_message(const char *buffer)
{
	check_invalid_message(buffer, -32602, "Invalid params");
}

static void check_method_not_found_message(const char *buffer)
{
	check_invalid_message(buffer, -32601, "Method not found");
}

static cJSON *create_correct_json()
{
	cJSON *root = cJSON_CreateObject();
	cJSON_AddNumberToObject(root, "id", 7384);
	cJSON_AddStringToObject(root, "method", "add");

	cJSON *params = cJSON_CreateObject();
	cJSON_AddStringToObject(params, "path", "/foo/bar/state/");
	cJSON_AddNumberToObject(params, "value", 123);
	cJSON_AddItemToObject(root, "params", params);
	return root;
}

static cJSON *create_two_method_json()
{
	cJSON *array = cJSON_CreateArray();
	cJSON *method_1 = create_correct_json();
	cJSON *method_2 = create_correct_json();
	cJSON_AddItemToArray(array, method_1);
	cJSON_AddItemToArray(array, method_2);
	return array;
}

static cJSON *create_json_no_method()
{
	cJSON *root = cJSON_CreateObject();
	cJSON_AddNumberToObject(root, "id", 7384);
	cJSON_AddStringToObject(root, "meth", "add");

	cJSON *params = cJSON_CreateObject();
	cJSON_AddStringToObject(params, "path", "/foo/bar/state/");
	cJSON_AddNumberToObject(params, "value", 123);
	cJSON_AddItemToObject(root, "params", params);
	return root;
}

static cJSON *create_json_no_string_method()
{
	cJSON *root = cJSON_CreateObject();
	cJSON_AddNumberToObject(root, "id", 7384);
	cJSON_AddNumberToObject(root, "method", 123);

	cJSON *params = cJSON_CreateObject();
	cJSON_AddStringToObject(params, "path", "/foo/bar/state/");
	cJSON_AddNumberToObject(params, "value", 123);
	cJSON_AddItemToObject(root, "params", params);
	return root;
}

static cJSON *create_json_unsupported_method()
{
	cJSON *root = cJSON_CreateObject();
	cJSON_AddNumberToObject(root, "id", 7384);
	cJSON_AddStringToObject(root, "method", "horst");

	cJSON *params = cJSON_CreateObject();
	cJSON_AddStringToObject(params, "path", "/foo/bar/state/");
	cJSON_AddNumberToObject(params, "value", 123);
	cJSON_AddItemToObject(root, "params", params);
	return root;
}

static cJSON *create_add_without_path()
{
	cJSON *root = cJSON_CreateObject();
	cJSON_AddNumberToObject(root, "id", 7384);
	cJSON_AddStringToObject(root, "method", "add");

	cJSON *params = cJSON_CreateObject();
	cJSON_AddNumberToObject(params, "value", 123);
	cJSON_AddItemToObject(root, "params", params);
	return root;
}

static cJSON *create_remove_without_path()
{
	cJSON *root = cJSON_CreateObject();
	cJSON_AddNumberToObject(root, "id", 7384);
	cJSON_AddStringToObject(root, "method", "remove");

	cJSON *params = cJSON_CreateObject();
	cJSON_AddNumberToObject(params, "value", 123);
	cJSON_AddItemToObject(root, "params", params);
	return root;
}

static void check_invalid_request_message(const char *buffer)
{
	check_invalid_message(buffer, -32600, "Invalid Request");
}

BOOST_AUTO_TEST_CASE(parse_correct_json)
{
	F f(-1);
	cJSON *correct_json = create_correct_json();
	char *unformatted_json = cJSON_PrintUnformatted(correct_json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json), f.p);
	cJSON_free(unformatted_json);
	cJSON_Delete(correct_json);
	BOOST_CHECK(ret == 0);
}

BOOST_AUTO_TEST_CASE(length_too_long)
{
	cJSON *correct_json = create_correct_json();
	char *unformatted_json = cJSON_PrintUnformatted(correct_json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json) + 1, NULL);
	cJSON_free(unformatted_json);
	cJSON_Delete(correct_json);

	BOOST_CHECK(ret == -1);
}

BOOST_AUTO_TEST_CASE(length_too_short)
{
	cJSON *correct_json = create_correct_json();
	char *unformatted_json = cJSON_PrintUnformatted(correct_json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json) - 1, NULL);
	cJSON_free(unformatted_json);
	cJSON_Delete(correct_json);

	BOOST_CHECK(ret == -1);
}

BOOST_AUTO_TEST_CASE(two_method)
{
	F f(-1);
	cJSON *array = create_two_method_json();
	char *unformatted_json = cJSON_PrintUnformatted(array);
	int ret = parse_message(unformatted_json, strlen(unformatted_json), f.p);
	cJSON_free(unformatted_json);
	cJSON_Delete(array);
	BOOST_CHECK(ret == 0);
}

BOOST_AUTO_TEST_CASE(wrong_array)
{
	const int numbers[2] = {1,2};
	cJSON *root = cJSON_CreateIntArray(numbers, 2);
	char *unformatted_json = cJSON_PrintUnformatted(root);
	int ret = parse_message(unformatted_json, strlen(unformatted_json), NULL);
	cJSON_free(unformatted_json);
	cJSON_Delete(root);

	BOOST_CHECK(ret == -1);
}

BOOST_AUTO_TEST_CASE(add_without_path_test)
{
	F f(ADD_WITHOUT_PATH);

	cJSON *json = create_add_without_path();
	char *unformatted_json = cJSON_PrintUnformatted(json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json), f.p);
	cJSON_free(unformatted_json);
	cJSON_Delete(json);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(remove_without_path_test)
{
	F f(REMOVE_WITHOUT_PATH);
	cJSON *json = create_remove_without_path();
	char *unformatted_json = cJSON_PrintUnformatted(json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json), f.p);
	cJSON_free(unformatted_json);
	cJSON_Delete(json);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(path_no_string_test)
{
	F f(PATH_NO_STRING);
	int ret = parse_message(path_no_string, strlen(path_no_string), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(no_value_test)
{
	F f(NO_VALUE);
	int ret = parse_message(no_value, strlen(no_value), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(no_params_test)
{
	F f(NO_PARAMS);
	int ret = parse_message(no_params, strlen(no_params), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(unsupported_method)
{
	F f(UNSUPPORTED_METHOD);

	cJSON *json = create_json_unsupported_method();
	char *unformatted_json = cJSON_PrintUnformatted(json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json), f.p);
	cJSON_free(unformatted_json);
	cJSON_Delete(json);
	BOOST_CHECK(ret == 0);

	check_method_not_found_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(no_method)
{
	F f(UNSUPPORTED_METHOD);
	cJSON *json = create_json_no_method();
	char *unformatted_json = cJSON_PrintUnformatted(json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json), f.p);
	cJSON_free(unformatted_json);
	cJSON_Delete(json);
	BOOST_CHECK(ret == 0);

	check_invalid_request_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(no_string_method)
{
	F f(UNSUPPORTED_METHOD);
	cJSON *json = create_json_no_string_method();
	char *unformatted_json = cJSON_PrintUnformatted(json);
	int ret = parse_message(unformatted_json, strlen(unformatted_json), f.p);
	cJSON_free(unformatted_json);
	cJSON_Delete(json);
	BOOST_CHECK(ret == 0);

	check_invalid_request_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(parse_wrong_json)
{
	int ret = parse_message(wrong_json, strlen(wrong_json), NULL);
	BOOST_CHECK(ret == -1);
}

BOOST_AUTO_TEST_CASE(fetch_without_id_test)
{
	F f(FETCH_WITHOUT_ID);
	int ret = parse_message(fetch_without_id, strlen(fetch_without_id), f.p);
	BOOST_CHECK(ret == 0);

	check_invalid_params_message(readback_buffer);
}

BOOST_AUTO_TEST_CASE(correct_fetch_test)
{
	F f(CORRECT_FETCH);
	int ret = parse_message(correct_fetch, strlen(correct_fetch), f.p);
	BOOST_CHECK(ret == 0);
}
<|endoftext|>