{
  auto *casted_data = reinterpret_cast<NativeType *>(data);
  *casted_data = ToBigEndian(FlipSign(val));
}

//                         Peloton
// art_index.cpp
// Identification: src/index/art_index.cpp

#include "index/art_index.h"

#include "common/container_tuple.h"
#include "index/scan_optimizer.h"
#include "settings/settings_manager.h"
#include "statistics/backend_stats_context.h"
#include "storage/data_table.h"
#include "storage/storage_manager.h"
#include "util/portable_endian.h"

namespace peloton {
namespace index {

namespace {

// Load the key for the given TID (i.e., an ItemPointer), storing the key into
// the provided art::Key output argument. The provided opaque 'ctx' argument
// is the index object.
void LoadKey(void *ctx, TID tid, art::Key &key) {
  auto *art_index = reinterpret_cast<const ArtIndex *>(ctx);
  auto *index_meta = art_index->GetMetadata();

  // Get physical table instance (to load appropriate tile group)
  auto *table = storage::StorageManager::GetInstance()->GetTableWithOid(
      index_meta->GetDatabaseOid(), index_meta->GetTableOid());

  // Get physical tile group
  auto *item_pointer = reinterpret_cast<ItemPointer *>(tid);
  auto tile_group = table->GetTileGroupById(item_pointer->block);

  // Construct tuple, project only indexed columns
  const auto &indexed_cols = index_meta->GetKeyAttrs();
  ContainerTuple<storage::TileGroup> tuple{tile_group.get(),
                                           item_pointer->offset, &indexed_cols};

  // Construct the index key
  art_index->ConstructArtKey(tuple, key);
}

}  // namespace

ArtIndex::ArtIndex(IndexMetadata *metadata)
    : Index(metadata),
      container_(LoadKey, this),
      key_constructor_(*GetKeySchema()) {}

bool ArtIndex::InsertEntry(const storage::Tuple *key, ItemPointer *value) {
  // Construct the key for the tree
  art::Key tree_key;
  ConstructArtKey(*key, tree_key);

  // Perform insert (always succeeds)
  auto thread_info = container_.getThreadInfo();
  container_.insert(tree_key, reinterpret_cast<TID>(value), thread_info);

  if (static_cast<StatsType>(settings::SettingsManager::GetInt(
          settings::SettingId::stats_mode)) != StatsType::INVALID) {
    stats::BackendStatsContext::GetInstance()->IncrementIndexInserts(
        GetMetadata());
  }

  // Update stats
  IncreaseNumberOfTuplesBy(1);

  return true;
}

bool ArtIndex::DeleteEntry(const storage::Tuple *key, ItemPointer *value) {
  // Construct the key for the tree
  art::Key tree_key;
  ConstructArtKey(*key, tree_key);

  // Perform deletion
  auto thread_info = container_.getThreadInfo();
  bool removed =
      container_.remove(tree_key, reinterpret_cast<TID>(value), thread_info);

  if (removed) {
    // Update stats
    DecreaseNumberOfTuplesBy(1);
    if (static_cast<StatsType>(settings::SettingsManager::GetInt(
            settings::SettingId::stats_mode)) != StatsType::INVALID) {
      stats::BackendStatsContext::GetInstance()->IncrementIndexDeletes(
          1, GetMetadata());
    }
  }

  return removed;
}

bool ArtIndex::CondInsertEntry(const storage::Tuple *key, ItemPointer *value,
                               std::function<bool(const void *)> predicate) {
  // Construct the key for the tree
  art::Key tree_key;
  ConstructArtKey(*key, tree_key);

  // Perform conditional insertion
  auto thread_info = container_.getThreadInfo();
  bool inserted = container_.conditionalInsert(
      tree_key, reinterpret_cast<TID>(value), predicate, thread_info);

  if (inserted) {
    // Update stats
    IncreaseNumberOfTuplesBy(1);
    if (static_cast<StatsType>(settings::SettingsManager::GetInt(
            settings::SettingId::stats_mode)) != StatsType::INVALID) {
      stats::BackendStatsContext::GetInstance()->IncrementIndexInserts(
          GetMetadata());
    }
  }

  return inserted;
}

void ArtIndex::ScanRange(const storage::Tuple *start, const storage::Tuple *end,
                         std::vector<ItemPointer *> &result) {
  // Build boundary keys
  art::Key start_key, end_key;
  ConstructArtKey(*start, start_key);
  ConstructArtKey(*end, end_key);

  // Perform scan
  ScanRange(start_key, end_key, result);
}

void ArtIndex::Scan(
    UNUSED_ATTRIBUTE const std::vector<type::Value> &values,
    UNUSED_ATTRIBUTE const std::vector<oid_t> &key_column_ids,
    UNUSED_ATTRIBUTE const std::vector<ExpressionType> &expr_types,
    UNUSED_ATTRIBUTE ScanDirectionType scan_direction,
    std::vector<ItemPointer *> &result,
    const ConjunctionScanPredicate *scan_predicate) {
  // Perform the appropriate scan based on the scan predicate
  if (scan_predicate->IsFullIndexScan()) {
    ScanAllKeys(result);
  } else if (scan_predicate->IsPointQuery()) {
    ScanKey(scan_predicate->GetPointQueryKey(), result);
  } else {
    ScanRange(scan_predicate->GetLowKey(), scan_predicate->GetHighKey(),
              result);
  }

  // Update stats
  if (static_cast<StatsType>(settings::SettingsManager::GetInt(
          settings::SettingId::stats_mode)) != StatsType::INVALID) {
    stats::BackendStatsContext::GetInstance()->IncrementIndexReads(
        result.size(), GetMetadata());
  }
}

void ArtIndex::ScanLimit(const std::vector<type::Value> &values,
                         const std::vector<oid_t> &key_column_ids,
                         const std::vector<ExpressionType> &expr_types,
                         ScanDirectionType scan_direction,
                         std::vector<ItemPointer *> &result,
                         const ConjunctionScanPredicate *scan_predicate,
                         uint64_t limit, uint64_t offset) {
  (void)values;
  (void)key_column_ids;
  (void)expr_types;
  (void)scan_direction;
  (void)result;
  (void)scan_predicate;
  (void)limit;
  (void)offset;
  // This fucking function takes seven fucking arguments ... The fuck?
}

void ArtIndex::ScanAllKeys(std::vector<ItemPointer *> &result) {
  // Build boundary keys
  art::Key min_key, max_key;
  key_constructor_.ConstructMinMaxKey(min_key, max_key);

  // Scan range
  ScanRange(min_key, max_key, result);
}

void ArtIndex::ScanKey(const storage::Tuple *key,
                       std::vector<ItemPointer *> &result) {
  art::Key tree_key;
  ConstructArtKey(*key, tree_key);

  std::vector<TID> tmp_result;
  auto thread_info = container_.getThreadInfo();
  container_.lookup(tree_key, tmp_result, thread_info);
  for (const auto &tid : tmp_result) {
    result.push_back(reinterpret_cast<ItemPointer *>(tid));
  }
}

void ArtIndex::ScanRange(const art::Key &start, const art::Key &end,
                         std::vector<ItemPointer *> &result) {
  const uint32_t batch_size = 1000;
  std::vector<TID> tmp_result;

  art::Key start_key;
  start_key.setFrom(start);

  bool has_more = true;
  while (has_more) {
    art::Key next_start_key;
    auto thread_info = container_.getThreadInfo();
    has_more = container_.lookupRange(start_key, end, next_start_key,
                                      tmp_result, batch_size, thread_info);

    // Copy the results to the vector
    for (const auto &tid : tmp_result) {
      result.push_back(reinterpret_cast<ItemPointer *>(tid));
    }

    // Set the next key
    start_key.setFrom(next_start_key);
  }

  // Update stats
  if (static_cast<StatsType>(settings::SettingsManager::GetInt(
          settings::SettingId::stats_mode)) != StatsType::INVALID) {
    stats::BackendStatsContext::GetInstance()->IncrementIndexReads(
        result.size(), GetMetadata());
  }
}

void ArtIndex::SetLoadKeyFunc(art::Tree::LoadKeyFunction load_func, void *ctx) {
  container_.setLoadKeyFunc(load_func, ctx);
}

// KeyConstructor

/// Here we define a four utility functions to help with conversion of native
/// integral types (i.e., 1-, 2-, 4-, and 8-byte integers) to their big-endian
namespace {

uint8_t ToBigEndian(int8_t data) { return static_cast<uint8_t>(data); }

uint16_t ToBigEndian(int16_t data) {
  return htobe16(static_cast<uint16_t>(data));
}

uint32_t ToBigEndian(int32_t data) {
  return htobe32(static_cast<uint32_t>(data));
}

uint64_t ToBigEndian(int64_t data) {
  return htobe64(static_cast<uint64_t>(data));
}

}  // namespace

template <typename NativeType>
NativeType ArtIndex::KeyConstructor::FlipSign(NativeType val) {
  // This sets 1 on the MSB of the corresponding type
  auto mask = static_cast<NativeType>(1) << (sizeof(NativeType) * 8ul - 1);
  return val ^ mask;
}

template <typename NativeType>
void ArtIndex::KeyConstructor::WriteValue(uint8_t *data, NativeType val)

// This is specialized for ASCII strings ...
void ArtIndex::KeyConstructor::WriteAsciiString(uint8_t *data, const char *val,
                                                uint32_t len) {
  // Write out the main payload, then tack on the NULL byte terminator
  PELOTON_MEMCPY(data, val, len);
  data[len] = '\0';
}

// Constructing an ART tree key from a Peloton input key involves converting it
// to a binary-comparable format. This method handles the conversion process.
//   1. For fixed-width integral types, we need to flip the sign and convert to
//      big-endian format. If the host system is big-endian, the conversion is
//      elided entirely.
//   2. For variable length strings, we write out the string and append a NULL
//      byte indicating the end of the string. The assumption is that NULL
//      cannot appear in normal strings. True for ASCII, not so for UTF-8.
//      TODO(pmenon): For UTF-8 strings, we need a third-party library to
//      generate sort-able text from UTF-8 string.
//   3. Floats/doubles are more complicated, not done here.
//   4. NULL is handled using special values.
void ArtIndex::KeyConstructor::ConstructKey(const AbstractTuple &input_key,
                                            art::Key &tree_key) const {
  // First calculate length of this key
  uint32_t key_len = 0;
  for (uint32_t i = 0; i < key_schema_.GetColumnCount(); i++) {
    auto col_info = key_schema_.GetColumn(i);
    if (col_info.IsInlined()) {
      key_len += col_info.GetFixedLength();
    } else {
      key_len += input_key.GetValue(i).GetLength();
      if (col_info.GetType() == type::TypeId::VARCHAR) {
        // Need to append NULL character
        key_len++;
      }
    }
  }

  // Set the tree key's length and grab a pointer the data area
  tree_key.setKeyLen(key_len);
  uint8_t *data = &tree_key[0];

  uint32_t offset = 0;
  for (uint32_t i = 0; i < key_schema_.GetColumnCount(); i++) {
    auto column = key_schema_.GetColumn(i);
    switch (column.GetType()) {
      case type::TypeId::BOOLEAN:
      case type::TypeId::TINYINT: {
        auto raw = type::ValuePeeker::PeekTinyInt(input_key.GetValue(i));
        WriteValue<int8_t>(data + offset, raw);
        offset += sizeof(int8_t);
        break;
      }
      case type::TypeId::SMALLINT: {
        auto raw = type::ValuePeeker::PeekSmallInt(input_key.GetValue(i));
        WriteValue<int16_t>(data + offset, raw);
        offset += sizeof(int16_t);
        break;
      }
      case type::TypeId::DATE:
      case type::TypeId::INTEGER: {
        auto raw = type::ValuePeeker::PeekInteger(input_key.GetValue(i));
        WriteValue<int32_t>(data + offset, raw);
        offset += sizeof(int32_t);
        break;
      }
      case type::TypeId::TIMESTAMP:
      case type::TypeId::BIGINT: {
        auto raw = type::ValuePeeker::PeekBigInt(input_key.GetValue(i));
        WriteValue<int64_t>(data + offset, raw);
        offset += sizeof(int64_t);
        break;
      }
      case type::TypeId::VARCHAR: {
        auto varchar_val = input_key.GetValue(i);
        auto raw = type::ValuePeeker::PeekVarchar(varchar_val);
        auto raw_len = varchar_val.GetLength();
        WriteAsciiString(data + offset, raw, raw_len);
        offset += raw_len;
        break;
      }
      default: {
        auto error =
            StringUtil::Format("Column type '%s' not supported in ART index",
                               TypeIdToString(column.GetType()).c_str());
        LOG_ERROR("%s", error.c_str());
        throw IndexException{error};
      }
    }
  }
}

void ArtIndex::KeyConstructor::ConstructMinMaxKey(art::Key &min_key,
                                                  art::Key &max_key) const {
  min_key.setKeyLen(1);
  max_key.setKeyLen(1);
  min_key[0] = 0u;
  max_key[0] = 255u;
}

}  // namespace index
}  // namespace peloton


//                         Peloton
// art_index.cpp
// Identification: src/index/art_index.cpp

#include "index/art_index.h"

#include "common/container_tuple.h"
#include "index/scan_optimizer.h"
#include "settings/settings_manager.h"
#include "statistics/backend_stats_context.h"
#include "storage/data_table.h"
#include "storage/storage_manager.h"
#include "util/portable_endian.h"

namespace peloton {
namespace index {

namespace {

// Load the key for the given TID (i.e., an ItemPointer), storing the key into
// the provided art::Key output argument. The provided opaque 'ctx' argument
// is the index object.
void LoadKey(void *ctx, TID tid, art::Key &key) {
  auto *art_index = reinterpret_cast<const ArtIndex *>(ctx);
  auto *index_meta = art_index->GetMetadata();

  // Get physical table instance (to load appropriate tile group)
  auto *table = storage::StorageManager::GetInstance()->GetTableWithOid(
      index_meta->GetDatabaseOid(), index_meta->GetTableOid());

  // Get physical tile group
  auto *item_pointer = reinterpret_cast<ItemPointer *>(tid);
  auto tile_group = table->GetTileGroupById(item_pointer->block);

  // Construct tuple, project only indexed columns
  const auto &indexed_cols = ind

  ContainerTuple<storage::TileGroup> tuple{tile_group.get(),
                                           item_pointer->offset, &indexed_cols};

  // Construct the index key
  art_index->ConstructArtKey(tuple, key);
}

}  // namespace

ArtIndex::ArtIndex(IndexMetadata *metadata)
    : Index(metadata),
      container_(LoadKey, this),
      key_constructor_(*GetKeySchema()) {}

bool ArtIndex::InsertEntry(const storage::Tuple *key, ItemPointer *value) {
  // Construct the key for the tree
  art::Key tree_key;
  ConstructArtKey(*key, tree_key);

  // Perform insert (always succeeds)
  auto thread_info = container_.getThreadInfo();
  container_.insert(tree_key, reinterpret_cast<TID>(value), thread_info);

  if (static_cast<StatsType>(settings::SettingsManager::GetInt(
          settings::SettingId::stats_mode)) != StatsType::INVALID) {
    stats::BackendStatsContext::GetInstance()->IncrementIndexInserts(
        GetMetadata());
  }

  // Update stats
  IncreaseNumberOfTuplesBy(1);

  return true;
}

bool ArtIndex::DeleteEntry(const storage::Tuple *key, ItemPointer *value) {
  // Construct the key for the tree
  art::Key tree_key;
  ConstructArtKey(*key, tree_key);

  // Perform deletion
  auto thread_info = container_.getThreadInfo();
  bool removed =
      container_.remove(tree_key, reinterpret_cast<TID>(value), thread_info);

  if (removed) {
    // Update stats
    DecreaseNumberOfTuplesBy(1);
    if (static_cast<StatsType>(settings::SettingsManager::GetInt(
            settings::SettingId::stats_mode)) != StatsType::INVALID) {
      stats::BackendStatsContext::GetInstance()->IncrementIndexDeletes(
          1, GetMetadata());
    }
  }

  return removed;
}

bool ArtIndex::CondInsertEntry(const storage::Tuple *key, ItemPointer *value,
                               std::function<bool(const void *)> predicate) {
  // Construct the key for the tree
  art::Key tree_key;
  ConstructArtKey(*key, tree_key);

  // Perform conditional insertion
  auto thread_info = container_.getThreadInfo();
  bool inserted = container_.conditionalInsert(
      tree_key, reinterpret_cast<TID>(value), predicate, thread_info);

  if (inserted) {
    // Update stats
    IncreaseNumberOfTuplesBy(1);
    if (static_cast<StatsType>(settings::SettingsManager::GetInt(
            settings::SettingId::stats_mode)) != StatsType::INVALID) {
      stats::BackendStatsContext::GetInstance()->IncrementIndexInserts(
          GetMetadata());
    }
  }

  return inserted;
}

void ArtIndex::ScanRange(const storage::Tuple *start, const storage::Tuple *end,
                         std::vector<ItemPointer *> &result) {
  // Build boundary keys
  art::Key start_key, end_key;
  ConstructArtKey(*start, start_key);
  ConstructArtKey(*end, end_key);

  // Perform scan
  ScanRange(start_key, end_key, result);
}

void ArtIndex::Scan(
    UNUSED_ATTRIBUTE const std::vector<type::Value> &values,
    UNUSED_ATTRIBUTE const std::vector<oid_t> &key_column_ids,
    UNUSED_ATTRIBUTE const std::vector<ExpressionType> &expr_types,
    UNUSED_ATTRIBUTE ScanDirectionType scan_direction,
    std::vector<ItemPointer *> &result,
    const ConjunctionScanPredicate *scan_predicate) {
  // Perform the appropriate scan based on the scan predicate
  if (scan_predicate->IsFullIndexScan()) {
    ScanAllKeys(result);
  } else if (scan_predicate->IsPointQuery()) {
    ScanKey(scan_predicate->GetPointQueryKey(), result);
  } else {
    ScanRange(scan_predicate->GetLowKey(), scan_predicate->GetHighKey(),
              result);
  }

  // Update stats
  if (static_cast<StatsType>(settings::SettingsManager::GetInt(
          settings::SettingId::stats_mode)) != StatsType::INVALID) {
    stats::BackendStatsContext::GetInstance()->IncrementIndexReads(
        result.size(), GetMetadata());
  }
}

void ArtIndex::ScanLimit(const std::vector<type::Value> &values,
                         const std::vector<oid_t> &key_column_ids,
                         const std::vector<ExpressionType> &expr_types,
                         ScanDirectionType scan_direction,
                         std::vector<ItemPointer *> &result,
                         const ConjunctionScanPredicate *scan_predicate,
                         uint64_t limit, uint64_t offset) {
  (void)values;
  (void)key_column_ids;
  (void)expr_types;
  (void)scan_direction;
  (void)result;
  (void)scan_predicate;
  (void)limit;
  (void)offset;
  // This fucking function takes seven fucking arguments ... The fuck?
}

void ArtIndex::ScanAllKeys(std::vector<ItemPointer *> &result) {
  // Build boundary keys
  art::Key min_key, max_key;
  key_constructor_.ConstructMinMaxKey(min_key, max_key);

  // Scan range
  ScanRange(min_key, max_key, result);
}

void ArtIndex::ScanKey(const storage::Tuple *key,
                       std::vector<ItemPointer *> &result) {
  art::Key tree_key;
  ConstructArtKey(*key, tree_key);

  std::vector<TID> tmp_result;
  auto thread_info = container_.getThreadInfo();
  container_.lookup(tree_key, tmp_result, thread_info);
  for (const auto &tid : tmp_result) {
    result.push_back(reinterpret_cast<ItemPointer *>(tid));
  }
}

void ArtIndex::ScanRange(const art::Key &start, const art::Key &end,
                         std::vector<ItemPointer *> &result) {
  const uint32_t batch_size = 1000;
  std::vector<TID> tmp_result;

  art::Key start_key;
  start_key.setFrom(start);

  bool has_more = true;
  while (has_more) {
    art::Key next_start_key;
    auto thread_info = container_.getThreadInfo();
    has_more = container_.lookupRange(start_key, end, next_start_key,
                                      tmp_result, batch_size, thread_info);

    // Copy the results to the vector
    for (const auto &tid : tmp_result) {
      result.push_back(reinterpret_cast<ItemPointer *>(tid));
    }

    // Set the next key
    start_key.setFrom(next_start_key);
  }

  // Update stats
  if (static_cast<StatsType>(settings::SettingsManager::GetInt(
          settings::SettingId::stats_mode)) != StatsType::INVALID) {
    stats::BackendStatsContext::GetInstance()->IncrementIndexReads(
        result.size(), GetMetadata());
  }
}

void ArtIndex::SetLoadKeyFunc(art::Tree::LoadKeyFunction load_func, void *ctx) {
  container_.setLoadKeyFunc(load_func, ctx);
}

// KeyConstructor

/// Here we define a four utility functions to help with conversion of native
/// integral types (i.e., 1-, 2-, 4-, and 8-byte integers) to their big-endian
namespace {

uint8_t ToBigEndian(int8_t data) { return static_cast<uint8_t>(data); }

uint16_t ToBigEndian(int16_t data) {
  return htobe16(static_cast<uint16_t>(data));
}

uint32_t ToBigEndian(int32_t data) {
  return htobe32(static_cast<uint32_t>(data));
}

uint64_t ToBigEndian(int64_t data) {
  return htobe64(static_cast<uint64_t>(data));
}

}  // namespace

template <typename NativeType>
NativeType ArtIndex::KeyConstructor::FlipSign(NativeType val) {
  // This sets 1 on the MSB of the corresponding type
  auto mask = static_cast<NativeType>(1) << (sizeof(NativeType) * 8ul - 1);
  return val ^ mask;
}

template <typename NativeType>
void ArtIndex::KeyConstructor::WriteValue(uint8_t *data, NativeType val) {
  auto *casted_data = reinterpret_cast<NativeType *>(data);
  *casted_data = ToBigEndian(FlipSign(val));
}

// This is specialized for ASCII strings ...
void ArtIndex::KeyConstructor::WriteAsciiString(uint8_t *data, const char *val,
                                                uint32_t len) {
  // Write out the main payload, then tack on the NULL byte terminator
  PELOTON_MEMCPY(data, val, len);
  data[len] = '\0';
}

// Constructing an ART tree key from a Peloton input key involves converting it
// to a binary-comparable format. This method handles the conversion process.
//   1. For fixed-width integral types, we need to flip the sign and convert to
//      big-endian format. If the host system is big-endian, the conversion is
//      elided entirely.
//   2. For variable length strings, we write out the string and append a NULL
//      byte indicating the end of the string. The assumption is that NULL
//      cannot appear in normal strings. True for ASCII, not so for UTF-8.
//      TODO(pmenon): For UTF-8 strings, we need a third-party library to
//      generate sort-able text from UTF-8 string.
//   3. Floats/doubles are more complicated, not done here.
//   4. NULL is handled using special values.
void ArtIndex::KeyConstructor::ConstructKey(const AbstractTuple &input_key,
                                            art::Key &tree_key) const {
  // First calculate length of this key
  uint32_t key_len = 0;
  for (uint32_t i = 0; i < key_schema_.GetColumnCount(); i++) {
    auto col_info = key_schema_.GetColumn(i);
    if (col_info.IsInlined()) {
      key_len += col_info.GetFixedLength();
    } else {
      key_len += input_key.GetValue(i).GetLength();
      if (col_info.GetType() == type::TypeId::VARCHAR) {
        // Need to append NULL character
        key_len++;
      }
    }
  }

  // Set the tree key's length and grab a pointer the data area
  tree_key.setKeyLen(key_len);
  uint8_t *data = &tree_key[0];

  uint32_t offset = 0;
  for (uint32_t i = 0; i < key_schema_.GetColumnCount(); i++) {
    auto column = key_schema_.GetColumn(i);
    switch (column.GetType()) {
      case type::TypeId::BOOLEAN:
      case type::TypeId::TINYINT: {
        auto raw = type::ValuePeeker::PeekTinyInt(input_key.GetValue(i));
        WriteValue<int8_t>(data + offset, raw);
        offset += sizeof(int8_t);
        break;
      }
      case type::TypeId::SMALLINT: {
        auto raw = type::ValuePeeker::PeekSmallInt(input_key.GetValue(i));
        WriteValue<int16_t>(data + offset, raw);
        offset += sizeof(int16_t);
        break;
      }
      case type::TypeId::DATE:
      case type::TypeId::INTEGER: {
        auto raw = type::ValuePeeker::PeekInteger(input_key.GetValue(i));
        WriteValue<int32_t>(data + offset, raw);
        offset += sizeof(int32_t);
        break;
      }
      case type::TypeId::TIMESTAMP:
      case type::TypeId::BIGINT: {
        auto raw = type::ValuePeeker::PeekBigInt(input_key.GetValue(i));
        WriteValue<int64_t>(data + offset, raw);
        offset += sizeof(int64_t);
        break;
      }
      case type::TypeId::VARCHAR: {
        auto varchar_val = input_key.GetValue(i);
        auto raw = type::ValuePeeker::PeekVarchar(varchar_val);
        auto raw_len = varchar_val.GetLength();
        WriteAsciiString(data + offset, raw, raw_len);
        offset += raw_len;
        break;
      }
      default: {
        auto error =
            StringUtil::Format("Column type '%s' not supported in ART index",
                               TypeIdToString(column.GetType()).c_str());
        LOG_ERROR("%s", error.c_str());
        throw IndexException{error};
      }
    }
  }
}

void ArtIndex::KeyConstructor::ConstructMinMaxKey(art::Key &min_key,
                                                  art::Key &max_key) const {
  min_key.setKeyLen(1);
  max_key.setKeyLen(1);
  min_key[0] = 0u;
  max_key[0] = 255u;
}

}  // namespace index
}  // namespace peloton


ArtIndex::ArtIndex(IndexMetadata *metadata)
    : Index(metadata),
      container_(LoadKey, this),
      key_constructor_(*GetKeySchema()) {}

//                         Peloton
// art_index.cpp
// Identification: src/index/art_index.cpp

#include "index/art_index.h"

#include "common/container_tuple.h"
#include "index/scan_optimizer.h"
#include "settings/settings_manager.h"
#include "statistics/backend_stats_context.h"
#include "storage/data_table.h"
#include "storage/storage_manager.h"
#include "util/portable_endian.h"

namespace peloton {
namespace index {

namespace {

// Load the key for the given TID (i.e., an ItemPointer), storing the key into
// the provided art::Key output argument. The provided opaque 'ctx' argument
// is the index object.
void LoadKey(void *ctx, TID tid, art::Key &key) {
  auto *art_index = reinterpret_cast<const ArtIndex *>(ctx);
  auto *index_meta = art_index->GetMetadata();

  // Get physical table instance (to load appropriate tile group)
  auto *table = storage::StorageManager::GetInstance()->GetTableWithOid(
      index_meta->GetDatabaseOid(), index_meta->GetTableOid());

  // Get physical tile group
  auto *item_pointer = reinterpret_cast<ItemPointer *>(tid);
  auto tile_group = table->GetTileGroupById(item_pointer->block);

  // Construct tuple, project only indexed columns
  const auto &indexed_cols = index_meta->GetKeyAttrs();
  ContainerTuple<storage::TileGroup> tuple{tile_group.get(),
                                           item_pointer->offset, &indexed_cols};

  // Construct the index key
  art_index->ConstructArtKey(tuple, key);
}

}  // namespace

bool ArtIndex::InsertEntry(const storage::Tuple *key, ItemPointer *value) {
  // Construct the key for the tree
  art::Key tree_key;
  ConstructArtKey(*key, tree_key);

  // Perform insert (always succeeds)
  auto thread_info = container_.getThreadInfo();
  container_.insert(tree_key, reinterpret_cast<TID>(value), thread_info);

  if (static_cast<StatsType>(settings::SettingsManager::GetInt(
          settings::SettingId::stats_mode)) != StatsType::INVALID) {
    stats::BackendStatsContext::GetInstance()->IncrementIndexInserts(
        GetMetadata());
  }

  // Update stats
  IncreaseNumberOfTuplesBy(1);

  return true;
}

bool ArtIndex::DeleteEntry(const storage::Tuple *key, ItemPointer *value) {
  // Construct the key for the tree
  art::Key tree_key;
  ConstructArtKey(*key, tree_key);

  // Perform deletion
  auto thread_info = container_.getThreadInfo();
  bool removed =
      container_.remove(tree_key, reinterpret_cast<TID>(value), thread_info);

  if (removed) {
    // Update stats
    DecreaseNumberOfTuplesBy(1);
    if (static_cast<StatsType>(settings::SettingsManager::GetInt(
            settings::SettingId::stats_mode)) != StatsType::INVALID) {
      stats::BackendStatsContext::GetInstance()->IncrementIndexDeletes(
          1, GetMetadata());
    }
  }

  return removed;
}

bool ArtIndex::CondInsertEntry(const storage::Tuple *key, ItemPointer *value,
                               std::function<bool(const void *)> predicate) {
  // Construct the key for the tree
  art::Key tree_key;
  ConstructArtKey(*key, tree_key);

  // Perform conditional insertion
  auto thread_info = container_.getThreadInfo();
  bool inserted = container_.conditionalInsert(
      tree_key, reinterpret_cast<TID>(value), predicate, thread_info);

  if (inserted) {
    // Update stats
    IncreaseNumberOfTuplesBy(1);
    if (static_cast<StatsType>(settings::SettingsManager::GetInt(
            settings::SettingId::stats_mode)) != StatsType::INVALID) {
      stats::BackendStatsContext::GetInstance()->IncrementIndexInserts(
          GetMetadata());
    }
  }

  return inserted;
}

void ArtIndex::ScanRange(const storage::Tuple *start, const storage::Tuple *end,
                         std::vector<ItemPointer *> &result) {
  // Build boundary keys
  art::Key start_key, end_key;
  ConstructArtKey(*start, start_key);
  ConstructArtKey(*end, end_key);

  // Perform scan
  ScanRange(start_key, end_key, result);
}

void ArtIndex::Scan(
    UNUSED_ATTRIBUTE const std::vector<type::Value> &values,
    UNUSED_ATTRIBUTE const std::vector<oid_t> &key_column_ids,
    UNUSED_ATTRIBUTE const std::vector<ExpressionType> &expr_types,
    UNUSED_ATTRIBUTE ScanDirectionType scan_direction,
    std::vector<ItemPointer *> &result,
    const ConjunctionScanPredicate *scan_predicate) {
  // Perform the appropriate scan based on the scan predicate
  if (scan_predicate->IsFullIndexScan()) {
    ScanAllKeys(result);
  } else if (scan_predicate->IsPointQuery()) {
    ScanKey(scan_predicate->GetPointQueryKey(), result);
  } else {
    ScanRange(scan_predicate->GetLowKey(), scan_predicate->GetHighKey(),
              result);
  }

  // Update stats
  if (static_cast<StatsType>(settings::SettingsManager::GetInt(
          settings::SettingId::stats_mode)) != StatsType::INVALID) {
    stats::BackendStatsContext::GetInstance()->IncrementIndexReads(
        result.size(), GetMetadata());
  }
}

void ArtIndex::ScanLimit(const std::vector<type::Value> &values,
                         const std::vector<oid_t> &key_column_ids,
                         const std::vector<ExpressionType> &expr_types,
                         ScanDirectionType scan_direction,
                         std::vector<ItemPointer *> &result,
                         const ConjunctionScanPredicate *scan_predicate,
                         uint64_t limit, uint64_t offset) {
  (void)values;
  (void)key_column_ids;
  (void)expr_types;
  (void)scan_direction;
  (void)result;
  (void)scan_predicate;
  (void)limit;
  (void)offset;
  // This fucking function takes seven fucking arguments ... The fuck?
}

void ArtIndex::ScanAllKeys(std::vector<ItemPointer *> &result) {
  // Build boundary keys
  art::Key min_key, max_key;
  key_constructor_.ConstructMinMaxKey(min_key, max_key);

  // Scan range
  ScanRange(min_key, max_key, result);
}

void ArtIndex::ScanKey(const storage::Tuple *key,
                       std::vector<ItemPointer *> &result) {
  art::Key tree_key;
  ConstructArtKey(*key, tree_key);

  std::vector<TID> tmp_result;
  auto thread_info = container_.getThreadInfo();
  container_.lookup(tree_key, tmp_result, thread_info);
  for (const auto &tid : tmp_result) {
    result.push_back(reinterpret_cast<ItemPointer *>(tid));
  }
}

void ArtIndex::ScanRange(const art::Key &start, const art::Key &end,
                         std::vector<ItemPointer *> &result) {
  const uint32_t batch_size = 1000;
  std::vector<TID> tmp_result;

  art::Key start_key;
  start_key.setFrom(start);

  bool has_more = true;
  while (has_more) {
    art::Key next_start_key;
    auto thread_info = container_.getThreadInfo();
    has_more = container_.lookupRange(start_key, end, next_start_key,
                                      tmp_result, batch_size, thread_info);

    // Copy the results to the vector
    for (const auto &tid : tmp_result) {
      result.push_back(reinterpret_cast<ItemPointer *>(tid));
    }

    // Set the next key
    start_key.setFrom(next_start_key);
  }

  // Update stats
  if (static_cast<StatsType>(settings::SettingsManager::GetInt(
          settings::SettingId::stats_mode)) != StatsType::INVALID) {
    stats::BackendStatsContext::GetInstance()->IncrementIndexReads(
        result.size(), GetMetadata());
  }
}

void ArtIndex::SetLoadKeyFunc(art::Tree::LoadKeyFunction load_func, void *ctx) {
  container_.setLoadKeyFunc(load_func, ctx);
}

// KeyConstructor

/// Here we define a four utility functions to help with conversion of native
/// integral types (i.e., 1-, 2-, 4-, and 8-byte integers) to their big-endian
namespace {

uint8_t ToBigEndian(int8_t data) { return static_cast<uint8_t>(data); }

uint16_t ToBigEndian(int16_t data) {
  return htobe16(static_cast<uint16_t>(data));
}

uint32_t ToBigEndian(int32_t data) {
  return htobe32(static_cast<uint32_t>(data));
}

uint64_t ToBigEndian(int64_t data) {
  return htobe64(static_cast<uint64_t>(data));
}

}  // namespace

template <typename NativeType>
NativeType ArtIndex::KeyConstructor::FlipSign(NativeType val) {
  // This sets 1 on the MSB of the corresponding type
  auto mask = static_cast<NativeType>(1) << (sizeof(NativeType) * 8ul - 1);
  return val ^ mask;
}

template <typename NativeType>
void ArtIndex::KeyConstructor::WriteValue(uint8_t *data, NativeType val) {
  auto *casted_data = reinterpret_cast<NativeType *>(data);
  *casted_data = ToBigEndian(FlipSign(val));
}

// This is specialized for ASCII strings ...
void ArtIndex::KeyConstructor::WriteAsciiString(uint8_t *data, const char *val,
                                                uint32_t len) {
  // Write out the main payload, then tack on the NULL byte terminator
  PELOTON_MEMCPY(data, val, len);
  data[len] = '\0';
}

// Constructing an ART tree key from a Peloton input key involves converting it
// to a binary-comparable format. This method handles the conversion process.
//   1. For fixed-width integral types, we need to flip the sign and convert to
//      big-endian format. If the host system is big-endian, the conversion is
//      elided entirely.
//   2. For variable length strings, we write out the string and append a NULL
//      byte indicating the end of the string. The assumption is that NULL
//      cannot appear in normal strings. True for ASCII, not so for UTF-8.
//      TODO(pmenon): For UTF-8 strings, we need a third-party library to
//      generate sort-able text from UTF-8 string.
//   3. Floats/doubles are more complicated, not done here.
//   4. NULL is handled using special values.
void ArtIndex::KeyConstructor::ConstructKey(const AbstractTuple &input_key,
                                            art::Key &tree_key) const {
  // First calculate length of this key
  uint32_t key_len = 0;
  for (uint32_t i = 0; i < key_schema_.GetColumnCount(); i++) {
    auto col_info = key_schema_.GetColumn(i);
    if (col_info.IsInlined()) {
      key_len += col_info.GetFixedLength();
    } else {
      key_len += input_key.GetValue(i).GetLength();
      if (col_info.GetType() == type::TypeId::VARCHAR) {
        // Need to append NULL character
        key_len++;
      }
    }
  }

  // Set the tree key's length and grab a pointer the data area
  tree_key.setKeyLen(key_len);
  uint8_t *data = &tree_key[0];

  uint32_t offset = 0;
  for (uint32_t i = 0; i < key_schema_.GetColumnCount(); i++) {
    auto column = key_schema_.GetColumn(i);
    switch (column.GetType()) {
      case type::TypeId::BOOLEAN:
      case type::TypeId::TINYINT: {
        auto raw = type::ValuePeeker::PeekTinyInt(input_key.GetValue(i));
        WriteValue<int8_t>(data + offset, raw);
        offset += sizeof(int8_t);
        break;
      }
      case type::TypeId::SMALLINT: {
        auto raw = type::ValuePeeker::PeekSmallInt(input_key.GetValue(i));
        WriteValue<int16_t>(data + offset, raw);
        offset += sizeof(int16_t);
        break;
      }
      case type::TypeId::DATE:
      case type::TypeId::INTEGER: {
        auto raw = type::ValuePeeker::PeekInteger(input_key.GetValue(i));
        WriteValue<int32_t>(data + offset, raw);
        offset += sizeof(int32_t);
        break;
      }
      case type::TypeId::TIMESTAMP:
      case type::TypeId::BIGINT: {
        auto raw = type::ValuePeeker::PeekBigInt(input_key.GetValue(i));
        WriteValue<int64_t>(data + offset, raw);
        offset += sizeof(int64_t);
        break;
      }
      case type::TypeId::VARCHAR: {
        auto varchar_val = input_key.GetValue(i);
        auto raw = type::ValuePeeker::PeekVarchar(varchar_val);
        auto raw_len = varchar_val.GetLength();
        WriteAsciiString(data + offset, raw, raw_len);
        offset += raw_len;
        break;
      }
      default: {
        auto error =
            StringUtil::Format("Column type '%s' not supported in ART index",
                               TypeIdToString(column.GetType()).c_str());
        LOG_ERROR("%s", error.c_str());
        throw IndexException{error};
      }
    }
  }
}

void ArtIndex::KeyConstructor::ConstructMinMaxKey(art::Key &min_key,
                                                  art::Key &max_key) const {
  min_key.setKeyLen(1);
  max_key.setKeyLen(1);
  min_key[0] = 0u;
  max_key[0] = 255u;
}

}  // namespace index
}  // namespace peloton


while (has_more) {
    art::Key next_start_key;
    auto thread_info = container_.getThreadInfo();
    has_more = container_.lookupRange(start_key, end, next_start_key,
                                      tmp_result, batch_size, thread_info);

    // Copy the results to the vector
    for (const auto &tid : tmp_result) {
      result.push_back(reinterpret_cast<ItemPointer *>(tid));
    }

    // Set the next key
    start_key.setFrom(next_start_key);
  }

//                         Peloton
// art_index.cpp
// Identification: src/index/art_index.cpp

#include "index/art_index.h"

#include "common/container_tuple.h"
#include "index/scan_optimizer.h"
#include "settings/settings_manager.h"
#include "statistics/backend_stats_context.h"
#include "storage/data_table.h"
#include "storage/storage_manager.h"
#include "util/portable_endian.h"

namespace peloton {
namespace index {

namespace {

// Load the key for the given TID (i.e., an ItemPointer), storing the key into
// the provided art::Key output argument. The provided opaque 'ctx' argument
// is the index object.
void LoadKey(void *ctx, TID tid, art::Key &key) {
  auto *art_index = reinterpret_cast<const ArtIndex *>(ctx);
  auto *index_meta = art_index->GetMetadata();

  // Get physical table instance (to load appropriate tile group)
  auto *table = storage::StorageManager::GetInstance()->GetTableWithOid(
      index_meta->GetDatabaseOid(), index_meta->GetTableOid());

  // Get physical tile group
  auto *item_pointer = reinterpret_cast<ItemPointer *>(tid);
  auto tile_group = table->GetTileGroupById(item_pointer->block);

  // Construct tuple, project only indexed columns
  const auto &indexed_cols = index_meta->GetKeyAttrs();
  ContainerTuple<storage::TileGroup> tuple{tile_group.get(),
                                           item_pointer->offset, &indexed_cols};

  // Construct the index key
  art_index->ConstructArtKey(tuple, key);
}

}  // namespace

ArtIndex::ArtIndex(IndexMetadata *metadata)
    : Index(metadata),
      container_(LoadKey, this),
      key_constructor_(*GetKeySchema()) {}

bool ArtIndex::InsertEntry(const storage::Tuple *key, ItemPointer *value) {
  // Construct the key for the tree
  art::Key tree_key;
  ConstructArtKey(*key, tree_key);

  // Perform insert (always succeeds)
  auto thread_info = container_.getThreadInfo();
  container_.insert(tree_key, reinterpret_cast<TID>(value), thread_info);

  if (static_cast<StatsType>(settings::SettingsManager::GetInt(
          settings::SettingId::stats_mode)) != StatsType::INVALID) {
    stats::BackendStatsContext::GetInstance()->IncrementIndexInserts(
        GetMetadata());
  }

  // Update stats
  IncreaseNumberOfTuplesBy(1);

  return true;
}

bool ArtIndex::DeleteEntry(const storage::Tuple *key, ItemPointer *value) {
  // Construct the key for the tree
  art::Key tree_key;
  ConstructArtKey(*key, tree_key);

  // Perform deletion
  auto thread_info = container_.getThreadInfo();
  bool removed =
      container_.remove(tree_key, reinterpret_cast<TID>(value), thread_info);

  if (removed) {
    // Update stats
    DecreaseNumberOfTuplesBy(1);
    if (static_cast<StatsType>(settings::SettingsManager::GetInt(
            settings::SettingId::stats_mode)) != StatsType::INVALID) {
      stats::BackendStatsContext::GetInstance()->IncrementIndexDeletes(
          1, GetMetadata());
    }
  }

  return removed;
}

bool ArtIndex::CondInsertEntry(const storage::Tuple *key, ItemPointer *value,
                               std::function<bool(const void *)> predicate) {
  // Construct the key for the tree
  art::Key tree_key;
  ConstructArtKey(*key, tree_key);

  // Perform conditional insertion
  auto thread_info = container_.getThreadInfo();
  bool inserted = container_.conditionalInsert(
      tree_key, reinterpret_cast<TID>(value), predicate, thread_info);

  if (inserted) {
    // Update stats
    IncreaseNumberOfTuplesBy(1);
    if (static_cast<StatsType>(settings::SettingsManager::GetInt(
            settings::SettingId::stats_mode)) != StatsType::INVALID) {
      stats::BackendStatsContext::GetInstance()->IncrementIndexInserts(
          GetMetadata());
    }
  }

  return inserted;
}

void ArtIndex::ScanRange(const storage::Tuple *start, const storage::Tuple *end,
                         std::vector<ItemPointer *> &result) {
  // Build boundary keys
  art::Key start_key, end_key;
  ConstructArtKey(*start, start_key);
  ConstructArtKey(*end, end_key);

  // Perform scan
  ScanRange(start_key, end_key, result);
}

void ArtIndex::Scan(
    UNUSED_ATTRIBUTE const std::vector<type::Value> &values,
    UNUSED_ATTRIBUTE const std::vector<oid_t> &key_column_ids,
    UNUSED_ATTRIBUTE const std::vector<ExpressionType> &expr_types,
    UNUSED_ATTRIBUTE ScanDirectionType scan_direction,
    std::vector<ItemPointer *> &result,
    const ConjunctionScanPredicate *scan_predicate) {
  // Perform the appropriate scan based on the scan predicate
  if (scan_predicate->IsFullIndexScan()) {
    ScanAllKeys(result);
  } else if (scan_predicate->IsPointQuery()) {
    ScanKey(scan_predicate->GetPointQueryKey(), result);
  } else {
    ScanRange(scan_predicate->GetLowKey(), scan_predicate->GetHighKey(),
              result);
  }

  // Update stats
  if (static_cast<StatsType>(settings::SettingsManager::GetInt(
          settings::SettingId::stats_mode)) != StatsType::INVALID) {
    stats::BackendStatsContext::GetInstance()->IncrementIndexReads(
        result.size(), GetMetadata());
  }
}

void ArtIndex::ScanLimit(const std::vector<type::Value> &values,
                         const std::vector<oid_t> &key_column_ids,
                         const std::vector<ExpressionType> &expr_types,
                         ScanDirectionType scan_direction,
                         std::vector<ItemPointer *> &result,
                         const ConjunctionScanPredicate *scan_predicate,
                         uint64_t limit, uint64_t offset) {
  (void)values;
  (void)key_column_ids;
  (void)expr_types;
  (void)scan_direction;
  (void)result;
  (void)scan_predicate;
  (void)limit;
  (void)offset;
  // This fucking function takes seven fucking arguments ... The fuck?
}

void ArtIndex::ScanAllKeys(std::vector<ItemPointer *> &result) {
  // Build boundary keys
  art::Key min_key, max_key;
  key_constructor_.ConstructMinMaxKey(min_key, max_key);

  // Scan range
  ScanRange(min_key, max_key, result);
}

void ArtIndex::ScanKey(const storage::Tuple *key,
                       std::vector<ItemPointer *> &result) {
  art::Key tree_key;
  ConstructArtKey(*key, tree_key);

  std::vector<TID> tmp_result;
  auto thread_info = container_.getThreadInfo();
  container_.lookup(tree_key, tmp_result, thread_info);
  for (const auto &tid : tmp_result) {
    result.push_back(reinterpret_cast<ItemPointer *>(tid));
  }
}

void ArtIndex::ScanRange(const art::Key &start, const art::Key &end,
                         std::vector<ItemPointer *> &result) {
  const uint32_t batch_size = 1000;
  std::vector<TID> tmp_result;

  art::Key start_key;
  start_key.setFrom(start);

  bool has_more = true;

  // Update stats
  if (static_cast<StatsType>(settings::SettingsManager::GetInt(
          settings::SettingId::stats_mode)) != StatsType::INVALID) {
    stats::BackendStatsContext::GetInstance()->IncrementIndexReads(
        result.size(), GetMetadata());
  }
}

void ArtIndex::SetLoadKeyFunc(art::Tree::LoadKeyFunction load_func, void *ctx) {
  container_.setLoadKeyFunc(load_func, ctx);
}

// KeyConstructor

/// Here we define a four utility functions to help with conversion of native
/// integral types (i.e., 1-, 2-, 4-, and 8-byte integers) to their big-endian
namespace {

uint8_t ToBigEndian(int8_t data) { return static_cast<uint8_t>(data); }

uint16_t ToBigEndian(int16_t data) {
  return htobe16(static_cast<uint16_t>(data));
}

uint32_t ToBigEndian(int32_t data) {
  return htobe32(static_cast<uint32_t>(data));
}

uint64_t ToBigEndian(int64_t data) {
  return htobe64(static_cast<uint64_t>(data));
}

}  // namespace

template <typename NativeType>
NativeType ArtIndex::KeyConstructor::FlipSign(NativeType val) {
  // This sets 1 on the MSB of the corresponding type
  auto mask = static_cast<NativeType>(1) << (sizeof(NativeType) * 8ul - 1);
  return val ^ mask;
}

template <typename NativeType>
void ArtIndex::KeyConstructor::WriteValue(uint8_t *data, NativeType val) {
  auto *casted_data = reinterpret_cast<NativeType *>(data);
  *casted_data = ToBigEndian(FlipSign(val));
}

// This is specialized for ASCII strings ...
void ArtIndex::KeyConstructor::WriteAsciiString(uint8_t *data, const char *val,
                                                uint32_t len) {
  // Write out the main payload, then tack on the NULL byte terminator
  PELOTON_MEMCPY(data, val, len);
  data[len] = '\0';
}

// Constructing an ART tree key from a Peloton input key involves converting it
// to a binary-comparable format. This method handles the conversion process.
//   1. For fixed-width integral types, we need to flip the sign and convert to
//      big-endian format. If the host system is big-endian, the conversion is
//      elided entirely.
//   2. For variable length strings, we write out the string and append a NULL
//      byte indicating the end of the string. The assumption is that NULL
//      cannot appear in normal strings. True for ASCII, not so for UTF-8.
//      TODO(pmenon): For UTF-8 strings, we need a third-party library to
//      generate sort-able text from UTF-8 string.
//   3. Floats/doubles are more complicated, not done here.
//   4. NULL is handled using special values.
void ArtIndex::KeyConstructor::ConstructKey(const AbstractTuple &input_key,
                                            art::Key &tree_key) const {
  // First calculate length of this key
  uint32_t key_len = 0;
  for (uint32_t i = 0; i < key_schema_.GetColumnCount(); i++) {
    auto col_info = key_schema_.GetColumn(i);
    if (col_info.IsInlined()) {
      key_len += col_info.GetFixedLength();
    } else {
      key_len += input_key.GetValue(i).GetLength();
      if (col_info.GetType() == type::TypeId::VARCHAR) {
        // Need to append NULL character
        key_len++;
      }
    }
  }

  // Set the tree key's length and grab a pointer the data area
  tree_key.setKeyLen(key_len);
  uint8_t *data = &tree_key[0];

  uint32_t offset = 0;
  for (uint32_t i = 0; i < key_schema_.GetColumnCount(); i++) {
    auto column = key_schema_.GetColumn(i);
    switch (column.GetType()) {
      case type::TypeId::BOOLEAN:
      case type::TypeId::TINYINT: {
        auto raw = type::ValuePeeker::PeekTinyInt(input_key.GetValue(i));
        WriteValue<int8_t>(data + offset, raw);
        offset += sizeof(int8_t);
        break;
      }
      case type::TypeId::SMALLINT: {
        auto raw = type::ValuePeeker::PeekSmallInt(input_key.GetValue(i));
        WriteValue<int16_t>(data + offset, raw);
        offset += sizeof(int16_t);
        break;
      }
      case type::TypeId::DATE:
      case type::TypeId::INTEGER: {
        auto raw = type::ValuePeeker::PeekInteger(input_key.GetValue(i));
        WriteValue<int32_t>(data + offset, raw);
        offset += sizeof(int32_t);
        break;
      }
      case type::TypeId::TIMESTAMP:
      case type::TypeId::BIGINT: {
        auto raw = type::ValuePeeker::PeekBigInt(input_key.GetValue(i));
        WriteValue<int64_t>(data + offset, raw);
        offset += sizeof(int64_t);
        break;
      }
      case type::TypeId::VARCHAR: {
        auto varchar_val = input_key.GetValue(i);
        auto raw = type::ValuePeeker::PeekVarchar(varchar_val);
        auto raw_len = varchar_val.GetLength();
        WriteAsciiString(data + offset, raw, raw_len);
        offset += raw_len;
        break;
      }
      default: {
        auto error =
            StringUtil::Format("Column type '%s' not supported in ART index",
                               TypeIdToString(column.GetType()).c_str());
        LOG_ERROR("%s", error.c_str());
        throw IndexException{error};
      }
    }
  }
}

void ArtIndex::KeyConstructor::ConstructMinMaxKey(art::Key &min_key,
                                                  art::Key &max_key) const {
  min_key.setKeyLen(1);
  max_key.setKeyLen(1);
  min_key[0] = 0u;
  max_key[0] = 255u;
}

}  // namespace index
}  // namespace peloton


/*
 *
 * This file is part of LMS.
 *
 */

#include "ArtistsView.hpp"

#include <Wt/WMenu.h>

#include "database/Session.hpp"
#include "database/TrackArtistLink.hpp"
#include "utils/EnumSet.hpp"
#include "utils/Logger.hpp"

#include "common/ValueStringModel.hpp"
#include "common/InfiniteScrollingContainer.hpp"
#include "ArtistListHelpers.hpp"
#include "Filters.hpp"
#include "LmsApplication.hpp"

using namespace Database;

namespace UserInterface {

using ArtistLinkModel = ValueStringModel<std::optional<TrackArtistLinkType>>;

Artists::Artists(Filters& filters)
: Wt::WTemplate {Wt::WString::tr("Lms.Explore.Artists.template")}
, _artistCollector {filters, _defaultMode, _maxCount}
{
	addFunction("tr", &Wt::WTemplate::Functions::tr);

	{
		auto* menu {bindNew<Wt::WMenu>("mode")};

		auto addItem = [this](Wt::WMenu& menu,const Wt::WString& str, ArtistCollector::Mode mode)
		{
			auto* item {menu.addItem(str)};
			item->clicked().connect([this, mode] { refreshView(mode); });

			if (mode == _defaultMode)
				item->renderSelected(true);
		};

		addItem(*menu, Wt::WString::tr("Lms.Explore.random"), ArtistCollector::Mode::Random);
		addItem(*menu, Wt::WString::tr("Lms.Explore.starred"), ArtistCollector::Mode::Starred);
		addItem(*menu, Wt::WString::tr("Lms.Explore.recently-played"), ArtistCollector::Mode::RecentlyPlayed);
		addItem(*menu, Wt::WString::tr("Lms.Explore.most-played"), ArtistCollector::Mode::MostPlayed);
		addItem(*menu, Wt::WString::tr("Lms.Explore.recently-added"), ArtistCollector::Mode::RecentlyAdded);
		addItem(*menu, Wt::WString::tr("Lms.Explore.all"), ArtistCollector::Mode::All);
	}

	_linkType = bindNew<Wt::WComboBox>("link-type");
	_linkType->setModel(std::make_shared<ArtistLinkModel>());
	_linkType->changed().connect([this]
	{
		const std::optional<TrackArtistLinkType> linkType {static_cast<ArtistLinkModel*>(_linkType->model().get())->getValue(_linkType->currentIndex())};
		refreshView(linkType);
	});
	refreshArtistLinkTypes();

	LmsApp->getScannerEvents().scanComplete.connect(this, [this](const Scanner::ScanStats& stats)
	{
		if (stats.nbChanges())
			refreshArtistLinkTypes();
	});

	_container = bindNew<InfiniteScrollingContainer>("artists");
	_container->onRequestElements.connect([this]
	{
		addSome();
	});

	filters.updated().connect([this]
	{
		refreshView();
	});

	refreshView(_artistCollector.getMode());
}

void
Artists::refreshView()
{
	_container->clear();
	_artistCollector.reset();
	addSome();
}

void
Artists::refreshView(ArtistCollector::Mode mode)
{
	_artistCollector.setMode(mode);
	refreshView();
}

void
Artists::refreshView(std::optional<Database::TrackArtistLinkType> linkType)
{
	_artistCollector.setArtistLinkType(linkType);
	refreshView();
}

{
	std::shared_ptr<ArtistLinkModel> linkTypeModel {std::static_pointer_cast<ArtistLinkModel>(_linkType->model())};

	EnumSet<Database::TrackArtistLinkType> usedLinkTypes;
	{
		auto transaction {LmsApp->getDbSession().createSharedTransaction()};
		usedLinkTypes = Database::TrackArtistLink::getUsedTypes(LmsApp->getDbSession());
	}

	auto addTypeIfUsed {[&](Database::TrackArtistLinkType linkType, std::string_view stringKey)
	{
		if (!usedLinkTypes.contains(linkType))
			return;

		linkTypeModel->add(Wt::WString::tr(std::string {stringKey}), linkType);
	}};

	linkTypeModel->clear();

	// add default one first (none)
	linkTypeModel->add(Wt::WString::tr("Lms.Explore.Artists.linktype-all"), std::nullopt);

	addTypeIfUsed(TrackArtistLinkType::Artist, "Lms.Explore.Artists.linktype-artist");
	addTypeIfUsed(TrackArtistLinkType::ReleaseArtist, "Lms.Explore.Artists.linktype-releaseartist");
	addTypeIfUsed(TrackArtistLinkType::Composer, "Lms.Explore.Artists.linktype-composer");
	addTypeIfUsed(TrackArtistLinkType::Lyricist, "Lms.Explore.Artists.linktype-lyricist");
	addTypeIfUsed(TrackArtistLinkType::Mixer, "Lms.Explore.Artists.linktype-mixer");
	addTypeIfUsed(TrackArtistLinkType::Producer, "Lms.Explore.Artists.linktype-producer");
	addTypeIfUsed(TrackArtistLinkType::Remixer, "Lms.Explore.Artists.linktype-remixer");
}

void
Artists::addSome()
{
	bool moreResults {};

	{
		auto transaction {LmsApp->getDbSession().createSharedTransaction()};

		const auto artists {_artistCollector.get(Range {static_cast<std::size_t>(_container->getCount()), _batchSize}, moreResults)};
		for (const auto& artist : artists)
			_container->add(ArtistListHelpers::createEntry(artist));
	}

	_container->setHasMore(moreResults);
}

} // namespace UserInterface


{
	_artistCollector.setArtistLinkType(linkType);
	refreshView();
}

/*
 *
 * This file is part of LMS.
 *
 */

#include "ArtistsView.hpp"

#include <Wt/WMenu.h>

#include "database/Session.hpp"
#include "database/TrackArtistLink.hpp"
#include "utils/EnumSet.hpp"
#include "utils/Logger.hpp"

#include "common/ValueStringModel.hpp"
#include "common/InfiniteScrollingContainer.hpp"
#include "ArtistListHelpers.hpp"
#include "Filters.hpp"
#include "LmsApplication.hpp"

using namespace Database;

namespace UserInterface {

using ArtistLinkModel = ValueStringModel<std::optional<TrackArtistLinkType>>;

Artists::Artists(Filters& filters)
: Wt::WTemplate {Wt::WString::tr("Lms.Explore.Artists.template")}
, _artistCollector {filters, _defaultMode, _maxCount}
{
	addFunction("tr", &Wt::WTemplate::Functions::tr);

	{
		auto* menu {bindNew<Wt::WMenu>("mode")};

		auto addItem = [this](Wt::WMenu& menu,const Wt::WString& str, ArtistCollector::Mode mode)
		{
			auto* item {menu.addItem(str)};
			item->clicked().connect([this, mode] { refreshView(mode); });

			if (mode == _defaultMode)
				item->renderSelected(true);
		};

		addItem(*menu, Wt::WString::tr("Lms.Explore.random"), ArtistCollector::Mode::Random);
		addItem(*menu, Wt::WString::tr("Lms.Explore.starred"), ArtistCollector::Mode::Starred);
		addItem(*menu, Wt::WString::tr("Lms.Explore.recently-played"), ArtistCollector::Mode::RecentlyPlayed);
		addItem(*menu, Wt::WString::tr("Lms.Explore.most-played"), ArtistCollector::Mode::MostPlayed);
		addItem(*menu, Wt::WString::tr("Lms.Explore.recently-added"), ArtistCollector::Mode::RecentlyAdded);
		addItem(*menu, Wt::WString::tr("Lms.Explore.all"), ArtistCollector::Mode::All);
	}

	_linkType = bindNew<Wt::WComboBox>("link-type");
	_linkType->setModel(std::make_shared<ArtistLinkModel>());
	_linkType->changed().connect([this]
	{
		const std::optional<TrackArtistLinkType> linkType {static_cast<ArtistLinkModel*>(_linkType->model().get())->getValue(_linkType->currentIndex())};
		refreshView(linkType);
	});
	refreshArtistLinkTypes();

	LmsApp->getScannerEvents().scanComplete.connect(this, [this](const Scanner::ScanStats& stats)
	{
		if (stats.nbChanges())
			refreshArtistLinkTypes();
	});

	_container = bindNew<InfiniteScrollingContainer>("artists");
	_container->onRequestElements.connect([this]
	{
		addSome();
	});

	filters.updated().connect([this]
	{
		refreshView();
	});

	refreshView(_artistCollector.getMode());
}

void
Artists::refreshView()
{
	_container->clear();
	_artistCollector.reset();
	addSome();
}

void
Artists::refreshView(ArtistCollector::Mode mode)
{
	_artistCollector.setMode(mode);
	refreshView();
}

void
Artists::refreshView(std::optional<Database::TrackArtistLinkType> linkType)

void
Artists::refreshArtistLinkTypes()
{
	std::shared_ptr<ArtistLinkModel> linkTypeModel {std::static_pointer_cast<ArtistLinkModel>(_linkType->model())};

	EnumSet<Database::TrackArtistLinkType> usedLinkTypes;
	{
		auto transaction {LmsApp->getDbSession().createSharedTransaction()};
		usedLinkTypes = Database::TrackArtistLink::getUsedTypes(LmsApp->getDbSession());
	}

	auto addTypeIfUsed {[&](Database::TrackArtistLinkType linkType, std::string_view stringKey)
	{
		if (!usedLinkTypes.contains(linkType))
			return;

		linkTypeModel->add(Wt::WString::tr(std::string {stringKey}), linkType);
	}};

	linkTypeModel->clear();

	// add default one first (none)
	linkTypeModel->add(Wt::WString::tr("Lms.Explore.Artists.linktype-all"), std::nullopt);

	addTypeIfUsed(TrackArtistLinkType::Artist, "Lms.Explore.Artists.linktype-artist");
	addTypeIfUsed(TrackArtistLinkType::ReleaseArtist, "Lms.Explore.Artists.linktype-releaseartist");
	addTypeIfUsed(TrackArtistLinkType::Composer, "Lms.Explore.Artists.linktype-composer");
	addTypeIfUsed(TrackArtistLinkType::Lyricist, "Lms.Explore.Artists.linktype-lyricist");
	addTypeIfUsed(TrackArtistLinkType::Mixer, "Lms.Explore.Artists.linktype-mixer");
	addTypeIfUsed(TrackArtistLinkType::Producer, "Lms.Explore.Artists.linktype-producer");
	addTypeIfUsed(TrackArtistLinkType::Remixer, "Lms.Explore.Artists.linktype-remixer");
}

void
Artists::addSome()
{
	bool moreResults {};

	{
		auto transaction {LmsApp->getDbSession().createSharedTransaction()};

		const auto artists {_artistCollector.get(Range {static_cast<std::size_t>(_container->getCount()), _batchSize}, moreResults)};
		for (const auto& artist : artists)
			_container->add(ArtistListHelpers::createEntry(artist));
	}

	_container->setHasMore(moreResults);
}

} // namespace UserInterface


/*
 *
 * This file is part of LMS.
 *
 */

#include "ArtistsView.hpp"

#include <Wt/WMenu.h>

#include "database/Session.hpp"
#include "database/TrackArtistLink.hpp"
#include "utils/EnumSet.hpp"
#include "utils/Logger.hpp"

#include "common/ValueStringModel.hpp"
#include "common/InfiniteScrollingContainer.hpp"
#include "ArtistListHelpers.hpp"
#include "Filters.hpp"
#include "LmsApplication.hpp"

using namespace Database;

namespace UserInterface {

using ArtistLinkModel = ValueStringModel<std::optional<TrackArtistLinkType>>;

Artists::Artists(Filters& filters)
: Wt::WTemplate {Wt::WString::tr("Lms.Explore.Artists.template")}
, _artistCollector {filters, _defaultMode, _maxCount}
{
	addFunction("tr", &Wt::WTemplate::Functions::tr);

	{
		auto* menu {bindNew<Wt::WMenu>("mode")};

		auto addItem = [this](Wt::WMenu& menu,const Wt::WString& str, ArtistCollector::Mode mode)
		{
			auto* item {menu.addItem(str)};
			item->clicked().connect([this, mode] { refreshView(mode); });

			if (mode == _defaultMode)
				item->renderSelected(true);
		};

		addItem(*menu, Wt::WString::tr("Lms.Explore.random"), ArtistCollector::Mode::Random);
		addItem(*menu, Wt::WString::tr("Lms.Explore.starred"), ArtistCollector::Mode::Starred);
		addItem(*menu, Wt::WString::tr("Lms.Explore.recently-played"), ArtistCollector::Mode::RecentlyPlayed);
		addItem(*menu, Wt::WString::tr("Lms.Explore.most-played"), ArtistCollector::Mode::MostPlayed);
		addItem(*menu, Wt::WString::tr("Lms.Explore.recently-added"), ArtistCollector::Mode::RecentlyAdded);
		addItem(*menu, Wt::WString::tr("Lms.Explore.all"), ArtistCollector::Mode::All);
	}

	_linkType = bindNew<Wt::WComboBox>("link-type");
	_linkType->setModel(std::make_shared<ArtistLinkModel>());
	_linkType->changed().connect([this]
	{
		const std::optional<TrackArtistLinkType> linkType {static_cast<ArtistLinkModel*>(_linkType->model().get())->getValue(_linkType->currentIndex())};
		refreshView(linkType);
	});
	refreshArtistLinkTypes();

	LmsApp->getScannerEvents().scanComplete.connect(this, [this](const Scanner::ScanStats& stats)
	{
		if (stats.nbChanges())
			refreshArtistLinkTypes();
	});

	_container = bindNew<InfiniteScrollingContainer>("artists");
	_container->onRequestElements.connect([this]
	{
		addSome();
	});

	filters.updated().connect([this]
	{
		refreshView();
	});

	refreshView(_artistCollector.getMode());
}

void
Artists::refreshView()
{
	_container->clear();
	_art

	addSome();
}

void
Artists::refreshView(ArtistCollector::Mode mode)
{
	_artistCollector.setMode(mode);
	refreshView();
}

void
Artists::refreshView(std::optional<Database::TrackArtistLinkType> linkType)
{
	_artistCollector.setArtistLinkType(linkType);
	refreshView();
}

void
Artists::refreshArtistLinkTypes()
{
	std::shared_ptr<ArtistLinkModel> linkTypeModel {std::static_pointer_cast<ArtistLinkModel>(_linkType->model())};

	EnumSet<Database::TrackArtistLinkType> usedLinkTypes;
	{
		auto transaction {LmsApp->getDbSession().createSharedTransaction()};
		usedLinkTypes = Database::TrackArtistLink::getUsedTypes(LmsApp->getDbSession());
	}

	auto addTypeIfUsed {[&](Database::TrackArtistLinkType linkType, std::string_view stringKey)
	{
		if (!usedLinkTypes.contains(linkType))
			return;

		linkTypeModel->add(Wt::WString::tr(std::string {stringKey}), linkType);
	}};

	linkTypeModel->clear();

	// add default one first (none)
	linkTypeModel->add(Wt::WString::tr("Lms.Explore.Artists.linktype-all"), std::nullopt);

	addTypeIfUsed(TrackArtistLinkType::Artist, "Lms.Explore.Artists.linktype-artist");
	addTypeIfUsed(TrackArtistLinkType::ReleaseArtist, "Lms.Explore.Artists.linktype-releaseartist");
	addTypeIfUsed(TrackArtistLinkType::Composer, "Lms.Explore.Artists.linktype-composer");
	addTypeIfUsed(TrackArtistLinkType::Lyricist, "Lms.Explore.Artists.linktype-lyricist");
	addTypeIfUsed(TrackArtistLinkType::Mixer, "Lms.Explore.Artists.linktype-mixer");
	addTypeIfUsed(TrackArtistLinkType::Producer, "Lms.Explore.Artists.linktype-producer");
	addTypeIfUsed(TrackArtistLinkType::Remixer, "Lms.Explore.Artists.linktype-remixer");
}

void
Artists::addSome()
{
	bool moreResults {};

	{
		auto transaction {LmsApp->getDbSession().createSharedTransaction()};

		const auto artists {_artistCollector.get(Range {static_cast<std::size_t>(_container->getCount()), _batchSize}, moreResults)};
		for (const auto& artist : artists)
			_container->add(ArtistListHelpers::createEntry(artist));
	}

	_container->setHasMore(moreResults);
}

} // namespace UserInterface


#include <tlp/test/Test.hpp>
#include <tlp/base/EmptyType.h>
#include <tlp/base/NullType.h>
#include <tlp/base/algo/Valid.h>

{
    TEST("null type should not be valid")
    {
        ASSERT_FALSE(__valid(__null()));
    };

    TEST("empty type should be valid")
    {
        ASSERT_TRUE(__valid(__empty()));
    };

    TEST("empty should not be equal to null")
    {
        ASSERT_NE(__empty(), __null());
    };
};


{
    TEST("null type should not be valid")
    {
        ASSERT_FALSE(__valid(__null()));
    };

    TEST("empty type should be valid")
    {
        ASSERT_TRUE(__valid(__empty()));
    };

    TEST("empty should not be equal to null")
    {
        ASSERT_NE(__empty(), __null());
    };
}

#include <tlp/test/Test.hpp>
#include <tlp/base/EmptyType.h>
#include <tlp/base/NullType.h>
#include <tlp/base/algo/Valid.h>

FIXTURE(TestBase)


#include <tlp/test/Test.hpp>
#include <tlp/base/EmptyType.h>
#include <tlp/base/NullType.h>
#include <tlp/base/algo/Valid.h>

FIXTURE(TestBase)
{
    TEST("n

    {
        ASSERT_FALSE(__valid(__null()));
    };

    TEST("empty type should be valid")
    {
        ASSERT_TRUE(__valid(__empty()));
    };

    TEST("empty should not be equal to null")
    {
        ASSERT_NE(__empty(), __null());
    };
};


/*****************************************************************************
 *
 * For a complete list of all authors, please refer to contributors.md
 * Interested in contributing? Visit http://example.com
 *
 *****************************************************************************/

#include "RideUseSystem.h"

namespace OpenRCT2::RideUse
{
    static RideHistory _history;
    static RideTypeHistory _typeHistory;

    RideHistory& GetHistory()
    {
        return _history;
    }

{
        return _typeHistory;
    }
} // namespace OpenRCT2::RideUse


/*****************************************************************************
 *
 * For a complete list of all authors, please refer to contributors.md
 * Interested in contributing? Visit http://example.com
 *
 *****************************************************************************/

#include "RideUseSystem.h"

namespace OpenRCT2::RideUse
{
    static RideHistory _history;
    static RideTypeHistory _typeHistory;

    RideHistory& GetHistory()

    RideTypeHistory& GetTypeHistory()
    {
        return _typeHistory;
    }
} // namespace OpenRCT2::RideUse


/*****************************************************************************
 *
 * For a complete list of all authors, please refer to contributors.md
 * Interested in contributing? Visit http://example.com
 *
 *****************************************************************************/

#include "RideUseSystem.h"

namespace OpenRCT2::RideUse
{
    static RideHistory _history;
    static RideTypeHistory _typeHistory;

    RideHistory& GetHistory()
    {
        return _history;
    }

    RideTypeHistory& GetTypeHistory()
    {
        return


#include <fstream>
#include "Simbody.h"
#include "Exception.h"
#include "FileAdapter.h"
#include "TimeSeriesTable.h"
#include "XsensDataReader.h"

namespace OpenSim {

{
    return new XsensDataReader{*this};
}

DataAdapter::OutputTables
XsensDataReader::extendRead(const std::string& folderName) const {

    std::vector<std::ifstream*> imuStreams;
    std::vector<std::string> labels;
    // files specified by prefix + file name exist
    double dataRate = SimTK::NaN;

    int accIndex = -1;
    int gyroIndex = -1;
    int magIndex = -1;
    int rotationsIndex = -1;

    int n_imus = _settings.getProperty_ExperimentalSensors().size();
    int last_size = 1024;
    // Will read data into pre-allocated Matrices in-memory rather than appendRow
    // on the fly to avoid the overhead of
    SimTK::Matrix_<SimTK::Quaternion> rotationsData{ last_size, n_imus };
    SimTK::Matrix_<SimTK::Vec3> linearAccelerationData{ last_size, n_imus };
    SimTK::Matrix_<SimTK::Vec3> magneticHeadingData{ last_size, n_imus };
    SimTK::Matrix_<SimTK::Vec3> angularVelocityData{ last_size, n_imus };
    std::vector<double> times;
    times.resize(last_size);

    std::string prefix = _settings.get_trial_prefix();
    std::map<std::string, std::string> headersKeyValuePairs;
    for (int index = 0; index < n_imus; ++index) {
        std::string prefix = _settings.get_trial_prefix();
        const ExperimentalSensor& nextItem = _settings.get_ExperimentalSensors(index);
        auto fileName = folderName + prefix + nextItem.getName() +".txt";
        auto* nextStream = new std::ifstream{ fileName };
        OPENSIM_THROW_IF(!nextStream->good(),
            FileDoesNotExist,
            fileName);
        // Add imu name to labels
        labels.push_back(nextItem.get_name_in_model());
        // Add corresponding stream to imuStreams
        imuStreams.push_back(nextStream);

        // Skip lines to get to data
        std::string line;
        auto commentLine = true;
        std::getline(*nextStream, line);
        auto isCommentLine = [](std::string aline) {
            return aline.substr(0, 2) == "//";
        };
        std::vector<std::string> tokens;
        while (commentLine) {
            // Comment lines of arbitrary number on the form // "key":"value"
            tokens =FileAdapter::tokenize(line.substr(2), ":"); //Skip leading 2 chars tokenize on ':'
            // Will populate map from first file/imu only, assume they all have same format
            if (tokens.size() == 2 && index == 0) {
                // Put values in map
                headersKeyValuePairs[tokens[0]] = tokens[1];
            }
            std::getline(*nextStream, line);
            commentLine = isCommentLine(line);
        }
        // Mag_{X,Y,Z}, Mat on first non-comment line
        tokens = FileAdapter::tokenize(line, "\t");

        if (accIndex == -1) accIndex = find_index(tokens, "Acc_X");
        if (gyroIndex == -1) gyroIndex = find_index(tokens, "Gyr_X");
        if (magIndex == -1) magIndex = find_index(tokens, "Mag_X");
        if (rotationsIndex == -1) rotationsIndex = find_index(tokens, "Mat[1][1]");
    }
    // Compute data rate based on key/value pair if available
    std::map<std::string, std::string>::iterator it =
            headersKeyValuePairs.find("Update Rate");
    if (it != headersKeyValuePairs.end())
        dataRate = std::stod(it->second);
    else
        dataRate = 40.0; // Need confirmation from XSens as later files don't specify rate
    // internally keep track of what data was found in input files
    bool foundLinearAccelerationData = (accIndex != -1);
    bool foundMagneticHeadingData = (magIndex != -1);
    bool foundAngularVelocityData = (gyroIndex != -1);

    // If no Orientation data is available we'll abort completely
    OPENSIM_THROW_IF((rotationsIndex == -1), TableMissingHeader);

    // For all tables, will create row, stitch values from different files then append,time and timestep
    // are based on the first file
    bool done = false;
    double time = 0.0;
    double timeIncrement = 1 / dataRate;
    int rowNumber = 0;
    while (!done){
        // Make vectors one per table
        TimeSeriesTableQuaternion::RowVector
            orientation_row_vector{ n_imus, SimTK::Quaternion() };
        TimeSeriesTableVec3::RowVector
            accel_row_vector{ n_imus, SimTK::Vec3(SimTK::NaN) };
        TimeSeriesTableVec3::RowVector
            magneto_row_vector{ n_imus, SimTK::Vec3(SimTK::NaN) };
        TimeSeriesTableVec3::RowVector
            gyro_row_vector{ n_imus, SimTK::Vec3(SimTK::NaN) };
        // Cycle through the filles collating values
        int imu_index = 0;
        for (std::vector<std::ifstream*>::iterator it = imuStreams.begin();
            it != imuStreams.end();
            ++it, ++imu_index) {
            std::ifstream* nextStream = *it;
            // parse gyro info from imuStream
            std::vector<std::string> nextRow = FileAdapter::getNextLine(*nextStream, "\t\r");
            if (nextRow.empty()) {
                done = true;
                break;
            }
            if (foundLinearAccelerationData)
                accel_row_vector[imu_index] = SimTK::Vec3(std::stod(nextRow[accIndex]),
                    std::stod(nextRow[accIndex + 1]), std::stod(nextRow[accIndex + 2]));
            if (foundMagneticHeadingData)
                magneto_row_vector[imu_index] = SimTK::Vec3(std::stod(nextRow[magIndex]),
                    std::stod(nextRow[magIndex + 1]), std::stod(nextRow[magIndex + 2]));
            if (foundAngularVelocityData)
                gyro_row_vector[imu_index] = SimTK::Vec3(std::stod(nextRow[gyroIndex]),
                    std::stod(nextRow[gyroIndex + 1]), std::stod(nextRow[gyroIndex + 2]));
            // Create Mat33 then convert into Quaternion
            SimTK::Mat33 imu_matrix{ SimTK::NaN };
            int matrix_entry_index = 0;
            for (int mcol = 0; mcol < 3; mcol++) {
                for (int mrow = 0; mrow < 3; mrow++) {
                    imu_matrix[mrow][mcol] = std::stod(nextRow[rotationsIndex + matrix_entry_index]);
                    matrix_entry_index++;
                }
            }
            // Convert imu_matrix to Quaternion
            SimTK::Rotation imu_rotation{ imu_matrix };
            orientation_row_vector[imu_index] = imu_rotation.convertRotationToQuaternion();
        }
        if (done)
            break;
        // append to the tables
        times[rowNumber] = time;
        if (foundLinearAccelerationData)
            linearAccelerationData[rowNumber] =  accel_row_vector;
        if (foundMagneticHeadingData)
            magneticHeadingData[rowNumber] = magneto_row_vector;
        if (foundAngularVelocityData)
            angularVelocityData[rowNumber] = gyro_row_vector;
        rotationsData[rowNumber] = orientation_row_vector;
        time += timeIncrement;
        rowNumber++;
        if (std::remainder(rowNumber, last_size) == 0) {
            // resize all Data/Matrices, double the size  while keeping data
            int newSize = last_size*2;
            times.resize(newSize);
            // Repeat for Data matrices in use
            if (foundLinearAccelerationData) linearAccelerationData.resizeKeep(newSize, n_imus);
            if (foundMagneticHeadingData) magneticHeadingData.resizeKeep(newSize, n_imus);
            if (foundAngularVelocityData) angularVelocityData.resizeKeep(newSize, n_imus);
            rotationsData.resizeKeep(newSize, n_imus);
            last_size = newSize;
        }
    }
    // Trim Matrices in use to actual data and move into tables
    times.resize(rowNumber);
    // Repeat for Data matrices in use and create Tables from them or size 0 for empty
    linearAccelerationData.resizeKeep(foundLinearAccelerationData? rowNumber : 0,
        n_imus);
    magneticHeadingData.resizeKeep(foundMagneticHeadingData? rowNumber : 0,
            n_imus);
    angularVelocityData.resizeKeep(foundAngularVelocityData? rowNumber :0,
        n_imus);
    rotationsData.resizeKeep(rowNumber, n_imus);

    // Now create the tables from matrices
    // Create 4 tables for Rotations, LinearAccelerations, AngularVelocity, MagneticHeading
    // Tables could be empty if data is not present in file(s)
    DataAdapter::OutputTables tables = createTablesFromMatrices(dataRate, labels, times,
        rotationsData, linearAccelerationData, magneticHeadingData, angularVelocityData);
    return tables;
}

int XsensDataReader::find_index(std::vector<std::string>& tokens, const std::string& keyToMatch) {
    int returnIndex = -1;
    std::vector<std::string>::iterator it = std::find(tokens.begin(), tokens.end(), keyToMatch);
    if (it != tokens.end())
        returnIndex = static_cast<int>(std::distance(tokens.begin(), it));
    return returnIndex;
}

}


{
    return new XsensDataReader{*this};
}

#include <fstream>
#include "Simbody.h"
#include "Exception.h"
#include "FileAdapter.h"
#include "TimeSeriesTable.h"
#include "XsensDataReader.h"

namespace OpenSim {

XsensDataReader* XsensDataReader::clone() const

DataAdapter::OutputTables
XsensDataReader::extendRead(const std::string& folderName) const {

    std::vector<std::ifstream*> imuStreams;
    std::vector<std::string> labels;
    // files specified by prefix + file name exist
    double dataRate = SimTK::NaN;

    int accIndex = -1;
    int gyroIndex = -1;
    int magIndex = -1;
    int rotationsIndex = -1;

    int n_imus = _settings.getProperty_ExperimentalSensors().size();
    int last_size = 1024;
    // Will read data into pre-allocated Matrices in-memory rather than appendRow
    // on the fly to avoid the overhead of
    SimTK::Matrix_<SimTK::Quaternion> rotationsData{ last_size, n_imus };
    SimTK::Matrix_<SimTK::Vec3> linearAccelerationData{ last_size, n_imus };
    SimTK::Matrix_<SimTK::Vec3> magneticHeadingData{ last_size, n_imus };
    SimTK::Matrix_<SimTK::Vec3> angularVelocityData{ last_size, n_imus };
    std::vector<double> times;
    times.resize(last_size);

    std::string prefix = _settings.get_trial_prefix();
    std::map<std::string, std::string> headersKeyValuePairs;
    for (int index = 0; index < n_imus; ++index) {
        std::string prefix = _settings.get_trial_prefix();
        const ExperimentalSensor& nextItem = _settings.get_ExperimentalSensors(index);
        auto fileName = folderName + prefix + nextItem.getName() +".txt";
        auto* nextStream = new std::ifstream{ fileName };
        OPENSIM_THROW_IF(!nextStream->good(),
            FileDoesNotExist,
            fileName);
        // Add imu name to labels
        labels.push_back(nextItem.get_name_in_model());
        // Add corresponding stream to imuStreams
        imuStreams.push_back(nextStream);

        // Skip lines to get to data
        std::string line;
        auto commentLine = true;
        std::getline(*nextStream, line);
        auto isCommentLine = [](std::string aline) {
            return aline.substr(0, 2) == "//";
        };
        std::vector<std::string> tokens;
        while (commentLine) {
            // Comment lines of arbitrary number on the form // "key":"value"
            tokens =FileAdapter::tokenize(line.substr(2), ":"); //Skip leading 2 chars tokenize on ':'
            // Will populate map from first file/imu only, assume they all have same format
            if (tokens.size() == 2 && index == 0) {
                // Put values in map
                headersKeyValuePairs[tokens[0]] = tokens[1];
            }
            std::getline(*nextStream, line);
            commentLine = isCommentLine(line);
        }
        // Mag_{X,Y,Z}, Mat on first non-comment line
        tokens = FileAdapter::tokenize(line, "\t");

        if (accIndex == -1) accIndex = find_index(tokens, "Acc_X");
        if (gyroIndex == -1) gyroIndex = find_index(tokens, "Gyr_X");
        if (magIndex == -1) magIndex = find_index(tokens, "Mag_X");
        if (rotationsIndex == -1) rotationsIndex = find_index(tokens, "Mat[1][1]");
    }
    // Compute data rate based on key/value pair if available
    std::map<std::string, std::string>::iterator it =
            headersKeyValuePairs.find("Update Rate");
    if (it != headersKeyValuePairs.end())
        dataRate = std::stod(it->second);
    else
        dataRate = 40.0; // Need confirmation from XSens as later files don't specify rate
    // internally keep track of what data was found in input files
    bool foundLinearAccelerationData = (accIndex != -1);
    bool foundMagneticHeadingData = (magIndex != -1);
    bool foundAngularVelocityData = (gyroIndex != -1);

    // If no Orientation data is available we'll abort completely
    OPENSIM_THROW_IF((rotationsIndex == -1), TableMissingHeader);

    // For all tables, will create row, stitch values from different files then append,time and timestep
    // are based on the first file
    bool done = false;
    double time = 0.0;
    double timeIncrement = 1 / dataRate;
    int rowNumber = 0;
    while (!done){
        // Make vectors one per table
        TimeSeriesTableQuaternion::RowVector
            orientation_row_vector{ n_imus, SimTK::Quaternion() };
        TimeSeriesTableVec3::RowVector
            accel_row_vector{ n_imus, SimTK::Vec3(SimTK::NaN) };
        TimeSeriesTableVec3::RowVector
            magneto_row_vector{ n_imus, SimTK::Vec3(SimTK::NaN) };
        TimeSeriesTableVec3::RowVector
            gyro_row_vector{ n_imus, SimTK::Vec3(SimTK::NaN) };
        // Cycle through the filles collating values
        int imu_index = 0;
        for (std::vector<std::ifstream*>::iterator it = imuStreams.begin();
            it != imuStreams.end();
            ++it, ++imu_index) {
            std::ifstream* nextStream = *it;
            // parse gyro info from imuStream
            std::vector<std::string> nextRow = FileAdapter::getNextLine(*nextStream, "\t\r");
            if (nextRow.empty()) {
                done = true;
                break;
            }
            if (foundLinearAccelerationData)
                accel_row_vector[imu_index] = SimTK::Vec3(std::stod(nextRow[accIndex]),
                    std::stod(nextRow[accIndex + 1]), std::stod(nextRow[accIndex + 2]));
            if (foundMagneticHeadingData)
                magneto_row_vector[imu_index] = SimTK::Vec3(std::stod(nextRow[magIndex]),
                    std::stod(nextRow[magIndex + 1]), std::stod(nextRow[magIndex + 2]));
            if (foundAngularVelocityData)
                gyro_row_vector[imu_index] = SimTK::Vec3(std::stod(nextRow[gyroIndex]),
                    std::stod(nextRow[gyroIndex + 1]), std::stod(nextRow[gyroIndex + 2]));
            // Create Mat33 then convert into Quaternion
            SimTK::Mat33 imu_matrix{ SimTK::NaN };
            int matrix_entry_index = 0;
            for (int mcol = 0; mcol < 3; mcol++) {
                for (int mrow = 0; mrow < 3; mrow++) {
                    imu_matrix[mrow][mcol] = std::stod(nextRow[rotationsIndex + matrix_entry_index]);
                    matrix_entry_index++;
                }
            }
            // Convert imu_matrix to Quaternion
            SimTK::Rotation imu_rotation{ imu_matrix };
            orientation_row_vector[imu_index] = imu_rotation.convertRotationToQuaternion();
        }
        if (done)
            break;
        // append to the tables
        times[rowNumber] = time;
        if (foundLinearAccelerationData)
            linearAccelerationData[rowNumber] =  accel_row_vector;
        if (foundMagneticHeadingData)
            magneticHeadingData[rowNumber] = magneto_row_vector;
        if (foundAngularVelocityData)
            angularVelocityData[rowNumber] = gyro_row_vector;
        rotationsData[rowNumber] = orientation_row_vector;
        time += timeIncrement;
        rowNumber++;
        if (std::remainder(rowNumber, last_size) == 0) {
            // resize all Data/Matrices, double the size  while keeping data
            int newSize = last_size*2;
            times.resize(newSize);
            // Repeat for Data matrices in use
            if (foundLinearAccelerationData) linearAccelerationData.resizeKeep(newSize, n_imus);
            if (foundMagneticHeadingData) magneticHeadingData.resizeKeep(newSize, n_imus);
            if (foundAngularVelocityData) angularVelocityData.resizeKeep(newSize, n_imus);
            rotationsData.resizeKeep(newSize, n_imus);
            last_size = newSize;
        }
    }
    // Trim Matrices in use to actual data and move into tables
    times.resize(rowNumber);
    // Repeat for Data matrices in use and create Tables from them or size 0 for empty
    linearAccelerationData.resizeKeep(foundLinearAccelerationData? rowNumber : 0,
        n_imus);
    magneticHeadingData.resizeKeep(foundMagneticHeadingData? rowNumber : 0,
            n_imus);
    angularVelocityData.resizeKeep(foundAngularVelocityData? rowNumber :0,
        n_imus);
    rotationsData.resizeKeep(rowNumber, n_imus);

    // Now create the tables from matrices
    // Create 4 tables for Rotations, LinearAccelerations, AngularVelocity, MagneticHeading
    // Tables could be empty if data is not present in file(s)
    DataAdapter::OutputTables tables = createTablesFromMatrices(dataRate, labels, times,
        rotationsData, linearAccelerationData, magneticHeadingData, angularVelocityData);
    return tables;
}

int XsensDataReader::find_index(std::vector<std::string>& tokens, const std::string& keyToMatch) {
    int returnIndex = -1;
    std::vector<std::string>::iterator it = std::find(tokens.begin(), tokens.end(), keyToMatch);
    if (it != tokens.end())
        returnIndex = static_cast<int>(std::distance(tokens.begin(), it));
    return returnIndex;
}

}


#include <fstream>
#include "Simbody.h"
#include "Exception.h"
#include "FileAdapter.h"
#include "TimeSeriesTable.h"
#include "XsensDataReader.h"

namespace OpenSim {

XsensDataReader* XsensDataReader::clone() const {
    return new XsensDataReader{*this};
}

DataAdapter::OutputTables
XsensDataReader::extendRead(const std::string& folderName) const {

    std::vector<std::ifstream*> imuStreams;
    std::vector<std::string> labels;
    // files specified by prefix + file name exist
    double dataRate = SimTK::NaN;

    int accIndex = -1;
    int gyroIndex = -1;
    int magIndex = -1;
    int rotationsIndex = -1;

    int n_imus = _settings.getProperty_ExperimentalSensors().size();
    int last_size = 1024;
    // Will read data into pre-allocated Matrices in-memory rather than appendRow
    // on the fly to avoid the overhead of
    SimTK::Matrix_<SimTK::Quaternion> rotat

    SimTK::Matrix_<SimTK::Vec3> linearAccelerationData{ last_size, n_imus };
    SimTK::Matrix_<SimTK::Vec3> magneticHeadingData{ last_size, n_imus };
    SimTK::Matrix_<SimTK::Vec3> angularVelocityData{ last_size, n_imus };
    std::vector<double> times;
    times.resize(last_size);

    std::string prefix = _settings.get_trial_prefix();
    std::map<std::string, std::string> headersKeyValuePairs;
    for (int index = 0; index < n_imus; ++index) {
        std::string prefix = _settings.get_trial_prefix();
        const ExperimentalSensor& nextItem = _settings.get_ExperimentalSensors(index);
        auto fileName = folderName + prefix + nextItem.getName() +".txt";
        auto* nextStream = new std::ifstream{ fileName };
        OPENSIM_THROW_IF(!nextStream->good(),
            FileDoesNotExist,
            fileName);
        // Add imu name to labels
        labels.push_back(nextItem.get_name_in_model());
        // Add corresponding stream to imuStreams
        imuStreams.push_back(nextStream);

        // Skip lines to get to data
        std::string line;
        auto commentLine = true;
        std::getline(*nextStream, line);
        auto isCommentLine = [](std::string aline) {
            return aline.substr(0, 2) == "//";
        };
        std::vector<std::string> tokens;
        while (commentLine) {
            // Comment lines of arbitrary number on the form // "key":"value"
            tokens =FileAdapter::tokenize(line.substr(2), ":"); //Skip leading 2 chars tokenize on ':'
            // Will populate map from first file/imu only, assume they all have same format
            if (tokens.size() == 2 && index == 0) {
                // Put values in map
                headersKeyValuePairs[tokens[0]] = tokens[1];
            }
            std::getline(*nextStream, line);
            commentLine = isCommentLine(line);
        }
        // Mag_{X,Y,Z}, Mat on first non-comment line
        tokens = FileAdapter::tokenize(line, "\t");

        if (accIndex == -1) accIndex = find_index(tokens, "Acc_X");
        if (gyroIndex == -1) gyroIndex = find_index(tokens, "Gyr_X");
        if (magIndex == -1) magIndex = find_index(tokens, "Mag_X");
        if (rotationsIndex == -1) rotationsIndex = find_index(tokens, "Mat[1][1]");
    }
    // Compute data rate based on key/value pair if available
    std::map<std::string, std::string>::iterator it =
            headersKeyValuePairs.find("Update Rate");
    if (it != headersKeyValuePairs.end())
        dataRate = std::stod(it->second);
    else
        dataRate = 40.0; // Need confirmation from XSens as later files don't specify rate
    // internally keep track of what data was found in input files
    bool foundLinearAccelerationData = (accIndex != -1);
    bool foundMagneticHeadingData = (magIndex != -1);
    bool foundAngularVelocityData = (gyroIndex != -1);

    // If no Orientation data is available we'll abort completely
    OPENSIM_THROW_IF((rotationsIndex == -1), TableMissingHeader);

    // For all tables, will create row, stitch values from different files then append,time and timestep
    // are based on the first file
    bool done = false;
    double time = 0.0;
    double timeIncrement = 1 / dataRate;
    int rowNumber = 0;
    while (!done){
        // Make vectors one per table
        TimeSeriesTableQuaternion::RowVector
            orientation_row_vector{ n_imus, SimTK::Quaternion() };
        TimeSeriesTableVec3::RowVector
            accel_row_vector{ n_imus, SimTK::Vec3(SimTK::NaN) };
        TimeSeriesTableVec3::RowVector
            magneto_row_vector{ n_imus, SimTK::Vec3(SimTK::NaN) };
        TimeSeriesTableVec3::RowVector
            gyro_row_vector{ n_imus, SimTK::Vec3(SimTK::NaN) };
        // Cycle through the filles collating values
        int imu_index = 0;
        for (std::vector<std::ifstream*>::iterator it = imuStreams.begin();
            it != imuStreams.end();
            ++it, ++imu_index) {
            std::ifstream* nextStream = *it;
            // parse gyro info from imuStream
            std::vector<std::string> nextRow = FileAdapter::getNextLine(*nextStream, "\t\r");
            if (nextRow.empty()) {
                done = true;
                break;
            }
            if (foundLinearAccelerationData)
                accel_row_vector[imu_index] = SimTK::Vec3(std::stod(nextRow[accIndex]),
                    std::stod(nextRow[accIndex + 1]), std::stod(nextRow[accIndex + 2]));
            if (foundMagneticHeadingData)
                magneto_row_vector[imu_index] = SimTK::Vec3(std::stod(nextRow[magIndex]),
                    std::stod(nextRow[magIndex + 1]), std::stod(nextRow[magIndex + 2]));
            if (foundAngularVelocityData)
                gyro_row_vector[imu_index] = SimTK::Vec3(std::stod(nextRow[gyroIndex]),
                    std::stod(nextRow[gyroIndex + 1]), std::stod(nextRow[gyroIndex + 2]));
            // Create Mat33 then convert into Quaternion
            SimTK::Mat33 imu_matrix{ SimTK::NaN };
            int matrix_entry_index = 0;
            for (int mcol = 0; mcol < 3; mcol++) {
                for (int mrow = 0; mrow < 3; mrow++) {
                    imu_matrix[mrow][mcol] = std::stod(nextRow[rotationsIndex + matrix_entry_index]);
                    matrix_entry_index++;
                }
            }
            // Convert imu_matrix to Quaternion
            SimTK::Rotation imu_rotation{ imu_matrix };
            orientation_row_vector[imu_index] = imu_rotation.convertRotationToQuaternion();
        }
        if (done)
            break;
        // append to the tables
        times[rowNumber] = time;
        if (foundLinearAccelerationData)
            linearAccelerationData[rowNumber] =  accel_row_vector;
        if (foundMagneticHeadingData)
            magneticHeadingData[rowNumber] = magneto_row_vector;
        if (foundAngularVelocityData)
            angularVelocityData[rowNumber] = gyro_row_vector;
        rotationsData[rowNumber] = orientation_row_vector;
        time += timeIncrement;
        rowNumber++;
        if (std::remainder(rowNumber, last_size) == 0) {
            // resize all Data/Matrices, double the size  while keeping data
            int newSize = last_size*2;
            times.resize(newSize);
            // Repeat for Data matrices in use
            if (foundLinearAccelerationData) linearAccelerationData.resizeKeep(newSize, n_imus);
            if (foundMagneticHeadingData) magneticHeadingData.resizeKeep(newSize, n_imus);
            if (foundAngularVelocityData) angularVelocityData.resizeKeep(newSize, n_imus);
            rotationsData.resizeKeep(newSize, n_imus);
            last_size = newSize;
        }
    }
    // Trim Matrices in use to actual data and move into tables
    times.resize(rowNumber);
    // Repeat for Data matrices in use and create Tables from them or size 0 for empty
    linearAccelerationData.resizeKeep(foundLinearAccelerationData? rowNumber : 0,
        n_imus);
    magneticHeadingData.resizeKeep(foundMagneticHeadingData? rowNumber : 0,
            n_imus);
    angularVelocityData.resizeKeep(foundAngularVelocityData? rowNumber :0,
        n_imus);
    rotationsData.resizeKeep(rowNumber, n_imus);

    // Now create the tables from matrices
    // Create 4 tables for Rotations, LinearAccelerations, AngularVelocity, MagneticHeading
    // Tables could be empty if data is not present in file(s)
    DataAdapter::OutputTables tables = createTablesFromMatrices(dataRate, labels, times,
        rotationsData, linearAccelerationData, magneticHeadingData, angularVelocityData);
    return tables;
}

int XsensDataReader::find_index(std::vector<std::string>& tokens, const std::string& keyToMatch) {
    int returnIndex = -1;
    std::vector<std::string>::iterator it = std::find(tokens.begin(), tokens.end(), keyToMatch);
    if (it != tokens.end())
        returnIndex = static_cast<int>(std::distance(tokens.begin(), it));
    return returnIndex;
}

}


void UnlockObjectAction::execute(Object &actor, ActionContext &ctx, float dt)

/*
 *
 */

#include "unlockobject.h"

namespace reone {

namespace game {

{
    // TODO: implement

    complete();
}

} // namespace game

} // namespace reone


{
    // TODO: implement

    complete();
}

/*
 *
 */

#include "unlockobject.h"

namespace reone {

namespace game {

void UnlockObjectAction::execute(Object &actor, ActionContext &ctx, float dt)

} // namespace game

} // namespace reone


/*
 *
 */

#include "unlockobject.h"

namespace reone {

namespace game {

void UnlockObjectAction::execute(Object &actor, ActionContext &ctx, float dt) {
    // TODO: implement

    co

}

} // namespace game

} // namespace reone