<commit_before>//  Copyright (c) 2013, Facebook, Inc.  All rights reserved.
//  This source code is licensed under the BSD-style license found in the
//  LICENSE file in the root directory of this source tree. An additional grant
//  of patent rights can be found in the PATENTS file in the same directory.
//
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
//
// We recover the contents of the descriptor from the other files we find.
// (1) Any log files are first converted to tables
// (2) We scan every table to compute
//     (a) smallest/largest for the table
//     (b) largest sequence number in the table
// (3) We generate descriptor contents:
//      - log number is set to zero
//      - next-file-number is set to 1 + largest file number we found
//      - last-sequence-number is set to largest sequence# found across
//        all tables (see 2c)
//      - compaction pointers are cleared
//      - every table file is added at level 0
//
// Possible optimization 1:
//   (a) Compute total size and use to pick appropriate max-level M
//   (b) Sort tables by largest sequence# in the table
//   (c) For each table: if it overlaps earlier table, place in level-0,
//       else place in level-M.
// Possible optimization 2:
//   Store per-table metadata (smallest, largest, largest-seq#, ...)
//   in the table's meta section to speed up ScanTable.

#ifndef ROCKSDB_LITE

#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif

#include <inttypes.h>
#include "db/builder.h"
#include "db/db_impl.h"
#include "db/dbformat.h"
#include "db/filename.h"
#include "db/log_reader.h"
#include "db/log_writer.h"
#include "db/memtable.h"
#include "db/table_cache.h"
#include "db/version_edit.h"
#include "db/writebuffer.h"
#include "db/write_batch_internal.h"
#include "rocksdb/comparator.h"
#include "rocksdb/db.h"
#include "rocksdb/env.h"
#include "rocksdb/options.h"
#include "rocksdb/immutable_options.h"
#include "util/scoped_arena_iterator.h"

namespace rocksdb {

namespace {

class Repairer {
 public:
  Repairer(const std::string& dbname, const Options& options)
      : dbname_(dbname),
        env_(options.env),
        icmp_(options.comparator),
        options_(SanitizeOptions(dbname, &icmp_, options)),
        ioptions_(options_),
        raw_table_cache_(
            // TableCache can be small since we expect each table to be opened
            // once.
            NewLRUCache(10, options_.table_cache_numshardbits)),
        next_file_number_(1) {
    GetIntTblPropCollectorFactory(options, &int_tbl_prop_collector_factories_);

    table_cache_ =
        new TableCache(ioptions_, env_options_, raw_table_cache_.get());
    edit_ = new VersionEdit();
  }

  ~Repairer() {
    delete table_cache_;
    raw_table_cache_.reset();
    delete edit_;
  }

  Status Run() {
    Status status = FindFiles();
    if (status.ok()) {
      ConvertLogFilesToTables();
      ExtractMetaData();
      status = WriteDescriptor();
    }
    if (status.ok()) {
      uint64_t bytes = 0;
      for (size_t i = 0; i < tables_.size(); i++) {
        bytes += tables_[i].meta.fd.GetFileSize();
      }
      Log(InfoLogLevel::WARN_LEVEL, options_.info_log,
          "**** Repaired rocksdb %s; "
          "recovered %zu files; %" PRIu64
          "bytes. "
          "Some data may have been lost. "
          "****",
          dbname_.c_str(), tables_.size(), bytes);
    }
    return status;
  }

 private:
  struct TableInfo {
    FileMetaData meta;
    SequenceNumber min_sequence;
    SequenceNumber max_sequence;
  };

  std::string const dbname_;
  Env* const env_;
  const InternalKeyComparator icmp_;
  std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
      int_tbl_prop_collector_factories_;
  const Options options_;
  const ImmutableCFOptions ioptions_;
  std::shared_ptr<Cache> raw_table_cache_;
  TableCache* table_cache_;
  VersionEdit* edit_;

  std::vector<std::string> manifests_;
  std::vector<FileDescriptor> table_fds_;
  std::vector<uint64_t> logs_;
  std::vector<TableInfo> tables_;
  uint64_t next_file_number_;
  const EnvOptions env_options_;

  Status FindFiles() {
    std::vector<std::string> filenames;
    bool found_file = false;
    for (uint32_t path_id = 0; path_id < options_.db_paths.size(); path_id++) {
      Status status =
          env_->GetChildren(options_.db_paths[path_id].path, &filenames);
      if (!status.ok()) {
        return status;
      }
      if (!filenames.empty()) {
        found_file = true;
      }

      uint64_t number;
      FileType type;
      for (size_t i = 0; i < filenames.size(); i++) {
        if (ParseFileName(filenames[i], &number, &type)) {
          if (type == kDescriptorFile) {
            assert(path_id == 0);
            manifests_.push_back(filenames[i]);
          } else {
            if (number + 1 > next_file_number_) {
              next_file_number_ = number + 1;
            }
            if (type == kLogFile) {
              assert(path_id == 0);
              logs_.push_back(number);
            } else if (type == kTableFile) {
              table_fds_.emplace_back(number, path_id, 0);
            } else {
              // Ignore other files
            }
          }
        }
      }
    }
    if (!found_file) {
      return Status::Corruption(dbname_, "repair found no files");
    }
    return Status::OK();
  }

  void ConvertLogFilesToTables() {
    for (size_t i = 0; i < logs_.size(); i++) {
      std::string logname = LogFileName(dbname_, logs_[i]);
      Status status = ConvertLogToTable(logs_[i]);
      if (!status.ok()) {
        Log(InfoLogLevel::WARN_LEVEL, options_.info_log,
            "Log #%" PRIu64 ": ignoring conversion error: %s", logs_[i],
            status.ToString().c_str());
      }
      ArchiveFile(logname);
    }
  }

  Status ConvertLogToTable(uint64_t log) {
    struct LogReporter : public log::Reader::Reporter {
      Env* env;
      std::shared_ptr<Logger> info_log;
      uint64_t lognum;
      virtual void Corruption(size_t bytes, const Status& s) override {
        // We print error messages for corruption, but continue repairing.
        Log(InfoLogLevel::ERROR_LEVEL, info_log,
            "Log #%" PRIu64 ": dropping %d bytes; %s", lognum,
            static_cast<int>(bytes), s.ToString().c_str());
      }
    };

    // Open the log file
    std::string logname = LogFileName(dbname_, log);
    unique_ptr<SequentialFile> lfile;
    Status status = env_->NewSequentialFile(logname, &lfile, env_options_);
    if (!status.ok()) {
      return status;
    }

    // Create the log reader.
    LogReporter reporter;
    reporter.env = env_;
    reporter.info_log = options_.info_log;
    reporter.lognum = log;
    // We intentially make log::Reader do checksumming so that
    // corruptions cause entire commits to be skipped instead of
    // propagating bad information (like overly large sequence
    // numbers).
    log::Reader reader(std::move(lfile), &reporter, false/*do not checksum*/,
                       0/*initial_offset*/);

    // Read all the records and add to a memtable
    std::string scratch;
    Slice record;
    WriteBatch batch;
    WriteBuffer wb(options_.db_write_buffer_size);
    MemTable* mem = new MemTable(icmp_, ioptions_,
                                 MutableCFOptions(options_, ioptions_), &wb);
    auto cf_mems_default = new ColumnFamilyMemTablesDefault(mem);
    mem->Ref();
    int counter = 0;
    while (reader.ReadRecord(&record, &scratch)) {
      if (record.size() < 12) {
        reporter.Corruption(
            record.size(), Status::Corruption("log record too small"));
        continue;
      }
      WriteBatchInternal::SetContents(&batch, record);
      status = WriteBatchInternal::InsertInto(&batch, cf_mems_default);
      if (status.ok()) {
        counter += WriteBatchInternal::Count(&batch);
      } else {
        Log(InfoLogLevel::WARN_LEVEL,
            options_.info_log, "Log #%" PRIu64 ": ignoring %s", log,
            status.ToString().c_str());
        status = Status::OK();  // Keep going with rest of file
      }
    }

    // Do not record a version edit for this conversion to a Table
    // since ExtractMetaData() will also generate edits.
    FileMetaData meta;
    meta.fd = FileDescriptor(next_file_number_++, 0, 0);
    {
      ReadOptions ro;
      ro.total_order_seek = true;
      Arena arena;
      ScopedArenaIterator iter(mem->NewIterator(ro, &arena));
      status = BuildTable(dbname_, env_, ioptions_, env_options_, table_cache_,
                          iter.get(), &meta, icmp_,
                          &int_tbl_prop_collector_factories_, 0, 0,
                          kNoCompression, CompressionOptions());
    }
    delete mem->Unref();
    delete cf_mems_default;
    mem = nullptr;
    if (status.ok()) {
      if (meta.fd.GetFileSize() > 0) {
        table_fds_.push_back(meta.fd);
      }
    }
    Log(InfoLogLevel::INFO_LEVEL, options_.info_log,
        "Log #%" PRIu64 ": %d ops saved to Table #%" PRIu64 " %s",
        log, counter, meta.fd.GetNumber(), status.ToString().c_str());
    return status;
  }

  void ExtractMetaData() {
    for (size_t i = 0; i < table_fds_.size(); i++) {
      TableInfo t;
      t.meta.fd = table_fds_[i];
      Status status = ScanTable(&t);
      if (!status.ok()) {
        std::string fname = TableFileName(
            options_.db_paths, t.meta.fd.GetNumber(), t.meta.fd.GetPathId());
        char file_num_buf[kFormatFileNumberBufSize];
        FormatFileNumber(t.meta.fd.GetNumber(), t.meta.fd.GetPathId(),
                         file_num_buf, sizeof(file_num_buf));
        Log(InfoLogLevel::WARN_LEVEL, options_.info_log,
            "Table #%s: ignoring %s", file_num_buf,
            status.ToString().c_str());
        ArchiveFile(fname);
      } else {
        tables_.push_back(t);
      }
    }
  }

  Status ScanTable(TableInfo* t) {
    std::string fname = TableFileName(options_.db_paths, t->meta.fd.GetNumber(),
                                      t->meta.fd.GetPathId());
    int counter = 0;
    uint64_t file_size;
    Status status = env_->GetFileSize(fname, &file_size);
    t->meta.fd = FileDescriptor(t->meta.fd.GetNumber(), t->meta.fd.GetPathId(),
                                file_size);
    if (status.ok()) {
      Iterator* iter = table_cache_->NewIterator(
          ReadOptions(), env_options_, icmp_, t->meta.fd);
      bool empty = true;
      ParsedInternalKey parsed;
      t->min_sequence = 0;
      t->max_sequence = 0;
      for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
        Slice key = iter->key();
        if (!ParseInternalKey(key, &parsed)) {
          Log(InfoLogLevel::ERROR_LEVEL,
              options_.info_log, "Table #%" PRIu64 ": unparsable key %s",
              t->meta.fd.GetNumber(), EscapeString(key).c_str());
          continue;
        }

        counter++;
        if (empty) {
          empty = false;
          t->meta.smallest.DecodeFrom(key);
        }
        t->meta.largest.DecodeFrom(key);
        if (parsed.sequence < t->min_sequence) {
          t->min_sequence = parsed.sequence;
        }
        if (parsed.sequence > t->max_sequence) {
          t->max_sequence = parsed.sequence;
        }
      }
      if (!iter->status().ok()) {
        status = iter->status();
      }
      delete iter;
    }
    Log(InfoLogLevel::INFO_LEVEL,
        options_.info_log, "Table #%" PRIu64 ": %d entries %s",
        t->meta.fd.GetNumber(), counter, status.ToString().c_str());
    return status;
  }

  Status WriteDescriptor() {
    std::string tmp = TempFileName(dbname_, 1);
    unique_ptr<WritableFile> file;
    Status status = env_->NewWritableFile(
        tmp, &file, env_->OptimizeForManifestWrite(env_options_));
    if (!status.ok()) {
      return status;
    }

    SequenceNumber max_sequence = 0;
    for (size_t i = 0; i < tables_.size(); i++) {
      if (max_sequence < tables_[i].max_sequence) {
        max_sequence = tables_[i].max_sequence;
      }
    }

    edit_->SetComparatorName(icmp_.user_comparator()->Name());
    edit_->SetLogNumber(0);
    edit_->SetNextFile(next_file_number_);
    edit_->SetLastSequence(max_sequence);

    for (size_t i = 0; i < tables_.size(); i++) {
      // TODO(opt): separate out into multiple levels
      const TableInfo& t = tables_[i];
      edit_->AddFile(0, t.meta.fd.GetNumber(), t.meta.fd.GetPathId(),
                     t.meta.fd.GetFileSize(), t.meta.smallest, t.meta.largest,
                     t.min_sequence, t.max_sequence);
    }

    //fprintf(stderr, "NewDescriptor:\n%s\n", edit_.DebugString().c_str());
    {
      log::Writer log(std::move(file));
      std::string record;
      edit_->EncodeTo(&record);
      status = log.AddRecord(record);
    }

    if (!status.ok()) {
      env_->DeleteFile(tmp);
    } else {
      // Discard older manifests
      for (size_t i = 0; i < manifests_.size(); i++) {
        ArchiveFile(dbname_ + "/" + manifests_[i]);
      }

      // Install new manifest
      status = env_->RenameFile(tmp, DescriptorFileName(dbname_, 1));
      if (status.ok()) {
        status = SetCurrentFile(env_, dbname_, 1, nullptr);
      } else {
        env_->DeleteFile(tmp);
      }
    }
    return status;
  }

  void ArchiveFile(const std::string& fname) {
    // Move into another directory.  E.g., for
    //    dir/foo
    // rename to
    //    dir/lost/foo
    const char* slash = strrchr(fname.c_str(), '/');
    std::string new_dir;
    if (slash != nullptr) {
      new_dir.assign(fname.data(), slash - fname.data());
    }
    new_dir.append("/lost");
    env_->CreateDir(new_dir);  // Ignore error
    std::string new_file = new_dir;
    new_file.append("/");
    new_file.append((slash == nullptr) ? fname.c_str() : slash + 1);
    Status s = env_->RenameFile(fname, new_file);
    Log(InfoLogLevel::INFO_LEVEL,
        options_.info_log, "Archiving %s: %s\n",
        fname.c_str(), s.ToString().c_str());
  }
};
}  // namespace

Status RepairDB(const std::string& dbname, const Options& options) {
  Repairer repairer(dbname, options);
  return repairer.Run();
}

}  // namespace rocksdb

#endif  // ROCKSDB_LITE
<commit_msg>Enabling checksum in repair db as it should have been.<commit_after>//  Copyright (c) 2013, Facebook, Inc.  All rights reserved.
//  This source code is licensed under the BSD-style license found in the
//  LICENSE file in the root directory of this source tree. An additional grant
//  of patent rights can be found in the PATENTS file in the same directory.
//
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
//
// We recover the contents of the descriptor from the other files we find.
// (1) Any log files are first converted to tables
// (2) We scan every table to compute
//     (a) smallest/largest for the table
//     (b) largest sequence number in the table
// (3) We generate descriptor contents:
//      - log number is set to zero
//      - next-file-number is set to 1 + largest file number we found
//      - last-sequence-number is set to largest sequence# found across
//        all tables (see 2c)
//      - compaction pointers are cleared
//      - every table file is added at level 0
//
// Possible optimization 1:
//   (a) Compute total size and use to pick appropriate max-level M
//   (b) Sort tables by largest sequence# in the table
//   (c) For each table: if it overlaps earlier table, place in level-0,
//       else place in level-M.
// Possible optimization 2:
//   Store per-table metadata (smallest, largest, largest-seq#, ...)
//   in the table's meta section to speed up ScanTable.

#ifndef ROCKSDB_LITE

#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#endif

#include <inttypes.h>
#include "db/builder.h"
#include "db/db_impl.h"
#include "db/dbformat.h"
#include "db/filename.h"
#include "db/log_reader.h"
#include "db/log_writer.h"
#include "db/memtable.h"
#include "db/table_cache.h"
#include "db/version_edit.h"
#include "db/writebuffer.h"
#include "db/write_batch_internal.h"
#include "rocksdb/comparator.h"
#include "rocksdb/db.h"
#include "rocksdb/env.h"
#include "rocksdb/options.h"
#include "rocksdb/immutable_options.h"
#include "util/scoped_arena_iterator.h"

namespace rocksdb {

namespace {

class Repairer {
 public:
  Repairer(const std::string& dbname, const Options& options)
      : dbname_(dbname),
        env_(options.env),
        icmp_(options.comparator),
        options_(SanitizeOptions(dbname, &icmp_, options)),
        ioptions_(options_),
        raw_table_cache_(
            // TableCache can be small since we expect each table to be opened
            // once.
            NewLRUCache(10, options_.table_cache_numshardbits)),
        next_file_number_(1) {
    GetIntTblPropCollectorFactory(options, &int_tbl_prop_collector_factories_);

    table_cache_ =
        new TableCache(ioptions_, env_options_, raw_table_cache_.get());
    edit_ = new VersionEdit();
  }

  ~Repairer() {
    delete table_cache_;
    raw_table_cache_.reset();
    delete edit_;
  }

  Status Run() {
    Status status = FindFiles();
    if (status.ok()) {
      ConvertLogFilesToTables();
      ExtractMetaData();
      status = WriteDescriptor();
    }
    if (status.ok()) {
      uint64_t bytes = 0;
      for (size_t i = 0; i < tables_.size(); i++) {
        bytes += tables_[i].meta.fd.GetFileSize();
      }
      Log(InfoLogLevel::WARN_LEVEL, options_.info_log,
          "**** Repaired rocksdb %s; "
          "recovered %zu files; %" PRIu64
          "bytes. "
          "Some data may have been lost. "
          "****",
          dbname_.c_str(), tables_.size(), bytes);
    }
    return status;
  }

 private:
  struct TableInfo {
    FileMetaData meta;
    SequenceNumber min_sequence;
    SequenceNumber max_sequence;
  };

  std::string const dbname_;
  Env* const env_;
  const InternalKeyComparator icmp_;
  std::vector<std::unique_ptr<IntTblPropCollectorFactory>>
      int_tbl_prop_collector_factories_;
  const Options options_;
  const ImmutableCFOptions ioptions_;
  std::shared_ptr<Cache> raw_table_cache_;
  TableCache* table_cache_;
  VersionEdit* edit_;

  std::vector<std::string> manifests_;
  std::vector<FileDescriptor> table_fds_;
  std::vector<uint64_t> logs_;
  std::vector<TableInfo> tables_;
  uint64_t next_file_number_;
  const EnvOptions env_options_;

  Status FindFiles() {
    std::vector<std::string> filenames;
    bool found_file = false;
    for (uint32_t path_id = 0; path_id < options_.db_paths.size(); path_id++) {
      Status status =
          env_->GetChildren(options_.db_paths[path_id].path, &filenames);
      if (!status.ok()) {
        return status;
      }
      if (!filenames.empty()) {
        found_file = true;
      }

      uint64_t number;
      FileType type;
      for (size_t i = 0; i < filenames.size(); i++) {
        if (ParseFileName(filenames[i], &number, &type)) {
          if (type == kDescriptorFile) {
            assert(path_id == 0);
            manifests_.push_back(filenames[i]);
          } else {
            if (number + 1 > next_file_number_) {
              next_file_number_ = number + 1;
            }
            if (type == kLogFile) {
              assert(path_id == 0);
              logs_.push_back(number);
            } else if (type == kTableFile) {
              table_fds_.emplace_back(number, path_id, 0);
            } else {
              // Ignore other files
            }
          }
        }
      }
    }
    if (!found_file) {
      return Status::Corruption(dbname_, "repair found no files");
    }
    return Status::OK();
  }

  void ConvertLogFilesToTables() {
    for (size_t i = 0; i < logs_.size(); i++) {
      std::string logname = LogFileName(dbname_, logs_[i]);
      Status status = ConvertLogToTable(logs_[i]);
      if (!status.ok()) {
        Log(InfoLogLevel::WARN_LEVEL, options_.info_log,
            "Log #%" PRIu64 ": ignoring conversion error: %s", logs_[i],
            status.ToString().c_str());
      }
      ArchiveFile(logname);
    }
  }

  Status ConvertLogToTable(uint64_t log) {
    struct LogReporter : public log::Reader::Reporter {
      Env* env;
      std::shared_ptr<Logger> info_log;
      uint64_t lognum;
      virtual void Corruption(size_t bytes, const Status& s) override {
        // We print error messages for corruption, but continue repairing.
        Log(InfoLogLevel::ERROR_LEVEL, info_log,
            "Log #%" PRIu64 ": dropping %d bytes; %s", lognum,
            static_cast<int>(bytes), s.ToString().c_str());
      }
    };

    // Open the log file
    std::string logname = LogFileName(dbname_, log);
    unique_ptr<SequentialFile> lfile;
    Status status = env_->NewSequentialFile(logname, &lfile, env_options_);
    if (!status.ok()) {
      return status;
    }

    // Create the log reader.
    LogReporter reporter;
    reporter.env = env_;
    reporter.info_log = options_.info_log;
    reporter.lognum = log;
    // We intentially make log::Reader do checksumming so that
    // corruptions cause entire commits to be skipped instead of
    // propagating bad information (like overly large sequence
    // numbers).
    log::Reader reader(std::move(lfile), &reporter, true /*enable checksum*/,
                       0/*initial_offset*/);

    // Read all the records and add to a memtable
    std::string scratch;
    Slice record;
    WriteBatch batch;
    WriteBuffer wb(options_.db_write_buffer_size);
    MemTable* mem = new MemTable(icmp_, ioptions_,
                                 MutableCFOptions(options_, ioptions_), &wb);
    auto cf_mems_default = new ColumnFamilyMemTablesDefault(mem);
    mem->Ref();
    int counter = 0;
    while (reader.ReadRecord(&record, &scratch)) {
      if (record.size() < 12) {
        reporter.Corruption(
            record.size(), Status::Corruption("log record too small"));
        continue;
      }
      WriteBatchInternal::SetContents(&batch, record);
      status = WriteBatchInternal::InsertInto(&batch, cf_mems_default);
      if (status.ok()) {
        counter += WriteBatchInternal::Count(&batch);
      } else {
        Log(InfoLogLevel::WARN_LEVEL,
            options_.info_log, "Log #%" PRIu64 ": ignoring %s", log,
            status.ToString().c_str());
        status = Status::OK();  // Keep going with rest of file
      }
    }

    // Do not record a version edit for this conversion to a Table
    // since ExtractMetaData() will also generate edits.
    FileMetaData meta;
    meta.fd = FileDescriptor(next_file_number_++, 0, 0);
    {
      ReadOptions ro;
      ro.total_order_seek = true;
      Arena arena;
      ScopedArenaIterator iter(mem->NewIterator(ro, &arena));
      status = BuildTable(dbname_, env_, ioptions_, env_options_, table_cache_,
                          iter.get(), &meta, icmp_,
                          &int_tbl_prop_collector_factories_, 0, 0,
                          kNoCompression, CompressionOptions());
    }
    delete mem->Unref();
    delete cf_mems_default;
    mem = nullptr;
    if (status.ok()) {
      if (meta.fd.GetFileSize() > 0) {
        table_fds_.push_back(meta.fd);
      }
    }
    Log(InfoLogLevel::INFO_LEVEL, options_.info_log,
        "Log #%" PRIu64 ": %d ops saved to Table #%" PRIu64 " %s",
        log, counter, meta.fd.GetNumber(), status.ToString().c_str());
    return status;
  }

  void ExtractMetaData() {
    for (size_t i = 0; i < table_fds_.size(); i++) {
      TableInfo t;
      t.meta.fd = table_fds_[i];
      Status status = ScanTable(&t);
      if (!status.ok()) {
        std::string fname = TableFileName(
            options_.db_paths, t.meta.fd.GetNumber(), t.meta.fd.GetPathId());
        char file_num_buf[kFormatFileNumberBufSize];
        FormatFileNumber(t.meta.fd.GetNumber(), t.meta.fd.GetPathId(),
                         file_num_buf, sizeof(file_num_buf));
        Log(InfoLogLevel::WARN_LEVEL, options_.info_log,
            "Table #%s: ignoring %s", file_num_buf,
            status.ToString().c_str());
        ArchiveFile(fname);
      } else {
        tables_.push_back(t);
      }
    }
  }

  Status ScanTable(TableInfo* t) {
    std::string fname = TableFileName(options_.db_paths, t->meta.fd.GetNumber(),
                                      t->meta.fd.GetPathId());
    int counter = 0;
    uint64_t file_size;
    Status status = env_->GetFileSize(fname, &file_size);
    t->meta.fd = FileDescriptor(t->meta.fd.GetNumber(), t->meta.fd.GetPathId(),
                                file_size);
    if (status.ok()) {
      Iterator* iter = table_cache_->NewIterator(
          ReadOptions(), env_options_, icmp_, t->meta.fd);
      bool empty = true;
      ParsedInternalKey parsed;
      t->min_sequence = 0;
      t->max_sequence = 0;
      for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
        Slice key = iter->key();
        if (!ParseInternalKey(key, &parsed)) {
          Log(InfoLogLevel::ERROR_LEVEL,
              options_.info_log, "Table #%" PRIu64 ": unparsable key %s",
              t->meta.fd.GetNumber(), EscapeString(key).c_str());
          continue;
        }

        counter++;
        if (empty) {
          empty = false;
          t->meta.smallest.DecodeFrom(key);
        }
        t->meta.largest.DecodeFrom(key);
        if (parsed.sequence < t->min_sequence) {
          t->min_sequence = parsed.sequence;
        }
        if (parsed.sequence > t->max_sequence) {
          t->max_sequence = parsed.sequence;
        }
      }
      if (!iter->status().ok()) {
        status = iter->status();
      }
      delete iter;
    }
    Log(InfoLogLevel::INFO_LEVEL,
        options_.info_log, "Table #%" PRIu64 ": %d entries %s",
        t->meta.fd.GetNumber(), counter, status.ToString().c_str());
    return status;
  }

  Status WriteDescriptor() {
    std::string tmp = TempFileName(dbname_, 1);
    unique_ptr<WritableFile> file;
    Status status = env_->NewWritableFile(
        tmp, &file, env_->OptimizeForManifestWrite(env_options_));
    if (!status.ok()) {
      return status;
    }

    SequenceNumber max_sequence = 0;
    for (size_t i = 0; i < tables_.size(); i++) {
      if (max_sequence < tables_[i].max_sequence) {
        max_sequence = tables_[i].max_sequence;
      }
    }

    edit_->SetComparatorName(icmp_.user_comparator()->Name());
    edit_->SetLogNumber(0);
    edit_->SetNextFile(next_file_number_);
    edit_->SetLastSequence(max_sequence);

    for (size_t i = 0; i < tables_.size(); i++) {
      // TODO(opt): separate out into multiple levels
      const TableInfo& t = tables_[i];
      edit_->AddFile(0, t.meta.fd.GetNumber(), t.meta.fd.GetPathId(),
                     t.meta.fd.GetFileSize(), t.meta.smallest, t.meta.largest,
                     t.min_sequence, t.max_sequence);
    }

    //fprintf(stderr, "NewDescriptor:\n%s\n", edit_.DebugString().c_str());
    {
      log::Writer log(std::move(file));
      std::string record;
      edit_->EncodeTo(&record);
      status = log.AddRecord(record);
    }

    if (!status.ok()) {
      env_->DeleteFile(tmp);
    } else {
      // Discard older manifests
      for (size_t i = 0; i < manifests_.size(); i++) {
        ArchiveFile(dbname_ + "/" + manifests_[i]);
      }

      // Install new manifest
      status = env_->RenameFile(tmp, DescriptorFileName(dbname_, 1));
      if (status.ok()) {
        status = SetCurrentFile(env_, dbname_, 1, nullptr);
      } else {
        env_->DeleteFile(tmp);
      }
    }
    return status;
  }

  void ArchiveFile(const std::string& fname) {
    // Move into another directory.  E.g., for
    //    dir/foo
    // rename to
    //    dir/lost/foo
    const char* slash = strrchr(fname.c_str(), '/');
    std::string new_dir;
    if (slash != nullptr) {
      new_dir.assign(fname.data(), slash - fname.data());
    }
    new_dir.append("/lost");
    env_->CreateDir(new_dir);  // Ignore error
    std::string new_file = new_dir;
    new_file.append("/");
    new_file.append((slash == nullptr) ? fname.c_str() : slash + 1);
    Status s = env_->RenameFile(fname, new_file);
    Log(InfoLogLevel::INFO_LEVEL,
        options_.info_log, "Archiving %s: %s\n",
        fname.c_str(), s.ToString().c_str());
  }
};
}  // namespace

Status RepairDB(const std::string& dbname, const Options& options) {
  Repairer repairer(dbname, options);
  return repairer.Run();
}

}  // namespace rocksdb

#endif  // ROCKSDB_LITE
<|endoftext|>


<commit_before>#include "client_manager.hh"

#include "buffer_manager.hh"
#include "command_manager.hh"
#include "event_manager.hh"
#include "face_registry.hh"
#include "file.hh"
#include "ranges.hh"
#include "window.hh"

namespace Kakoune
{

ClientManager::ClientManager() = default;
ClientManager::~ClientManager()
{
    clear(true);
}

void ClientManager::clear(bool disconnect_clients)
{
    if (disconnect_clients)
    {
        while (not m_clients.empty())
            remove_client(*m_clients.front(), true, 0);
    }
    else
        m_clients.clear();
    m_client_trash.clear();

    for (auto& window : m_free_windows)
        window.window->run_hook_in_own_context(Hook::WinClose,
                                               window.window->buffer().name());
    m_free_windows.clear();
    m_window_trash.clear();
}

String ClientManager::generate_name() const
{
    for (int i = 0; true; ++i)
    {
        String name = format("client{}", i);
        if (not client_name_exists(name))
            return name;
    }
}

Client* ClientManager::create_client(std::unique_ptr<UserInterface>&& ui, int pid,
                                     String name, EnvVarMap env_vars, StringView init_cmds,
                                     Optional<BufferCoord> init_coord,
                                     Client::OnExitCallback on_exit)
{
    Buffer& buffer = BufferManager::instance().get_first_buffer();
    WindowAndSelections ws = get_free_window(buffer);
    Client* client = new Client{std::move(ui), std::move(ws.window),
                                std::move(ws.selections), pid,
                                std::move(env_vars),
                                name.empty() ? generate_name() : std::move(name),
                                std::move(on_exit)};
    m_clients.emplace_back(client);

    if (init_coord)
    {
        auto& selections = client->context().selections_write_only();
        selections = SelectionList(buffer, buffer.clamp(*init_coord));
        client->context().window().center_line(init_coord->line);
    }

    try
    {
        auto& context = client->context();
        context.hooks().run_hook(Hook::ClientCreate, context.name(), context);
        CommandManager::instance().execute(init_cmds, context);
    }
    catch (Kakoune::runtime_error& error)
    {
        client->context().print_status({ fix_atom_text(error.what().str()),
                                         client->context().faces()["Error"] });
        client->context().hooks().run_hook(Hook::RuntimeError, error.what(),
                                           client->context());
    }

    // Do not return the client if it already got moved to the trash
    return contains(m_clients, client) ? client : nullptr;
}

void ClientManager::process_pending_inputs()
{
    while (true)
    {
        bool had_input = false;
        // Use index based iteration as a m_clients might get mutated during
        // client input processing, which would break iterator based iteration.
        // (its fine to skip a client if that happens as had_input will be true
        // if a client triggers client removal)
        for (int i = 0; i < m_clients.size(); )
        {
            if (not m_clients[i]->is_ui_ok())
            {
                remove_client(*m_clients[i], false, -1);
                continue;
            }
            had_input = m_clients[i]->process_pending_inputs() or had_input;
            ++i;
        }

        if (not had_input)
            break;
    }
}

bool ClientManager::has_pending_inputs() const
{
    return any_of(m_clients, [](auto&& c) { return c->has_pending_inputs(); });
}

void ClientManager::remove_client(Client& client, bool graceful, int status)
{
    auto it = find(m_clients, &client);
    if (it == m_clients.end())
    {
        kak_assert(contains(m_client_trash, &client));
        return;
    }

    m_client_trash.push_back(std::move(*it));
    m_clients.erase(it);

    auto& context = client.context();
    context.hooks().run_hook(Hook::ClientClose, context.name(), context);

    client.exit(status);

    if (not graceful and m_clients.empty())
        BufferManager::instance().backup_modified_buffers();
}

WindowAndSelections ClientManager::get_free_window(Buffer& buffer)
{
    auto it = find_if(m_free_windows | reverse(),
                      [&](const WindowAndSelections& ws)
                      { return &ws.window->buffer() == &buffer; });

    if (it == m_free_windows.rend())
        return { std::make_unique<Window>(buffer), { buffer, Selection{} } };

    it->window->force_redraw();
    WindowAndSelections res = std::move(*it);
    m_free_windows.erase(it.base()-1);
    res.selections.update();
    return res;
}

void ClientManager::add_free_window(std::unique_ptr<Window>&& window, SelectionList selections)
{
    window->clear_display_buffer();
    m_free_windows.push_back({std::move(window), std::move(selections)});
}

void ClientManager::ensure_no_client_uses_buffer(Buffer& buffer)
{
    for (auto& client : m_clients)
    {
        auto& context = client->context();
        context.jump_list().forget_buffer(buffer);
        if (client->last_buffer() == &buffer)
            client->set_last_buffer(nullptr);

        if (&context.buffer() != &buffer)
            continue;

        if (context.is_editing())
            context.input_handler().reset_normal_mode();

        Buffer* last = client->last_buffer();
        context.change_buffer(last ? *last : BufferManager::instance().get_first_buffer());
    }
    Vector<std::unique_ptr<Window>> removed_windows;
    m_free_windows.erase(std::remove_if(m_free_windows.begin(), m_free_windows.end(),
                                        [&buffer, &removed_windows](WindowAndSelections& ws) {
                                            if (&ws.window->buffer() != &buffer)
                                                return false;
                                            removed_windows.push_back(std::move(ws.window));
                                            return true;
                                         }),
                         m_free_windows.end());

    for (auto&& removed_window : removed_windows)
    {
        removed_window->run_hook_in_own_context(Hook::WinClose,
                                                removed_window->buffer().name());
        m_window_trash.push_back(std::move(removed_window));
    }
}

void ClientManager::clear_window_trash()
{
    m_window_trash.clear();
}

void ClientManager::clear_client_trash()
{
    m_client_trash.clear();
}

bool ClientManager::client_name_exists(StringView name) const
{
    return const_cast<ClientManager*>(this)->get_client_ifp(name) != nullptr;
}

Client* ClientManager::get_client_ifp(StringView name)
{
    for (auto& client : m_clients)
    {
        if (client->context().name() == name)
            return client.get();
    }
    return nullptr;
}

Client& ClientManager::get_client(StringView name)
{
    if (Client* client = get_client_ifp(name))
        return *client;
    throw runtime_error(format("no such client: '{}'", name));
}

void ClientManager::redraw_clients() const
{
    for (auto& client : m_clients)
        client->redraw_ifn();
}

CandidateList ClientManager::complete_client_name(StringView prefix,
                                                  ByteCount cursor_pos) const
{
    auto c = m_clients | transform([](const std::unique_ptr<Client>& c) -> const String&
                                   { return c->context().name(); });
    return complete(prefix, cursor_pos, c);
}

}
<commit_msg>Do not add window pointing to deleted buffers in the free window list<commit_after>#include "client_manager.hh"

#include "buffer_manager.hh"
#include "command_manager.hh"
#include "event_manager.hh"
#include "face_registry.hh"
#include "file.hh"
#include "ranges.hh"
#include "window.hh"

namespace Kakoune
{

ClientManager::ClientManager() = default;
ClientManager::~ClientManager()
{
    clear(true);
}

void ClientManager::clear(bool disconnect_clients)
{
    if (disconnect_clients)
    {
        while (not m_clients.empty())
            remove_client(*m_clients.front(), true, 0);
    }
    else
        m_clients.clear();
    m_client_trash.clear();

    for (auto& window : m_free_windows)
        window.window->run_hook_in_own_context(Hook::WinClose,
                                               window.window->buffer().name());
    m_free_windows.clear();
    m_window_trash.clear();
}

String ClientManager::generate_name() const
{
    for (int i = 0; true; ++i)
    {
        String name = format("client{}", i);
        if (not client_name_exists(name))
            return name;
    }
}

Client* ClientManager::create_client(std::unique_ptr<UserInterface>&& ui, int pid,
                                     String name, EnvVarMap env_vars, StringView init_cmds,
                                     Optional<BufferCoord> init_coord,
                                     Client::OnExitCallback on_exit)
{
    Buffer& buffer = BufferManager::instance().get_first_buffer();
    WindowAndSelections ws = get_free_window(buffer);
    Client* client = new Client{std::move(ui), std::move(ws.window),
                                std::move(ws.selections), pid,
                                std::move(env_vars),
                                name.empty() ? generate_name() : std::move(name),
                                std::move(on_exit)};
    m_clients.emplace_back(client);

    if (init_coord)
    {
        auto& selections = client->context().selections_write_only();
        selections = SelectionList(buffer, buffer.clamp(*init_coord));
        client->context().window().center_line(init_coord->line);
    }

    try
    {
        auto& context = client->context();
        context.hooks().run_hook(Hook::ClientCreate, context.name(), context);
        CommandManager::instance().execute(init_cmds, context);
    }
    catch (Kakoune::runtime_error& error)
    {
        client->context().print_status({ fix_atom_text(error.what().str()),
                                         client->context().faces()["Error"] });
        client->context().hooks().run_hook(Hook::RuntimeError, error.what(),
                                           client->context());
    }

    // Do not return the client if it already got moved to the trash
    return contains(m_clients, client) ? client : nullptr;
}

void ClientManager::process_pending_inputs()
{
    while (true)
    {
        bool had_input = false;
        // Use index based iteration as a m_clients might get mutated during
        // client input processing, which would break iterator based iteration.
        // (its fine to skip a client if that happens as had_input will be true
        // if a client triggers client removal)
        for (int i = 0; i < m_clients.size(); )
        {
            if (not m_clients[i]->is_ui_ok())
            {
                remove_client(*m_clients[i], false, -1);
                continue;
            }
            had_input = m_clients[i]->process_pending_inputs() or had_input;
            ++i;
        }

        if (not had_input)
            break;
    }
}

bool ClientManager::has_pending_inputs() const
{
    return any_of(m_clients, [](auto&& c) { return c->has_pending_inputs(); });
}

void ClientManager::remove_client(Client& client, bool graceful, int status)
{
    auto it = find(m_clients, &client);
    if (it == m_clients.end())
    {
        kak_assert(contains(m_client_trash, &client));
        return;
    }

    m_client_trash.push_back(std::move(*it));
    m_clients.erase(it);

    auto& context = client.context();
    context.hooks().run_hook(Hook::ClientClose, context.name(), context);

    client.exit(status);

    if (not graceful and m_clients.empty())
        BufferManager::instance().backup_modified_buffers();
}

WindowAndSelections ClientManager::get_free_window(Buffer& buffer)
{
    kak_assert(contains(BufferManager::instance(), &buffer));
    auto it = find_if(m_free_windows | reverse(),
                      [&](const WindowAndSelections& ws)
                      { return &ws.window->buffer() == &buffer; });

    if (it == m_free_windows.rend())
        return { std::make_unique<Window>(buffer), { buffer, Selection{} } };

    it->window->force_redraw();
    WindowAndSelections res = std::move(*it);
    m_free_windows.erase(it.base()-1);
    res.selections.update();
    return res;
}

void ClientManager::add_free_window(std::unique_ptr<Window>&& window, SelectionList selections)
{
    if (not contains(BufferManager::instance(), &window->buffer()))
        return;

    window->clear_display_buffer();
    m_free_windows.push_back({std::move(window), std::move(selections)});
}

void ClientManager::ensure_no_client_uses_buffer(Buffer& buffer)
{
    for (auto& client : m_clients)
    {
        auto& context = client->context();
        context.jump_list().forget_buffer(buffer);
        if (client->last_buffer() == &buffer)
            client->set_last_buffer(nullptr);

        if (&context.buffer() != &buffer)
            continue;

        if (context.is_editing())
            context.input_handler().reset_normal_mode();

        Buffer* last = client->last_buffer();
        context.change_buffer(last ? *last : BufferManager::instance().get_first_buffer());
    }
    Vector<std::unique_ptr<Window>> removed_windows;
    m_free_windows.erase(std::remove_if(m_free_windows.begin(), m_free_windows.end(),
                                        [&buffer, &removed_windows](WindowAndSelections& ws) {
                                            if (&ws.window->buffer() != &buffer)
                                                return false;
                                            removed_windows.push_back(std::move(ws.window));
                                            return true;
                                         }),
                         m_free_windows.end());

    for (auto&& removed_window : removed_windows)
    {
        removed_window->run_hook_in_own_context(Hook::WinClose,
                                                removed_window->buffer().name());
        m_window_trash.push_back(std::move(removed_window));
    }
}

void ClientManager::clear_window_trash()
{
    m_window_trash.clear();
}

void ClientManager::clear_client_trash()
{
    m_client_trash.clear();
}

bool ClientManager::client_name_exists(StringView name) const
{
    return const_cast<ClientManager*>(this)->get_client_ifp(name) != nullptr;
}

Client* ClientManager::get_client_ifp(StringView name)
{
    for (auto& client : m_clients)
    {
        if (client->context().name() == name)
            return client.get();
    }
    return nullptr;
}

Client& ClientManager::get_client(StringView name)
{
    if (Client* client = get_client_ifp(name))
        return *client;
    throw runtime_error(format("no such client: '{}'", name));
}

void ClientManager::redraw_clients() const
{
    for (auto& client : m_clients)
        client->redraw_ifn();
}

CandidateList ClientManager::complete_client_name(StringView prefix,
                                                  ByteCount cursor_pos) const
{
    auto c = m_clients | transform([](const std::unique_ptr<Client>& c) -> const String&
                                   { return c->context().name(); });
    return complete(prefix, cursor_pos, c);
}

}
<|endoftext|>


<commit_before>#pragma once

#include "grid_base.hpp"
#include <numeric>

namespace gftools {

template <bool Fermion> inline complex_type Matsubara(int n, real_type beta){return PI*I/beta*complex_type(2*n+Fermion);};
template <bool Fermion> inline int MatsubaraIndex(complex_type in, real_type beta){return std::round((beta*imag(in)/PI-Fermion)/2.0);};

inline complex_type FMatsubara(int n, real_type beta){return Matsubara<1>(n,beta);};
inline complex_type BMatsubara(int n, real_type beta){return Matsubara<0>(n,beta);};
inline int FMatsubaraIndex(complex_type in, real_type beta){return MatsubaraIndex<1>(in,beta);};
inline int BMatsubaraIndex(complex_type in, real_type beta){return MatsubaraIndex<0>(in,beta);};

/** A Grid of fermionic Matsubara frequencies. */
template <bool Fermion>
class matsubara_grid : public grid_base<complex_type, matsubara_grid<Fermion>>
{
public:
    typedef grid_base<complex_type, matsubara_grid<Fermion>> base;
    typedef typename base::point point;
    using base::vals_;
    using typename base::ex_wrong_index;
    //typedef typename grid_base<complex_type, matsubara_grid<Fermion>>::point point;
    //using typename grid_base<complex_type, matsubara_grid<Fermion>>::point;
    /** Inverse temperature. */
    const real_type beta_;
    /** Spacing between values. */
    const real_type spacing_;
    /** Min and max numbers of freq. - useful for searching. */
    const int w_min_, w_max_;
    matsubara_grid(int min, int max, real_type beta);
    matsubara_grid(const matsubara_grid &rhs);
    matsubara_grid(matsubara_grid&& rhs);
    matsubara_grid(std::vector<complex_type> const& in);
    int getNumber(complex_type in) const;
    double beta() const { return beta_; }
    int min_n() const { return w_min_; }
    int max_n() const { return w_max_; }

    point find_nearest(complex_type in) const;
    template <class Obj> auto integrate(const Obj &in) const -> decltype(in(vals_[0]));
    template <class Obj> auto prod(const Obj &in) const -> decltype(in(vals_[0]));
    //template <class Obj> auto gridIntegrate(const std::vector<Obj> &in) const -> Obj;
    template <class Obj> auto eval(Obj &in, complex_type x) const ->decltype(in[0]);
    //template <class Obj> auto eval(Obj &in, point x) const ->decltype(in[0]);
    //using base::eval;
};

typedef matsubara_grid<1> fmatsubara_grid;
typedef matsubara_grid<0> bmatsubara_grid;

//
// matsubara_grid implementations
//

template <bool F>
matsubara_grid<F>::matsubara_grid(int min, int max, real_type beta):
    //grid_base<complex_type, matsubara_grid<F>>(min,max,std::bind(Matsubara<F>, std::placeholders::_1, beta)),
    grid_base<complex_type, matsubara_grid<F>>(min,max,[beta](int n){return Matsubara<F>(n,beta);}),
    beta_(beta),
    spacing_(PI/beta),
    w_min_(min),
    w_max_(max)
{
}

template <bool F>
matsubara_grid<F>::matsubara_grid(const matsubara_grid<F> &rhs) :
    grid_base<complex_type, matsubara_grid<F>>(rhs.vals_),
    beta_(rhs.beta_),
    spacing_(rhs.spacing_),
    w_min_(rhs.w_min_),
    w_max_(rhs.w_max_)
{
}

template <bool F>
matsubara_grid<F>::matsubara_grid(matsubara_grid<F>&& rhs):
    grid_base<complex_type, matsubara_grid>(rhs.vals_),
    beta_(rhs.beta_),
    spacing_(rhs.spacing_),
    w_min_(rhs.w_min_),
    w_max_(rhs.w_max_)
{
}

template <bool F>
matsubara_grid<F>::matsubara_grid(std::vector<complex_type> const& in):
    base(in),
    beta_( PI / std::abs((in[1] - in[0])/2.) ),
    spacing_(std::abs((in[1] - in[0])/2.)),
    w_min_(FMatsubaraIndex(in[0], beta_)),
    w_max_(FMatsubaraIndex(in[in.size() - 1], beta_))
{
}

template <bool F>
template <class Obj>
auto matsubara_grid<F>::integrate(const Obj &in) const -> decltype(in(vals_[0]))
{
    decltype(in(this->vals_[0])) R = in(this->vals_[0]);
    R=std::accumulate(vals_.begin()+1, vals_.end(), R,[&](decltype(in(vals_[0]))& y,decltype(vals_[0]) &x) {return y+in(x);});
    return R/beta_;
}

template <bool F>
template <class Obj>
auto matsubara_grid<F>::prod(const Obj &in) const -> decltype(in(vals_[0]))
{
    // fix prod for more numerical stability
    decltype(in(vals_[0])) R = in(vals_[0]);
    R=std::accumulate(vals_.begin()+1, vals_.end(), R,[&](decltype(in(vals_[0]))& y,decltype(vals_[0]) &x) {return y*in(x);});
    //decltype(in(vals_[0])) R = in(vals_[vals_.size()/2]);
    //R=std::accumulate(vals_.begin()+1+vals_.size()/2, vals_.end(), R,[&](decltype(in(vals_[0]))& y,decltype(vals_[0]) &x) {DEBUG(x << "|" << in(x) << "|" << y << "->" << y*in(x)); return y*in(x);});
    //R=std::accumulate(vals_.begin(), vals_.begin()+vals_.size()/2, R,[&](decltype(in(vals_[0]))& y,decltype(vals_[0]) &x) {DEBUG(x << "|" << in(x) << "|" << y << "->" << y*in(x)); return y*in(x);});
    return R;
}

template <bool F>
inline typename matsubara_grid<F>::point matsubara_grid<F>::find_nearest (complex_type in) const
{
    int n=getNumber(in);
    #ifndef NDEBUG
    //DEBUG("Invoking matsubara find");
    #endif
    if (n>=w_min_ && n<w_max_) { return vals_[n-w_min_]; }
    else {
        #ifndef NDEBUG
        //ERROR("Couldn't find the point");
        #endif
        if (n<w_min_) return vals_[0];
        else return vals_[vals_.size()-1];
        };
}

template <bool F>
inline int matsubara_grid<F>::getNumber(complex_type in) const
{
    assert (std::abs(real(in))<std::numeric_limits<real_type>::epsilon());
    return std::lround(imag(in)/spacing_-F)/2;
};

template <bool F>
template <class Obj>
inline auto matsubara_grid<F>::eval(Obj &in, complex_type x) const ->decltype(in[0])
{
    const auto find_result=this->find_nearest(x);
    if (!tools::is_float_equal<complex_type>(x,find_result.val_)) { throw (ex_wrong_index()); }
    return in[find_result.index_];
}

} // end of namespace gftools
<commit_msg>matsubara_grid : bugfix in constructor for bosonic grids<commit_after>#pragma once

#include "grid_base.hpp"
#include <numeric>

namespace gftools {

template <bool Fermion> inline complex_type Matsubara(int n, real_type beta){return PI*I/beta*complex_type(2*n+Fermion);};
template <bool Fermion> inline int MatsubaraIndex(complex_type in, real_type beta){return std::round((beta*imag(in)/PI-Fermion)/2.0);};

inline complex_type FMatsubara(int n, real_type beta){return Matsubara<1>(n,beta);};
inline complex_type BMatsubara(int n, real_type beta){return Matsubara<0>(n,beta);};
inline int FMatsubaraIndex(complex_type in, real_type beta){return MatsubaraIndex<1>(in,beta);};
inline int BMatsubaraIndex(complex_type in, real_type beta){return MatsubaraIndex<0>(in,beta);};

/** A Grid of fermionic Matsubara frequencies. */
template <bool Fermion>
class matsubara_grid : public grid_base<complex_type, matsubara_grid<Fermion>>
{
public:
    typedef grid_base<complex_type, matsubara_grid<Fermion>> base;
    typedef typename base::point point;
    using base::vals_;
    using typename base::ex_wrong_index;
    //typedef typename grid_base<complex_type, matsubara_grid<Fermion>>::point point;
    //using typename grid_base<complex_type, matsubara_grid<Fermion>>::point;
    /** Inverse temperature. */
    const real_type beta_;
    /** Spacing between values. */
    const real_type spacing_;
    /** Min and max numbers of freq. - useful for searching. */
    const int w_min_, w_max_;
    matsubara_grid(int min, int max, real_type beta);
    matsubara_grid(const matsubara_grid &rhs);
    matsubara_grid(matsubara_grid&& rhs);
    matsubara_grid(std::vector<complex_type> const& in);
    int getNumber(complex_type in) const;
    double beta() const { return beta_; }
    int min_n() const { return w_min_; }
    int max_n() const { return w_max_; }

    point find_nearest(complex_type in) const;
    template <class Obj> auto integrate(const Obj &in) const -> decltype(in(vals_[0]));
    template <class Obj> auto prod(const Obj &in) const -> decltype(in(vals_[0]));
    //template <class Obj> auto gridIntegrate(const std::vector<Obj> &in) const -> Obj;
    template <class Obj> auto eval(Obj &in, complex_type x) const ->decltype(in[0]);
    //template <class Obj> auto eval(Obj &in, point x) const ->decltype(in[0]);
    //using base::eval;
};

typedef matsubara_grid<1> fmatsubara_grid;
typedef matsubara_grid<0> bmatsubara_grid;

//
// matsubara_grid implementations
//

template <bool F>
matsubara_grid<F>::matsubara_grid(int min, int max, real_type beta):
    //grid_base<complex_type, matsubara_grid<F>>(min,max,std::bind(Matsubara<F>, std::placeholders::_1, beta)),
    grid_base<complex_type, matsubara_grid<F>>(min,max,[beta](int n){return Matsubara<F>(n,beta);}),
    beta_(beta),
    spacing_(PI/beta),
    w_min_(min),
    w_max_(max)
{
}

template <bool F>
matsubara_grid<F>::matsubara_grid(const matsubara_grid<F> &rhs) :
    grid_base<complex_type, matsubara_grid<F>>(rhs.vals_),
    beta_(rhs.beta_),
    spacing_(rhs.spacing_),
    w_min_(rhs.w_min_),
    w_max_(rhs.w_max_)
{
}

template <bool F>
matsubara_grid<F>::matsubara_grid(matsubara_grid<F>&& rhs):
    grid_base<complex_type, matsubara_grid>(rhs.vals_),
    beta_(rhs.beta_),
    spacing_(rhs.spacing_),
    w_min_(rhs.w_min_),
    w_max_(rhs.w_max_)
{
}

template <bool F>
matsubara_grid<F>::matsubara_grid(std::vector<complex_type> const& in):
    base(in),
    beta_( PI / std::abs((in[1] - in[0])/2.) ),
    spacing_(std::abs((in[1] - in[0])/2.)),
    w_min_(MatsubaraIndex<F>(in[0], beta_)),
    w_max_(MatsubaraIndex<F>(in[in.size() - 1], beta_))
{
}

template <bool F>
template <class Obj>
auto matsubara_grid<F>::integrate(const Obj &in) const -> decltype(in(vals_[0]))
{
    decltype(in(this->vals_[0])) R = in(this->vals_[0]);
    R=std::accumulate(vals_.begin()+1, vals_.end(), R,[&](decltype(in(vals_[0]))& y,decltype(vals_[0]) &x) {return y+in(x);});
    return R/beta_;
}

template <bool F>
template <class Obj>
auto matsubara_grid<F>::prod(const Obj &in) const -> decltype(in(vals_[0]))
{
    // fix prod for more numerical stability
    decltype(in(vals_[0])) R = in(vals_[0]);
    R=std::accumulate(vals_.begin()+1, vals_.end(), R,[&](decltype(in(vals_[0]))& y,decltype(vals_[0]) &x) {return y*in(x);});
    //decltype(in(vals_[0])) R = in(vals_[vals_.size()/2]);
    //R=std::accumulate(vals_.begin()+1+vals_.size()/2, vals_.end(), R,[&](decltype(in(vals_[0]))& y,decltype(vals_[0]) &x) {DEBUG(x << "|" << in(x) << "|" << y << "->" << y*in(x)); return y*in(x);});
    //R=std::accumulate(vals_.begin(), vals_.begin()+vals_.size()/2, R,[&](decltype(in(vals_[0]))& y,decltype(vals_[0]) &x) {DEBUG(x << "|" << in(x) << "|" << y << "->" << y*in(x)); return y*in(x);});
    return R;
}

template <bool F>
inline typename matsubara_grid<F>::point matsubara_grid<F>::find_nearest (complex_type in) const
{
    int n=getNumber(in);
    #ifndef NDEBUG
    //DEBUG("Invoking matsubara find");
    #endif
    if (n>=w_min_ && n<w_max_) { return vals_[n-w_min_]; }
    else {
        #ifndef NDEBUG
        //ERROR("Couldn't find the point");
        #endif
        if (n<w_min_) return vals_[0];
        else return vals_[vals_.size()-1];
        };
}

template <bool F>
inline int matsubara_grid<F>::getNumber(complex_type in) const
{
    assert (std::abs(real(in))<std::numeric_limits<real_type>::epsilon());
    return std::lround(imag(in)/spacing_-F)/2;
};

template <bool F>
template <class Obj>
inline auto matsubara_grid<F>::eval(Obj &in, complex_type x) const ->decltype(in[0])
{
    const auto find_result=this->find_nearest(x);
    if (!tools::is_float_equal<complex_type>(x,find_result.val_)) { throw (ex_wrong_index()); }
    return in[find_result.index_];
}

} // end of namespace gftools
<|endoftext|>


<commit_before>//
//  Utils.cpp
//  ee_x
//
//  Created by Zinge on 10/9/17.
//
//

#include "ee/core/Utils.hpp"

#include <cassert>
#include <cstdarg>
#include <cstdlib>
#include <dlfcn.h>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <queue>
#include <random>
#include <sstream>
#include <thread>
#include <unwind.h>

#ifdef EE_X_ANDROID
#include <android/log.h>
#endif // EE_X_ANDROID

#include <ee/nlohmann/json.hpp>

#include "ee/core/LambdaAwaiter.hpp"
#include "ee/core/LogLevel.hpp"
#include "ee/core/SpinLock.hpp"
#include "ee/core/internal/MessageBridge.hpp"

namespace ee {
namespace core {
std::string str_tolower(std::string s) {
    std::transform(s.begin(), s.end(), s.begin(),
                   [](unsigned char c) { return std::tolower(c); } // correct
    );
    return s;
}

std::string toString(float value) {
    std::stringstream ss;
    ss << value;
    return ss.str();
}

std::string toString(bool value) {
    return value ? "true" : "false";
}

bool toBool(const std::string& value) {
    assert(value == "true" || value == "false");
    return value == "true";
}

namespace detail {
std::mt19937& getRandomEngine() {
    static std::random_device rd;
    static std::mt19937 engine(rd());
    return engine;
}
} // namespace detail

std::string format(std::string formatString, ...) {
    std::va_list args;
    va_start(args, formatString);
    auto result = format(formatString, args);
    va_end(args);
    return result;
}

std::string format(std::string formatString, std::va_list args) {
    std::size_t n = formatString.size() * 2;
    while (true) {
        auto formatted = std::make_unique<char[]>(n);
        std::va_list temp_args;
        va_copy(temp_args, args);
        auto final_n =
            std::vsnprintf(&formatted[0], n, formatString.c_str(), temp_args);
        va_end(temp_args);
        if (final_n < 0 || final_n >= static_cast<int>(n)) {
            n += static_cast<std::size_t>(
                std::abs(final_n - static_cast<int>(n) + 1));
        } else {
            return std::string(formatted.get());
        }
    }
}

struct BacktraceState {
    void** current;
    void** end;
};

_Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg) {
    BacktraceState* state = static_cast<BacktraceState*>(arg);
    uintptr_t pc = _Unwind_GetIP(context);
    if (pc) {
        if (state->current == state->end) {
            return _URC_END_OF_STACK;
        } else {
            *state->current++ = reinterpret_cast<void*>(pc);
        }
    }
    return _URC_NO_REASON;
}

size_t captureBacktrace(void** buffer, size_t max) {
    BacktraceState state = {buffer, buffer + max};
    _Unwind_Backtrace(unwindCallback, &state);

    return state.current - buffer;
}

void dumpBacktrace(std::ostream& os, void** buffer, size_t count) {
    for (size_t idx = 0; idx < count; ++idx) {
        const void* addr = buffer[idx];
        const char* symbol = "";

        Dl_info info;
        if (dladdr(addr, &info) && info.dli_sname) {
            symbol = info.dli_sname;
        }

        os << "  #" << std::setw(2) << idx << ": " << addr << "  " << symbol
           << "\n";
    }
}

std::string dumpBacktrace(size_t count) {
    void* buffer[count];
    std::ostringstream oss;

    dumpBacktrace(oss, buffer, captureBacktrace(buffer, count));
    return oss.str();
}

namespace {
// clang-format off
constexpr auto k__isMainThread                  = "Utils_isMainThread";
constexpr auto k__runOnUiThread                 = "Utils_runOnUiThread";
constexpr auto k__runOnUiThreadDelayed          = "Utils_runOnUiThreadDelayed";
constexpr auto k__runOnUiThreadCallback         = "Utils_runOnUiThreadCallback";

constexpr auto k__isApplicationInstalled        = "Utils_isApplicationInstalled";
constexpr auto k__openApplication               = "Utils_openApplication";

constexpr auto k__getApplicationId              = "Utils_getApplicationId";
constexpr auto k__getApplicationName            = "Utils_getApplicationName";
constexpr auto k__getVersionName                = "Utils_getVersionName";
constexpr auto k__getVersionCode                = "Utils_getVersionCode";

constexpr auto k__getSHA1CertificateFingerprint = "Utils_getSHA1CertificateFingerprint";
constexpr auto k__isInstantApp                  = "Utils_isInstantApp";
constexpr auto k__isTablet                      = "Utils_isTablet";
constexpr auto k__getDensity                    = "Utils_getDensity";
constexpr auto k__getDeviceId                   = "Utils_getDeviceId";
constexpr auto k__getSafeInset                  = "Utils_getSafeInset";

constexpr auto k__sendMail                      = "Utils_sendMail";
constexpr auto k__testConnection                = "Utils_testConnection";
constexpr auto k__showInstallPrompt             = "Utils_showInstallPrompt";
// clang-format on
} // namespace

bool isMainThread() {
    auto&& bridge = MessageBridge::getInstance();
    auto response = bridge.call(k__isMainThread);
    return toBool(response);
}

namespace {
std::queue<Runnable<>> q_;
SpinLock lock_;

void pushRunnable(const Runnable<>& runnable) {
    std::lock_guard<SpinLock> guard(lock_);
    q_.push(runnable);
}

Runnable<> popRunnable() {
    std::lock_guard<SpinLock> guard(lock_);
    assert(not q_.empty());
    auto runnable = q_.front();
    q_.pop();
    return runnable;
}

void runOnUiThreadCallback() {
    popRunnable()();
}

void registerHandler() {
    static std::once_flag flag;
    std::call_once(flag, [] {
        auto&& bridge = MessageBridge::getInstance();
        bridge.registerHandler(
            [](const std::string& message) {
                runOnUiThreadCallback();
                return "";
            },
            k__runOnUiThreadCallback);
    });
}
} // namespace

bool runOnUiThread(const Runnable<>& runnable) {
    if (isMainThread()) {
        runnable();
        return true;
    }
    registerHandler();
    pushRunnable(runnable);
    auto&& bridge = MessageBridge::getInstance();
    auto response = bridge.call(k__runOnUiThread);
    return toBool(response);
}

void runOnUiThreadDelayed(const Runnable<>& runnable, float delay) {
    noAwait([runnable, delay]() -> Task<> {
        nlohmann::json json;
        json["delay"] = delay;
        auto&& bridge = MessageBridge::getInstance();
        auto response =
            co_await bridge.callAsync(k__runOnUiThreadDelayed, json.dump());
        if (runnable) {
            runnable();
        }
    });
}

void runOnUiThreadAndWait(const Runnable<>& runnable) {
    std::promise<void> promise;
    runOnUiThread([runnable, &promise] {
        runnable();
        promise.set_value();
    });
    return promise.get_future().get();
}

bool isApplicationInstalled(const std::string& applicationId) {
    auto&& bridge = MessageBridge::getInstance();
    auto response = bridge.call(k__isApplicationInstalled, applicationId);
    return toBool(response);
}

bool openApplication(const std::string& applicationId) {
    auto&& bridge = MessageBridge::getInstance();
    auto response = bridge.call(k__openApplication, applicationId);
    return toBool(response);
}

std::string getApplicationId() {
    auto&& bridge = MessageBridge::getInstance();
    return bridge.call(k__getApplicationId);
}

std::string getApplicationName() {
    auto&& bridge = MessageBridge::getInstance();
    return bridge.call(k__getApplicationName);
}

std::string getVersionName() {
    auto&& bridge = MessageBridge::getInstance();
    return bridge.call(k__getVersionName);
}

std::string getVersionCode() {
    auto&& bridge = MessageBridge::getInstance();
    return bridge.call(k__getVersionCode);
}

std::string getSHA1CertificateFingerprint() {
#ifdef EE_X_ANDROID
    auto&& bridge = MessageBridge::getInstance();
    return bridge.call(k__getSHA1CertificateFingerprint);
#else  // EE_X_ANDROID
    return "";
#endif // EE_X_ANDROID
}

bool isInstantApp() {
#ifdef EE_X_ANDROID
    auto&& bridge = MessageBridge::getInstance();
    auto response = bridge.call(k__isInstantApp);
    return toBool(response);
#else  // EE_X_ANDROID
    return false;
#endif // EE_X_ANDROID
}

bool isTablet() {
    auto&& bridge = MessageBridge::getInstance();
    auto response = bridge.call(k__isTablet);
    return toBool(response);
}

float getDensity() {
    auto&& bridge = MessageBridge::getInstance();
    auto response = bridge.call(k__getDensity);
    return std::stof(response);
}

Task<std::string> getDeviceId() {
    auto&& bridge = MessageBridge::getInstance();
    auto response = co_await bridge.callAsync(k__getDeviceId);
    co_return response;
}

SafeInset getSafeInset() {
    SafeInset result;
#ifdef EE_X_ANDROID
    auto&& bridge = MessageBridge::getInstance();
    auto response = bridge.call(k__getSafeInset);
    auto json = nlohmann::json::parse(response);

    result.left = json["left"];
    result.right = json["right"];
    result.top = json["top"];
    result.bottom = json["bottom"];
#else  // EE_X_ANDROID
    // TODO.
    result.left = 0;
    result.right = 0;
    result.top = 0;
    result.bottom = 0;
#endif // EE_X_ANDROID
    return result;
}

bool sendMail(const std::string& recipient, const std::string& subject,
              const std::string& body) {
    nlohmann::json json;
    json["recipient"] = recipient;
    json["subject"] = subject;
    json["body"] = body;
    auto&& bridge = MessageBridge::getInstance();
    auto response = bridge.call(k__sendMail, json.dump());
    return toBool(response);
}

Task<bool> testConnection(const std::string& hostName, float timeOut) {
    nlohmann::json json;
    json["host_name"] = hostName;
    json["time_out"] = timeOut;
    auto&& bridge = MessageBridge::getInstance();
    auto response = co_await bridge.callAsync(k__testConnection, json.dump());
    co_return toBool(response);
}

void showInstallPrompt(const std::string& url, const std::string& referrer) {
#ifdef EE_X_ANDROID
    nlohmann::json json;
    json["url"] = url;
    json["referrer"] = referrer;
    auto&& bridge = MessageBridge::getInstance();
    bridge.call(k__showInstallPrompt, json.dump());
#endif // EE_X_ANDROID
}

#ifdef EE_X_ANDROID
void log(const LogLevel& level, const std::string& tag,
         const std::string& message) {
    __android_log_print(level.priority, tag.c_str(), "%s", message.c_str());
}
#endif // EE_X_ANDROID

#if defined(EE_X_IOS) || defined(EE_X_OSX)
extern "C" {
void ee_staticLog(const char* message);
} // extern "C"

void log(const LogLevel& level, const std::string& tag,
         const std::string& message) {
    std::string buffer;
    buffer.reserve(level.desc.size() + 1 + tag.size() + 1 + message.size());
    buffer += level.desc;
    buffer += " ";
    buffer += tag;
    buffer += " ";
    buffer += message;
    ee_staticLog(buffer.c_str());
}
#endif // defined(EE_X_IOS) || defined(EE_X_OSX)
} // namespace core
} // namespace ee
<commit_msg>Fix getApplicationSignatures.<commit_after>//
//  Utils.cpp
//  ee_x
//
//  Created by Zinge on 10/9/17.
//
//

#include "ee/core/Utils.hpp"

#include <cassert>
#include <cstdarg>
#include <cstdlib>
#include <dlfcn.h>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <queue>
#include <random>
#include <sstream>
#include <thread>
#include <unwind.h>

#ifdef EE_X_ANDROID
#include <android/log.h>
#endif // EE_X_ANDROID

#include <ee/nlohmann/json.hpp>

#include "ee/core/LambdaAwaiter.hpp"
#include "ee/core/LogLevel.hpp"
#include "ee/core/SpinLock.hpp"
#include "ee/core/internal/MessageBridge.hpp"

namespace ee {
namespace core {
std::string str_tolower(std::string s) {
    std::transform(s.begin(), s.end(), s.begin(),
                   [](unsigned char c) { return std::tolower(c); } // correct
    );
    return s;
}

std::string toString(float value) {
    std::stringstream ss;
    ss << value;
    return ss.str();
}

std::string toString(bool value) {
    return value ? "true" : "false";
}

bool toBool(const std::string& value) {
    assert(value == "true" || value == "false");
    return value == "true";
}

namespace detail {
std::mt19937& getRandomEngine() {
    static std::random_device rd;
    static std::mt19937 engine(rd());
    return engine;
}
} // namespace detail

std::string format(std::string formatString, ...) {
    std::va_list args;
    va_start(args, formatString);
    auto result = format(formatString, args);
    va_end(args);
    return result;
}

std::string format(std::string formatString, std::va_list args) {
    std::size_t n = formatString.size() * 2;
    while (true) {
        auto formatted = std::make_unique<char[]>(n);
        std::va_list temp_args;
        va_copy(temp_args, args);
        auto final_n =
            std::vsnprintf(&formatted[0], n, formatString.c_str(), temp_args);
        va_end(temp_args);
        if (final_n < 0 || final_n >= static_cast<int>(n)) {
            n += static_cast<std::size_t>(
                std::abs(final_n - static_cast<int>(n) + 1));
        } else {
            return std::string(formatted.get());
        }
    }
}

struct BacktraceState {
    void** current;
    void** end;
};

_Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg) {
    BacktraceState* state = static_cast<BacktraceState*>(arg);
    uintptr_t pc = _Unwind_GetIP(context);
    if (pc) {
        if (state->current == state->end) {
            return _URC_END_OF_STACK;
        } else {
            *state->current++ = reinterpret_cast<void*>(pc);
        }
    }
    return _URC_NO_REASON;
}

size_t captureBacktrace(void** buffer, size_t max) {
    BacktraceState state = {buffer, buffer + max};
    _Unwind_Backtrace(unwindCallback, &state);

    return state.current - buffer;
}

void dumpBacktrace(std::ostream& os, void** buffer, size_t count) {
    for (size_t idx = 0; idx < count; ++idx) {
        const void* addr = buffer[idx];
        const char* symbol = "";

        Dl_info info;
        if (dladdr(addr, &info) && info.dli_sname) {
            symbol = info.dli_sname;
        }

        os << "  #" << std::setw(2) << idx << ": " << addr << "  " << symbol
           << "\n";
    }
}

std::string dumpBacktrace(size_t count) {
    void* buffer[count];
    std::ostringstream oss;

    dumpBacktrace(oss, buffer, captureBacktrace(buffer, count));
    return oss.str();
}

namespace {
// clang-format off
const std::string kPrefix = "Utils_";

const auto kIsMainThread             = kPrefix + "IsMainThread";
const auto kRunOnUiThread            = kPrefix + "runOnUiThread";
const auto kRunOnUiThreadDelayed     = kPrefix + "runOnUiThreadDelayed";
const auto kRunOnUiThreadCallback    = kPrefix + "runOnUiThreadCallback";

const auto kIsApplicationInstalled   = kPrefix + "isApplicationInstalled";
const auto kOpenApplication          = kPrefix + "openApplication";

const auto kGetApplicationId         = kPrefix + "getApplicationId";
const auto kGetApplicationName       = kPrefix + "getApplicationName";
const auto kGetVersionName           = kPrefix + "getVersionName";
const auto kGetVersionCode           = kPrefix + "getVersionCode";

const auto kGetApplicationSignatures = kPrefix + "getApplicationSignatures";
const auto kIsInstantApp             = kPrefix + "isInstantApp";
const auto kIsTablet                 = kPrefix + "isTablet";
const auto kGetDensity               = kPrefix + "getDensity";
const auto kGetDeviceId              = kPrefix + "getDeviceId";
const auto kGetSafeInset             = kPrefix + "getSafeInset";

const auto kSendMail                 = kPrefix + "sendMail";
const auto kTestConnection           = kPrefix + "testConnection";
const auto kShowInstallPrompt        = kPrefix + "showInstallPrompt";
// clang-format on
} // namespace

bool isMainThread() {
    auto&& bridge = MessageBridge::getInstance();
    auto response = bridge.call(kIsMainThread);
    return toBool(response);
}

namespace {
std::queue<Runnable<>> q_;
SpinLock lock_;

void pushRunnable(const Runnable<>& runnable) {
    std::lock_guard<SpinLock> guard(lock_);
    q_.push(runnable);
}

Runnable<> popRunnable() {
    std::lock_guard<SpinLock> guard(lock_);
    assert(not q_.empty());
    auto runnable = q_.front();
    q_.pop();
    return runnable;
}

void runOnUiThreadCallback() {
    popRunnable()();
}

void registerHandler() {
    static std::once_flag flag;
    std::call_once(flag, [] {
        auto&& bridge = MessageBridge::getInstance();
        bridge.registerHandler(
            [](const std::string& message) {
                runOnUiThreadCallback();
                return "";
            },
            kRunOnUiThreadCallback);
    });
}
} // namespace

bool runOnUiThread(const Runnable<>& runnable) {
    if (isMainThread()) {
        runnable();
        return true;
    }
    registerHandler();
    pushRunnable(runnable);
    auto&& bridge = MessageBridge::getInstance();
    auto response = bridge.call(kRunOnUiThread);
    return toBool(response);
}

void runOnUiThreadDelayed(const Runnable<>& runnable, float delay) {
    noAwait([runnable, delay]() -> Task<> {
        nlohmann::json json;
        json["delay"] = delay;
        auto&& bridge = MessageBridge::getInstance();
        auto response =
            co_await bridge.callAsync(kRunOnUiThreadDelayed, json.dump());
        if (runnable) {
            runnable();
        }
    });
}

void runOnUiThreadAndWait(const Runnable<>& runnable) {
    std::promise<void> promise;
    runOnUiThread([runnable, &promise] {
        runnable();
        promise.set_value();
    });
    return promise.get_future().get();
}

bool isApplicationInstalled(const std::string& applicationId) {
    auto&& bridge = MessageBridge::getInstance();
    auto response = bridge.call(kIsApplicationInstalled, applicationId);
    return toBool(response);
}

bool openApplication(const std::string& applicationId) {
    auto&& bridge = MessageBridge::getInstance();
    auto response = bridge.call(kOpenApplication, applicationId);
    return toBool(response);
}

std::string getApplicationId() {
    auto&& bridge = MessageBridge::getInstance();
    return bridge.call(kGetApplicationId);
}

std::string getApplicationName() {
    auto&& bridge = MessageBridge::getInstance();
    return bridge.call(kGetApplicationName);
}

std::string getVersionName() {
    auto&& bridge = MessageBridge::getInstance();
    return bridge.call(kGetVersionName);
}

std::string getVersionCode() {
    auto&& bridge = MessageBridge::getInstance();
    return bridge.call(kGetVersionCode);
}

std::string getSHA1CertificateFingerprint() {
#ifdef EE_X_ANDROID
    auto&& bridge = MessageBridge::getInstance();
    auto response = bridge.call(kGetApplicationSignatures, "SHA1");
    auto signatures = nlohmann::json::parse(response);
    return signatures.empty() ? "" : signatures[0];
#else  // EE_X_ANDROID
    return "";
#endif // EE_X_ANDROID
}

bool isInstantApp() {
#ifdef EE_X_ANDROID
    auto&& bridge = MessageBridge::getInstance();
    auto response = bridge.call(kIsInstantApp);
    return toBool(response);
#else  // EE_X_ANDROID
    return false;
#endif // EE_X_ANDROID
}

bool isTablet() {
    auto&& bridge = MessageBridge::getInstance();
    auto response = bridge.call(kIsTablet);
    return toBool(response);
}

float getDensity() {
    auto&& bridge = MessageBridge::getInstance();
    auto response = bridge.call(kGetDensity);
    return std::stof(response);
}

Task<std::string> getDeviceId() {
    auto&& bridge = MessageBridge::getInstance();
    auto response = co_await bridge.callAsync(kGetDeviceId);
    co_return response;
}

SafeInset getSafeInset() {
    SafeInset result;
#ifdef EE_X_ANDROID
    auto&& bridge = MessageBridge::getInstance();
    auto response = bridge.call(kGetSafeInset);
    auto json = nlohmann::json::parse(response);

    result.left = json["left"];
    result.right = json["right"];
    result.top = json["top"];
    result.bottom = json["bottom"];
#else  // EE_X_ANDROID
    // TODO.
    result.left = 0;
    result.right = 0;
    result.top = 0;
    result.bottom = 0;
#endif // EE_X_ANDROID
    return result;
}

bool sendMail(const std::string& recipient, const std::string& subject,
              const std::string& body) {
    nlohmann::json json;
    json["recipient"] = recipient;
    json["subject"] = subject;
    json["body"] = body;
    auto&& bridge = MessageBridge::getInstance();
    auto response = bridge.call(kSendMail, json.dump());
    return toBool(response);
}

Task<bool> testConnection(const std::string& hostName, float timeOut) {
    nlohmann::json json;
    json["host_name"] = hostName;
    json["time_out"] = timeOut;
    auto&& bridge = MessageBridge::getInstance();
    auto response = co_await bridge.callAsync(kTestConnection, json.dump());
    co_return toBool(response);
}

void showInstallPrompt(const std::string& url, const std::string& referrer) {
#ifdef EE_X_ANDROID
    nlohmann::json json;
    json["url"] = url;
    json["referrer"] = referrer;
    auto&& bridge = MessageBridge::getInstance();
    bridge.call(kShowInstallPrompt, json.dump());
#endif // EE_X_ANDROID
}

#ifdef EE_X_ANDROID
void log(const LogLevel& level, const std::string& tag,
         const std::string& message) {
    __android_log_print(level.priority, tag.c_str(), "%s", message.c_str());
}
#endif // EE_X_ANDROID

#if defined(EE_X_IOS) || defined(EE_X_OSX)
extern "C" {
void ee_staticLog(const char* message);
} // extern "C"

void log(const LogLevel& level, const std::string& tag,
         const std::string& message) {
    std::string buffer;
    buffer.reserve(level.desc.size() + 1 + tag.size() + 1 + message.size());
    buffer += level.desc;
    buffer += " ";
    buffer += tag;
    buffer += " ";
    buffer += message;
    ee_staticLog(buffer.c_str());
}
#endif // defined(EE_X_IOS) || defined(EE_X_OSX)
} // namespace core
} // namespace ee
<|endoftext|>


<commit_before>#include <iostream>
#include <random>
#include <queue>
#include <SFML/System.hpp>

#include "Map.h"
#include "PoissonDiskSampling/PoissonDiskSampling.h"
#include "Math/Vector2.h"
#include "Noise/Noise.h"

const std::vector<std::vector<BiomeType>> Map::m_elevationMoistureMatrix = MakeBiomeMatrix();

std::vector<std::vector<BiomeType>> Map::MakeBiomeMatrix()
{
	std::vector<std::vector<BiomeType>> matrix;
	std::vector<BiomeType> biomeVector;

	biomeVector.push_back(BiomeType::SubtropicalDesert);
	biomeVector.push_back(BiomeType::TemprateDesert);
	biomeVector.push_back(BiomeType::TemprateDesert);
	biomeVector.push_back(BiomeType::Mountain);
	matrix.push_back(biomeVector);
	biomeVector.clear();

	biomeVector.push_back(BiomeType::Grassland);
	biomeVector.push_back(BiomeType::Grassland);
	biomeVector.push_back(BiomeType::TemprateDesert);
	biomeVector.push_back(BiomeType::Mountain);
	matrix.push_back(biomeVector);
	biomeVector.clear();

	biomeVector.push_back(BiomeType::TropicalSeasonalForest);
	biomeVector.push_back(BiomeType::Grassland);
	biomeVector.push_back(BiomeType::Shrubland);
	biomeVector.push_back(BiomeType::Tundra);
	matrix.push_back(biomeVector);
	biomeVector.clear();

	biomeVector.push_back(BiomeType::TropicalSeasonalForest);
	biomeVector.push_back(BiomeType::TemprateDeciduousForest);
	biomeVector.push_back(BiomeType::Shrubland);
	biomeVector.push_back(BiomeType::Snow);
	matrix.push_back(biomeVector);
	biomeVector.clear();

	biomeVector.push_back(BiomeType::TropicalRainForest);
	biomeVector.push_back(BiomeType::TemprateDeciduousForest);
	biomeVector.push_back(BiomeType::Taiga);
	biomeVector.push_back(BiomeType::Snow);
	matrix.push_back(biomeVector);
	biomeVector.clear();

	biomeVector.push_back(BiomeType::TropicalRainForest);
	biomeVector.push_back(BiomeType::TemprateRainForest);
	biomeVector.push_back(BiomeType::Taiga);
	biomeVector.push_back(BiomeType::Snow);
	matrix.push_back(biomeVector);
	biomeVector.clear();

	return matrix;
}

Map::Map(int width, int height, double pointSpread, std::string seed) :
	m_mapWidth(width), m_mapHeight(height), m_pointSpread(pointSpread), m_zCoord(0.0),
	m_noiseMap(nullptr), m_seed(seed), m_centersQuadTree(AABB(Vector2(width / 2, height / 2), Vector2(width / 2, height / 2)), 1)
{
	double approxPointCount = (2 * m_mapWidth * m_mapHeight) / (3.1416 * m_pointSpread * m_pointSpread);
	int maxTreeDepth = floor((log(approxPointCount) / log(4)) + 0.5);
	QuadTree<Center*>::SetMaxDepth(maxTreeDepth);

	m_seed = seed != "" ? seed : CreateSeed(20);
	std::mt19937 mt_rand(HashString(m_seed));

	m_zCoord = mt_rand();
	std::cout << "Seed: " << m_seed << "(" << HashString(m_seed) << ")" << std::endl;
}

void Map::Generate()
{
	sf::Clock timer;

	GeneratePolygons();

	std::cout << "Land distribution: ";
	timer.restart();
	GenerateLand();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	// Elevation
	std::cout << "Coast assignment: ";
	timer.restart();
	AssignOceanCoastLand();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	std::cout << "Corner altitude: ";
	timer.restart();
	AssignCornerElevations();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	std::cout << "Altitude redistribution: ";
	timer.restart();
	RedistributeElevations();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	std::cout << "Center altitude: ";
	timer.restart();
	AssignPolygonElevations();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	// Moisture
	std::cout << "Downslopes: ";
	timer.restart();
	CalculateDownslopes();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	std::cout << "River generation: ";
	timer.restart();
	GenerateRivers();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	std::cout << "Corner moisture: ";
	timer.restart();
	AssignCornerMoisture();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	std::cout << "Moisture redistribution: ";
	timer.restart();
	RedistributeMoisture();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	std::cout << "Center moisture: ";
	timer.restart();
	AssignPolygonMoisture();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	// Biomes
	std::cout << "Biome assignment: ";
	timer.restart();
	AssignBiomes();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	std::cout << "Populate Quadtree: ";
	timer.restart();
	for (auto center : m_centers)
	{
		std::pair<Vector2, Vector2> aabb(center->GetBoundingBox());
		m_centersQuadTree.Insert2(center, AABB(aabb.first, aabb.second));
	}
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;
}

void Map::GeneratePolygons()
{
	sf::Clock timer;

	GeneratePoints();
	std::cout << "Point placement: " << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;
	timer.restart();

	Triangulate(m_points);
	std::cout << "Triangulation: " << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;
	timer.restart();

	FinishInfo();
	std::cout << "Finishing touches: " << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;
}

void Map::GenerateLand()
{
	m_noiseMap = new noise::module::Perlin();

	for (auto corner : m_corners)
	{
		if (!corner->IsInsideBoundingBox(m_mapWidth, m_mapHeight))
		{
			corner->m_border = true;
			corner->m_ocean = true;
			corner->m_water = true;
		}
	}

	for (auto corner : m_corners)
	{
		corner->m_water = !IsIsland(corner->m_position);
	}
}

std::vector<Edge*> Map::GetEdges() const
{
	return m_edges;
}

std::vector<Corner*> Map::GetCorners() const
{
	return m_corners;
}

std::vector<Center*> Map::GetCenters() const
{
	return m_centers;
}

Center* Map::GetCenterAt(Vector2 pos)
{
	Center* center = nullptr;
	std::vector<Center*> auxCenters = m_centersQuadTree.QueryRange(pos);

	if (auxCenters.size() > 0)
	{
		double minDist = Vector2(auxCenters[0]->m_position, pos).Length();
		center = auxCenters[0];

		for (auto auxCenter : auxCenters)
		{
			double newDist = Vector2(auxCenter->m_position, pos).Length();
			if (newDist < minDist)
			{
				minDist = newDist;
				center = auxCenter;
			}
		}
	}

	return center;
}

bool Map::IsIsland(Vector2 position)
{
	double waterThreshold = 0.075;

	if (position.x < m_mapWidth * waterThreshold || position.y < m_mapHeight * waterThreshold ||
		position.x > m_mapWidth * (1 - waterThreshold) || position.y > m_mapHeight * (1 - waterThreshold))
	{
		return false;
	}

	Vector2 centerPos = Vector2(m_mapWidth / 2.0, m_mapHeight / 2.0);
	position -= centerPos;

	double xCoord = (position.x / m_mapWidth) * 4;
	double yCoord = (position.y / m_mapHeight) * 4;
	double noiseVal = m_noiseMap->GetValue(xCoord, yCoord, m_zCoord);

	position /= std::min(m_mapWidth, m_mapHeight);
	double radius = position.Length();

	double factor = radius - 0.5;

	return noiseVal >= 0.3 * radius + factor;
}

void Map::CalculateDownslopes()
{
	for (auto c : m_corners)
	{
		Corner* d = c;
		for (auto q : c->m_corners)
		{
			if (q->m_elevation < d->m_elevation)
			{
				d = q;
			}
		}

		c->m_downslope = d;
	}
}

void Map::GenerateRivers()
{
	std::mt19937 mt_rand(HashString(m_seed));
	int numRivers = m_centers.size() / 3;

	for (int i = 0; i < numRivers; ++i)
	{
		Corner* q = m_corners[mt_rand() % m_corners.size()];

		if (q->m_ocean || q->m_elevation < 0.3 || q->m_elevation > 0.9)
		{
			continue;
		}

		while (!q->m_coast)
		{
			if (q == q->m_downslope)
			{
				break;
			}

			Edge* e = q->GetEdgeWith(q->m_downslope);
			e->m_riverVolume += 1;
			q->m_riverVolume += 1;
			q->m_downslope->m_riverVolume += 1;
			q = q->m_downslope;
		}
	}
}

void Map::AssignOceanCoastLand()
{
	std::queue<Center*> centersQueue;

	for (auto c : m_centers)
	{
		int adjacentWater = 0;

		for (auto q : c->m_centers)
		{
			if (q->m_border)
			{
				c->m_border = true;
				c->m_ocean = true;
				q->m_water = true;
				centersQueue.push(c);
			}

			if (q->m_water)
			{
				adjacentWater++;
			}
		}

		c->m_water = (c->m_ocean || adjacentWater >= c->m_corners.size() * 0.5);
	}

	while (!centersQueue.empty())
	{
		Center* c = centersQueue.front();
		centersQueue.pop();

		for (auto r : c->m_centers)
		{
			if (r->m_water && !r->m_ocean)
			{
				r->m_ocean = true;
				centersQueue.push(r);
			}
		}
	}

	for (auto p : m_centers)
	{
		int numOcean = 0;
		int numLand = 0;

		for (auto q : p->m_centers)
		{
			numOcean += static_cast<int>(q->m_ocean);
			numLand += static_cast<int>(!q->m_water);
		}

		p->m_coast = numLand > 0 && numOcean > 0;
	}

	for (auto c : m_corners)
	{
		int adjOcean = 0;
		int adjLand = 0;

		for (auto p : c->m_centers)
		{
			adjOcean += static_cast<int>(p->m_ocean);
			adjLand += static_cast<int>(!p->m_water);
		}

		c->m_ocean = adjOcean == c->m_centers.size();
		c->m_coast = adjLand > 0 && adjOcean > 0;
		c->m_water = c->m_border || (adjLand != c->m_centers.size() && !c->m_coast);
	}
}

void Map::RedistributeElevations()
{
	std::vector<Corner*> locations = GetLandCorners();
	const double SCALE_FACTOR = 1.05;

	sort(locations.begin(), locations.end(), &Corner::SortByElevation);

	for (int i = 0; i < locations.size(); ++i)
	{
		double y = static_cast<double>(i) / (locations.size() - 1);
		double x = sqrt(SCALE_FACTOR) - sqrt(SCALE_FACTOR * (1 - y));
		x = std::min(x, 1.0);
		locations[i]->m_elevation = x;
	}
}

void AssignCornerElevations();
void AssignPolygonElevations();
void RedistributeMoisture();
void AssignCornerMoisture();
void AssignPolygonMoisture();
void AssignBiomes();

void GeneratePoints();
void Triangulate(std::vector<DelaunayTriangulation::Vertex> points);
void FinishInfo();
void AddCenter(Center* c);
Center* GetCenter(Vector2 position);
void OrderPoints(std::vector<Corner*>& corners);

std::vector<Corner*> GetLandCorners();
std::vector<Corner*> GetLakeCorners();
void LloydRelaxation();
std::string CreateSeed(int length);

static unsigned int HashString(std::string seed);<commit_msg>Implement Map::AssignCornerElevations()<commit_after>#include <iostream>
#include <random>
#include <queue>
#include <SFML/System.hpp>

#include "Map.h"
#include "PoissonDiskSampling/PoissonDiskSampling.h"
#include "Math/Vector2.h"
#include "Noise/Noise.h"

const std::vector<std::vector<BiomeType>> Map::m_elevationMoistureMatrix = MakeBiomeMatrix();

std::vector<std::vector<BiomeType>> Map::MakeBiomeMatrix()
{
	std::vector<std::vector<BiomeType>> matrix;
	std::vector<BiomeType> biomeVector;

	biomeVector.push_back(BiomeType::SubtropicalDesert);
	biomeVector.push_back(BiomeType::TemprateDesert);
	biomeVector.push_back(BiomeType::TemprateDesert);
	biomeVector.push_back(BiomeType::Mountain);
	matrix.push_back(biomeVector);
	biomeVector.clear();

	biomeVector.push_back(BiomeType::Grassland);
	biomeVector.push_back(BiomeType::Grassland);
	biomeVector.push_back(BiomeType::TemprateDesert);
	biomeVector.push_back(BiomeType::Mountain);
	matrix.push_back(biomeVector);
	biomeVector.clear();

	biomeVector.push_back(BiomeType::TropicalSeasonalForest);
	biomeVector.push_back(BiomeType::Grassland);
	biomeVector.push_back(BiomeType::Shrubland);
	biomeVector.push_back(BiomeType::Tundra);
	matrix.push_back(biomeVector);
	biomeVector.clear();

	biomeVector.push_back(BiomeType::TropicalSeasonalForest);
	biomeVector.push_back(BiomeType::TemprateDeciduousForest);
	biomeVector.push_back(BiomeType::Shrubland);
	biomeVector.push_back(BiomeType::Snow);
	matrix.push_back(biomeVector);
	biomeVector.clear();

	biomeVector.push_back(BiomeType::TropicalRainForest);
	biomeVector.push_back(BiomeType::TemprateDeciduousForest);
	biomeVector.push_back(BiomeType::Taiga);
	biomeVector.push_back(BiomeType::Snow);
	matrix.push_back(biomeVector);
	biomeVector.clear();

	biomeVector.push_back(BiomeType::TropicalRainForest);
	biomeVector.push_back(BiomeType::TemprateRainForest);
	biomeVector.push_back(BiomeType::Taiga);
	biomeVector.push_back(BiomeType::Snow);
	matrix.push_back(biomeVector);
	biomeVector.clear();

	return matrix;
}

Map::Map(int width, int height, double pointSpread, std::string seed) :
	m_mapWidth(width), m_mapHeight(height), m_pointSpread(pointSpread), m_zCoord(0.0),
	m_noiseMap(nullptr), m_seed(seed), m_centersQuadTree(AABB(Vector2(width / 2, height / 2), Vector2(width / 2, height / 2)), 1)
{
	double approxPointCount = (2 * m_mapWidth * m_mapHeight) / (3.1416 * m_pointSpread * m_pointSpread);
	int maxTreeDepth = floor((log(approxPointCount) / log(4)) + 0.5);
	QuadTree<Center*>::SetMaxDepth(maxTreeDepth);

	m_seed = seed != "" ? seed : CreateSeed(20);
	std::mt19937 mt_rand(HashString(m_seed));

	m_zCoord = mt_rand();
	std::cout << "Seed: " << m_seed << "(" << HashString(m_seed) << ")" << std::endl;
}

void Map::Generate()
{
	sf::Clock timer;

	GeneratePolygons();

	std::cout << "Land distribution: ";
	timer.restart();
	GenerateLand();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	// Elevation
	std::cout << "Coast assignment: ";
	timer.restart();
	AssignOceanCoastLand();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	std::cout << "Corner altitude: ";
	timer.restart();
	AssignCornerElevations();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	std::cout << "Altitude redistribution: ";
	timer.restart();
	RedistributeElevations();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	std::cout << "Center altitude: ";
	timer.restart();
	AssignPolygonElevations();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	// Moisture
	std::cout << "Downslopes: ";
	timer.restart();
	CalculateDownslopes();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	std::cout << "River generation: ";
	timer.restart();
	GenerateRivers();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	std::cout << "Corner moisture: ";
	timer.restart();
	AssignCornerMoisture();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	std::cout << "Moisture redistribution: ";
	timer.restart();
	RedistributeMoisture();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	std::cout << "Center moisture: ";
	timer.restart();
	AssignPolygonMoisture();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	// Biomes
	std::cout << "Biome assignment: ";
	timer.restart();
	AssignBiomes();
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;

	std::cout << "Populate Quadtree: ";
	timer.restart();
	for (auto center : m_centers)
	{
		std::pair<Vector2, Vector2> aabb(center->GetBoundingBox());
		m_centersQuadTree.Insert2(center, AABB(aabb.first, aabb.second));
	}
	std::cout << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;
}

void Map::GeneratePolygons()
{
	sf::Clock timer;

	GeneratePoints();
	std::cout << "Point placement: " << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;
	timer.restart();

	Triangulate(m_points);
	std::cout << "Triangulation: " << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;
	timer.restart();

	FinishInfo();
	std::cout << "Finishing touches: " << timer.getElapsedTime().asMicroseconds() / 1000.0 << " ms." << std::endl;
}

void Map::GenerateLand()
{
	m_noiseMap = new noise::module::Perlin();

	for (auto corner : m_corners)
	{
		if (!corner->IsInsideBoundingBox(m_mapWidth, m_mapHeight))
		{
			corner->m_border = true;
			corner->m_ocean = true;
			corner->m_water = true;
		}
	}

	for (auto corner : m_corners)
	{
		corner->m_water = !IsIsland(corner->m_position);
	}
}

std::vector<Edge*> Map::GetEdges() const
{
	return m_edges;
}

std::vector<Corner*> Map::GetCorners() const
{
	return m_corners;
}

std::vector<Center*> Map::GetCenters() const
{
	return m_centers;
}

Center* Map::GetCenterAt(Vector2 pos)
{
	Center* center = nullptr;
	std::vector<Center*> auxCenters = m_centersQuadTree.QueryRange(pos);

	if (auxCenters.size() > 0)
	{
		double minDist = Vector2(auxCenters[0]->m_position, pos).Length();
		center = auxCenters[0];

		for (auto auxCenter : auxCenters)
		{
			double newDist = Vector2(auxCenter->m_position, pos).Length();
			if (newDist < minDist)
			{
				minDist = newDist;
				center = auxCenter;
			}
		}
	}

	return center;
}

bool Map::IsIsland(Vector2 position)
{
	double waterThreshold = 0.075;

	if (position.x < m_mapWidth * waterThreshold || position.y < m_mapHeight * waterThreshold ||
		position.x > m_mapWidth * (1 - waterThreshold) || position.y > m_mapHeight * (1 - waterThreshold))
	{
		return false;
	}

	Vector2 centerPos = Vector2(m_mapWidth / 2.0, m_mapHeight / 2.0);
	position -= centerPos;

	double xCoord = (position.x / m_mapWidth) * 4;
	double yCoord = (position.y / m_mapHeight) * 4;
	double noiseVal = m_noiseMap->GetValue(xCoord, yCoord, m_zCoord);

	position /= std::min(m_mapWidth, m_mapHeight);
	double radius = position.Length();

	double factor = radius - 0.5;

	return noiseVal >= 0.3 * radius + factor;
}

void Map::CalculateDownslopes()
{
	for (auto c : m_corners)
	{
		Corner* d = c;
		for (auto q : c->m_corners)
		{
			if (q->m_elevation < d->m_elevation)
			{
				d = q;
			}
		}

		c->m_downslope = d;
	}
}

void Map::GenerateRivers()
{
	std::mt19937 mt_rand(HashString(m_seed));
	int numRivers = m_centers.size() / 3;

	for (int i = 0; i < numRivers; ++i)
	{
		Corner* q = m_corners[mt_rand() % m_corners.size()];

		if (q->m_ocean || q->m_elevation < 0.3 || q->m_elevation > 0.9)
		{
			continue;
		}

		while (!q->m_coast)
		{
			if (q == q->m_downslope)
			{
				break;
			}

			Edge* e = q->GetEdgeWith(q->m_downslope);
			e->m_riverVolume += 1;
			q->m_riverVolume += 1;
			q->m_downslope->m_riverVolume += 1;
			q = q->m_downslope;
		}
	}
}

void Map::AssignOceanCoastLand()
{
	std::queue<Center*> centersQueue;

	for (auto c : m_centers)
	{
		int adjacentWater = 0;

		for (auto q : c->m_centers)
		{
			if (q->m_border)
			{
				c->m_border = true;
				c->m_ocean = true;
				q->m_water = true;
				centersQueue.push(c);
			}

			if (q->m_water)
			{
				adjacentWater++;
			}
		}

		c->m_water = (c->m_ocean || adjacentWater >= c->m_corners.size() * 0.5);
	}

	while (!centersQueue.empty())
	{
		Center* c = centersQueue.front();
		centersQueue.pop();

		for (auto r : c->m_centers)
		{
			if (r->m_water && !r->m_ocean)
			{
				r->m_ocean = true;
				centersQueue.push(r);
			}
		}
	}

	for (auto p : m_centers)
	{
		int numOcean = 0;
		int numLand = 0;

		for (auto q : p->m_centers)
		{
			numOcean += static_cast<int>(q->m_ocean);
			numLand += static_cast<int>(!q->m_water);
		}

		p->m_coast = numLand > 0 && numOcean > 0;
	}

	for (auto c : m_corners)
	{
		int adjOcean = 0;
		int adjLand = 0;

		for (auto p : c->m_centers)
		{
			adjOcean += static_cast<int>(p->m_ocean);
			adjLand += static_cast<int>(!p->m_water);
		}

		c->m_ocean = adjOcean == c->m_centers.size();
		c->m_coast = adjLand > 0 && adjOcean > 0;
		c->m_water = c->m_border || (adjLand != c->m_centers.size() && !c->m_coast);
	}
}

void Map::RedistributeElevations()
{
	std::vector<Corner*> locations = GetLandCorners();
	const double SCALE_FACTOR = 1.05;

	sort(locations.begin(), locations.end(), &Corner::SortByElevation);

	for (int i = 0; i < locations.size(); ++i)
	{
		double y = static_cast<double>(i) / (locations.size() - 1);
		double x = sqrt(SCALE_FACTOR) - sqrt(SCALE_FACTOR * (1 - y));
		x = std::min(x, 1.0);
		locations[i]->m_elevation = x;
	}
}

void Map::AssignCornerElevations()
{
	std::queue<Corner*> cornersQueue;

	for (auto q : m_corners)
	{
		if (q->m_border)
		{
			q->m_elevation = 0.0;
			cornersQueue.push(q);
		}
		else
		{
			q->m_elevation = 99999;
		}
	}

	while (!cornersQueue.empty())
	{
		Corner* q = cornersQueue.front();
		cornersQueue.pop();

		for (auto s : q->m_corners)
		{
			double newElevation = q->m_elevation + 0.01;

			if (!q->m_water && !s->m_water)
			{
				newElevation += 1;
			}

			if (newElevation < s->m_elevation)
			{
				s->m_elevation = newElevation;
				cornersQueue.push(s);
			}
		}
	}

	for (auto q : m_corners)
	{
		if (q->m_water)
		{
			q->m_elevation = 0.0;
		}
	}
}

void AssignPolygonElevations();
void RedistributeMoisture();
void AssignCornerMoisture();
void AssignPolygonMoisture();
void AssignBiomes();

void GeneratePoints();
void Triangulate(std::vector<DelaunayTriangulation::Vertex> points);
void FinishInfo();
void AddCenter(Center* c);
Center* GetCenter(Vector2 position);
void OrderPoints(std::vector<Corner*>& corners);

std::vector<Corner*> GetLandCorners();
std::vector<Corner*> GetLakeCorners();
void LloydRelaxation();
std::string CreateSeed(int length);

static unsigned int HashString(std::string seed);<|endoftext|>