// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2015 Benoit Jacob <benoitjacob@google.com>
//
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <iostream>
#include <cstdint>
#include <cstdlib>
#include <vector>
#include <algorithm>
#include <fstream>
#include <string>
#include <cmath>
#include <cassert>
#include <cstring>
#include <memory>

#include <Eigen/Core>

using namespace std;

const int default_precision = 4;

// see --only-cubic-sizes
bool only_cubic_sizes = false;

// see --dump-tables
bool dump_tables = false;

uint8_t log2_pot(size_t x) {
  size_t l = 0;
  while (x >>= 1) l++;
  return l;
}

uint16_t compact_size_triple(size_t k, size_t m, size_t n)
{
  return (log2_pot(k) << 8) | (log2_pot(m) << 4) | log2_pot(n);
}

// just a helper to store a triple of K,M,N sizes for matrix product
struct size_triple_t
{
  uint16_t k, m, n;
  size_triple_t() : k(0), m(0), n(0) {}
  size_triple_t(size_t _k, size_t _m, size_t _n) : k(_k), m(_m), n(_n) {}
  size_triple_t(const size_triple_t& o) : k(o.k), m(o.m), n(o.n) {}
  size_triple_t(uint16_t compact)
  {
    k = 1 << ((compact & 0xf00) >> 8);
    m = 1 << ((compact & 0x0f0) >> 4);
    n = 1 << ((compact & 0x00f) >> 0);
  }
  bool is_cubic() const { return k == m && m == n; }
};

ostream& operator<<(ostream& s, const size_triple_t& t)
{
  return s << "(" << t.k << ", " << t.m << ", " << t.n << ")";
}

struct inputfile_entry_t
{
  uint16_t product_size;
  uint16_t pot_block_size;
  size_triple_t nonpot_block_size;
  float gflops;
};

struct inputfile_t
{
  enum class type_t {
    unknown,
    all_pot_sizes,
    default_sizes
  };

  string filename;
  vector<inputfile_entry_t> entries;
  type_t type;

  inputfile_t(const string& fname)
    : filename(fname)
    , type(type_t::unknown)
  {
    ifstream stream(filename);
    if (!stream.is_open()) {
      cerr << "couldn't open input file: " << filename << endl;
      exit(1);
    }
    string line;
    while (getline(stream, line)) {
      if (line.empty()) continue;
      if (line.find("BEGIN MEASUREMENTS ALL POT SIZES") == 0) {
        if (type != type_t::unknown) {
          cerr << "Input file " << filename << " contains redundant BEGIN MEASUREMENTS lines";
          exit(1);
        }
        type = type_t::all_pot_sizes;
        continue;
      }
      if (line.find("BEGIN MEASUREMENTS DEFAULT SIZES") == 0) {
        if (type != type_t::unknown) {
          cerr << "Input file " << filename << " contains redundant BEGIN MEASUREMENTS lines";
          exit(1);
        }
        type = type_t::default_sizes;
        continue;
      }

      if (type == type_t::unknown) {
        continue;
      }
      switch(type) {
        case type_t::all_pot_sizes: {
          unsigned int product_size, block_size;
          float gflops;
          int sscanf_result =
            sscanf(line.c_str(), "%x %x %f",
                   &product_size,
                   &block_size,
                   &gflops);
          if (3 != sscanf_result ||
              !product_size ||
              product_size > 0xfff ||
              !block_size ||
              block_size > 0xfff ||
              !isfinite(gflops))
          {
            cerr << "ill-formed input file: " << filename << endl;
            cerr << "offending line:" << endl << line << endl;
            exit(1);
          }
          if (only_cubic_sizes && !size_triple_t(product_size).is_cubic()) {
            continue;
          }
          inputfile_entry_t entry;
          entry.product_size = uint16_t(product_size);
          entry.pot_block_size = uint16_t(block_size);
          entry.gflops = gflops;
          entries.push_back(entry);
          break;
        }
        case type_t::default_sizes: {
          unsigned int product_size;
          float gflops;
          int bk, bm, bn;
          int sscanf_result =
            sscanf(line.c_str(), "%x default(%d, %d, %d) %f",
                   &product_size,
                   &bk, &bm, &bn,
                   &gflops);
          if (5 != sscanf_result ||
              !product_size ||
              product_size > 0xfff ||
              !isfinite(gflops))
          {
            cerr << "ill-formed input file: " << filename << endl;
            cerr << "offending line:" << endl << line << endl;
            exit(1);
          }
          if (only_cubic_sizes && !size_triple_t(product_size).is_cubic()) {
            continue;
          }
          inputfile_entry_t entry;
          entry.product_size = uint16_t(product_size);
          entry.pot_block_size = 0;
          entry.nonpot_block_size = size_triple_t(bk, bm, bn);
          entry.gflops = gflops;
          entries.push_back(entry);
          break;
        }

        default:
          break;
      }
    }
    stream.close();
    if (type == type_t::unknown) {
      cerr << "Unrecognized input file " << filename << endl;
      exit(1);
    }
    if (entries.empty()) {
      cerr << "didn't find any measurements in input file: " << filename << endl;
      exit(1);
    }
  }
};

struct preprocessed_inputfile_entry_t
{
  uint16_t product_size;
  uint16_t block_size;

  float efficiency;
};

bool lower_efficiency(const preprocessed_inputfile_entry_t& e1, const preprocessed_inputfile_entry_t& e2)
{
  return e1.efficiency < e2.efficiency;
}

struct preprocessed_inputfile_t
{
  string filename;
  vector<preprocessed_inputfile_entry_t> entries;

  preprocessed_inputfile_t(const inputfile_t& inputfile)
    : filename(inputfile.filename)
  {
    if (inputfile.type != inputfile_t::type_t::all_pot_sizes) {
      abort();
    }
    auto it = inputfile.entries.begin();
    auto it_first_with_given_product_size = it;
    while (it != inputfile.entries.end()) {
      ++it;
      if (it == inputfile.entries.end() ||
        it->product_size != it_first_with_given_product_size->product_size)
      {
        import_input_file_range_one_product_size(it_first_with_given_product_size, it);
        it_first_with_given_product_size = it;
      }
    }
  }

private:
  void import_input_file_range_one_product_size(
    const vector<inputfile_entry_t>::const_iterator& begin,
    const vector<inputfile_entry_t>::const_iterator& end)
  {
    uint16_t product_size = begin->product_size;
    float max_gflops = 0.0f;
    for (auto it = begin; it != end; ++it) {
      if (it->product_size != product_size) {
        cerr << "Unexpected ordering of entries in " << filename << endl;
        cerr << "(Expected all entries for product size " << hex << product_size << dec << " to be grouped)" << endl;
        exit(1);
      }
      max_gflops = max(max_gflops, it->gflops);
    }
    for (auto it = begin; it != end; ++it) {
      preprocessed_inputfile_entry_t entry;
      entry.product_size = it->product_size;
      entry.block_size = it->pot_block_size;
      entry.efficiency = it->gflops / max_gflops;
      entries.push_back(entry);
    }
  }
};

void check_all_files_in_same_exact_order(
       const vector<preprocessed_inputfile_t>& preprocessed_inputfiles)
{
  if (preprocessed_inputfiles.empty()) {
    return;
  }

  const preprocessed_inputfile_t& first_file = preprocessed_inputfiles[0];
  const size_t num_entries = first_file.entries.size();

  for (size_t i = 0; i < preprocessed_inputfiles.size(); i++) {
    if (preprocessed_inputfiles[i].entries.size() != num_entries) {
      cerr << "these files have different number of entries: "
           << preprocessed_inputfiles[i].filename
           << " and "
           << first_file.filename
           << endl;
      exit(1);
    }
  }

  for (size_t entry_index = 0; entry_index < num_entries; entry_index++) {
    const uint16_t entry_product_size = first_file.entries[entry_index].product_size;
    const uint16_t entry_block_size = first_file.entries[entry_index].block_size;
    for (size_t file_index = 0; file_index < preprocessed_inputfiles.size(); file_index++) {
      const preprocessed_inputfile_t& cur_file = preprocessed_inputfiles[file_index];
      if (cur_file.entries[entry_index].product_size != entry_product_size ||
          cur_file.entries[entry_index].block_size != entry_block_size)
      {
        cerr << "entries not in same order between these files: "
             << first_file.filename
             << " and "
             << cur_file.filename
             << endl;
        exit(1);
      }
    }
  }
}

float efficiency_of_subset(
        const vector<preprocessed_inputfile_t>& preprocessed_inputfiles,
        const vector<size_t>& subset)
{
  if (subset.size() <= 1) {
    return 1.0f;
  }
  const preprocessed_inputfile_t& first_file = preprocessed_inputfiles[subset[0]];
  const size_t num_entries = first_file.entries.size();
  float efficiency = 1.0f;
  size_t entry_index = 0;
  size_t first_entry_index_with_this_product_size = 0;
  uint16_t product_size = first_file.entries[0].product_size;
  while (entry_index < num_entries) {
    ++entry_index;
    if (entry_index == num_entries ||
        first_file.entries[entry_index].product_size != product_size)
    {
      float efficiency_this_product_size = 0.0f;
      for (size_t e = first_entry_index_with_this_product_size; e < entry_index; e++) {
        float efficiency_this_entry = 1.0f;
        for (auto i = subset.begin(); i != subset.end(); ++i) {
          efficiency_this_entry = min(efficiency_this_entry, preprocessed_inputfiles[*i].entries[e].efficiency);
        }
        efficiency_this_product_size = max(efficiency_this_product_size, efficiency_this_entry);
      }
      efficiency = min(efficiency, efficiency_this_product_size);
      if (entry_index < num_entries) {
        first_entry_index_with_this_product_size = entry_index;
        product_size = first_file.entries[entry_index].product_size;
      }
    }
  }

  return efficiency;
}

void dump_table_for_subset(
        const vector<preprocessed_inputfile_t>& preprocessed_inputfiles,
        const vector<size_t>& subset)
{
  const preprocessed_inputfile_t& first_file = preprocessed_inputfiles[subset[0]];
  const size_t num_entries = first_file.entries.size();
  size_t entry_index = 0;
  size_t first_entry_index_with_this_product_size = 0;
  uint16_t product_size = first_file.entries[0].product_size;
  size_t i = 0;
  size_triple_t min_product_size(first_file.entries.front().product_size);
  size_triple_t max_product_size(first_file.entries.back().product_size);
  if (!min_product_size.is_cubic() || !max_product_size.is_cubic()) {
    abort();
  }
  if (only_cubic_sizes) {
    cerr << "Can't generate tables with --only-cubic-sizes." << endl;
    abort();
  }
  cout << "struct LookupTable {" << endl;
  cout << "  static const size_t BaseSize = " << min_product_size.k << ";" << endl;
  const size_t NumSizes = log2_pot(max_product_size.k / min_product_size.k) + 1;
  const size_t TableSize = NumSizes * NumSizes * NumSizes;
  cout << "  static const size_t NumSizes = " << NumSizes << ";" << endl;
  cout << "  static const unsigned short* Data() {" << endl;
  cout << "    static const unsigned short data[" << TableSize << "] = {";
  while (entry_index < num_entries) {
    ++entry_index;
    if (entry_index == num_entries ||
        first_file.entries[entry_index].product_size != product_size)
    {
      float best_efficiency_this_product_size = 0.0f;
      uint16_t best_block_size_this_product_size = 0;
      for (size_t e = first_entry_index_with_this_product_size; e < entry_index; e++) {
        float efficiency_this_entry = 1.0f;
        for (auto i = subset.begin(); i != subset.end(); ++i) {
          efficiency_this_entry = min(efficiency_this_entry, preprocessed_inputfiles[*i].entries[e].efficiency);
        }
        if (efficiency_this_entry > best_efficiency_this_product_size) {
          best_efficiency_this_product_size = efficiency_this_entry;
          best_block_size_this_product_size = first_file.entries[e].block_size;
        }
      }
      if ((i++) % NumSizes) {
        cout << " ";
      } else {
        cout << endl << "      ";
      }
      cout << "0x" << hex << best_block_size_this_product_size << dec;
      if (entry_index < num_entries) {
        cout << ",";
        first_entry_index_with_this_product_size = entry_index;
        product_size = first_file.entries[entry_index].product_size;
      }
    }
  }
  if (i != TableSize) {
    cerr << endl << "Wrote " << i << " table entries, expected " << TableSize << endl;
    abort();
  }
  cout << endl << "    };" << endl;
  cout << "    return data;" << endl;
  cout << "  }" << endl;
  cout << "};" << endl;
}

float efficiency_of_partition(
        const vector<preprocessed_inputfile_t>& preprocessed_inputfiles,
        const vector<vector<size_t>>& partition)
{
  float efficiency = 1.0f;
  for (auto s = partition.begin(); s != partition.end(); ++s) {
    efficiency = min(efficiency, efficiency_of_subset(preprocessed_inputfiles, *s));
  }
  return efficiency;
}

void make_first_subset(size_t subset_size, vector<size_t>& out_subset, size_t set_size)
{
  assert(subset_size >= 1 && subset_size <= set_size);
  out_subset.resize(subset_size);
  for (size_t i = 0; i < subset_size; i++) {
    out_subset[i] = i;
  }
}

bool is_last_subset(const vector<size_t>& subset, size_t set_size)
{
  return subset[0] == set_size - subset.size();
}

void next_subset(vector<size_t>& inout_subset, size_t set_size)
{
  if (is_last_subset(inout_subset, set_size)) {
    cerr << "iterating past the last subset" << endl;
    abort();
  }
  size_t i = 1;
  while (inout_subset[inout_subset.size() - i] == set_size - i) {
    i++;
    assert(i <= inout_subset.size());
  }
  size_t first_index_to_change = inout_subset.size() - i;
  inout_subset[first_index_to_change]++;
  size_t p = inout_subset[first_index_to_change];
  for (size_t j = first_index_to_change + 1; j < inout_subset.size(); j++) {
    inout_subset[j] = ++p;
  }
}

const size_t number_of_subsets_limit = 100;
const size_t always_search_subsets_of_size_at_least = 2;

bool is_number_of_subsets_feasible(size_t n, size_t p)
{
  assert(n>0 && p>0 && p<=n);
  uint64_t numerator = 1, denominator = 1;
  for (size_t i = 0; i < p; i++) {
    numerator *= n - i;
    denominator *= i + 1;
    if (numerator > denominator * number_of_subsets_limit) {
      return false;
    }
  }
  return true;
}

size_t max_feasible_subset_size(size_t n)
{
  assert(n > 0);
  const size_t minresult = min<size_t>(n-1, always_search_subsets_of_size_at_least);
  for (size_t p = 1; p <= n - 1; p++) {
    if (!is_number_of_subsets_feasible(n, p+1)) {
      return max(p, minresult);
    }
  }
  return n - 1;
}

void find_subset_with_efficiency_higher_than(
       const vector<preprocessed_inputfile_t>& preprocessed_inputfiles,
       float required_efficiency_to_beat,
       vector<size_t>& inout_remainder,
       vector<size_t>& out_subset)
{
  out_subset.resize(0);

  if (required_efficiency_to_beat >= 1.0f) {
    cerr << "can't beat efficiency 1." << endl;
    abort();
  }

  while (!inout_remainder.empty()) {

    vector<size_t> candidate_indices(inout_remainder.size());
    for (size_t i = 0; i < candidate_indices.size(); i++) {
      candidate_indices[i] = i;
    }

    size_t candidate_indices_subset_size = max_feasible_subset_size(candidate_indices.size());
    while (candidate_indices_subset_size >= 1) {
      vector<size_t> candidate_indices_subset;
      make_first_subset(candidate_indices_subset_size,
                        candidate_indices_subset,
                        candidate_indices.size());

      vector<size_t> best_candidate_indices_subset;
      float best_efficiency = 0.0f;
      vector<size_t> trial_subset = out_subset;
      trial_subset.resize(out_subset.size() + candidate_indices_subset_size);
      while (true)
      {
        for (size_t i = 0; i < candidate_indices_subset_size; i++) {
          trial_subset[out_subset.size() + i] = inout_remainder[candidate_indices_subset[i]];
        }

        float trial_efficiency = efficiency_of_subset(preprocessed_inputfiles, trial_subset);
        if (trial_efficiency > best_efficiency) {
          best_efficiency = trial_efficiency;
          best_candidate_indices_subset = candidate_indices_subset;
        }
        if (is_last_subset(candidate_indices_subset, candidate_indices.size())) {
          break;
        }
        next_subset(candidate_indices_subset, candidate_indices.size());
      }

      if (best_efficiency > required_efficiency_to_beat) {
        for (size_t i = 0; i < best_candidate_indices_subset.size(); i++) {
          candidate_indices[i] = candidate_indices[best_candidate_indices_subset[i]];
        }
        candidate_indices.resize(best_candidate_indices_subset.size());
      }
      candidate_indices_subset_size--;
    }

    size_t candidate_index = candidate_indices[0];
    auto candidate_iterator = inout_remainder.begin() + candidate_index;
    vector<size_t> trial_subset = out_subset;

    trial_subset.push_back(*candidate_iterator);
    float trial_efficiency = efficiency_of_subset(preprocessed_inputfiles, trial_subset);
    if (trial_efficiency > required_efficiency_to_beat) {
      out_subset.push_back(*candidate_iterator);
      inout_remainder.erase(candidate_iterator);
    } else {
      break;
    }
  }
}

void find_partition_with_efficiency_higher_than(
       const vector<preprocessed_inputfile_t>& preprocessed_inputfiles,
       float required_efficiency_to_beat,
       vector<vector<size_t>>& out_partition)
{
  out_partition.resize(0);

  vector<size_t> remainder;
  for (size_t i = 0; i < preprocessed_inputfiles.size(); i++) {
    remainder.push_back(i);
  }

  while (!remainder.empty()) {
    vector<size_t> new_subset;
    find_subset_with_efficiency_higher_than(
      preprocessed_inputfiles,
      required_efficiency_to_beat,
      remainder,
      new_subset);
    out_partition.push_back(new_subset);
  }
}

void print_partition(
       const vector<preprocessed_inputfile_t>& preprocessed_inputfiles,
       const vector<vector<size_t>>& partition)
{
  float efficiency = efficiency_of_partition(preprocessed_inputfiles, partition);
  cout << "Partition into " << partition.size() << " subsets for " << efficiency * 100.0f << "% efficiency"  << endl;
  for (auto subset = partition.begin(); subset != partition.end(); ++subset) {
    cout << "  Subset " << (subset - partition.begin())
         << ", efficiency " << efficiency_of_subset(preprocessed_inputfiles, *subset) * 100.0f << "%:"
         << endl;
    for (auto file = subset->begin(); file != subset->end(); ++file) {
      cout << "    " << preprocessed_inputfiles[*file].filename << endl;
    }
    if (dump_tables) {
      cout << "  Table:" << endl;
      dump_table_for_subset(preprocessed_inputfiles, *subset);
    }
  }
  cout << endl;
}

struct action_t
{
  virtual const char* invokation_name() const { abort(); return nullptr; }
  virtual void run(const vector<string>&) const { abort(); }
  virtual ~action_t() {}
};

struct partition_action_t : action_t
{
  virtual const char* invokation_name() const override { return "partition"; }
  virtual void run(const vector<string>& input_filenames) const override
  {
    vector<preprocessed_inputfile_t> preprocessed_inputfiles;

    if (input_filenames.empty()) {
      cerr << "The " << invokation_name() << " action needs a list of input files." << endl;
      exit(1);
    }

    for (auto it = input_filenames.begin(); it != input_filenames.end(); ++it) {
      inputfile_t inputfile(*it);
      switch (inputfile.type) {
        case inputfile_t::type_t::all_pot_sizes:
          preprocessed_inputfiles.emplace_back(inputfile);
          break;
        case inputfile_t::type_t::default_sizes:
          cerr << "The " << invokation_name() << " action only uses measurements for all pot sizes, and "
               << "has no use for " << *it << " which contains measurements for default sizes." << endl;
          exit(1);
          break;
        default:
          cerr << "Unrecognized input file: " << *it << endl;
          exit(1);
      }
    }

    check_all_files_in_same_exact_order(preprocessed_inputfiles);

    float required_efficiency_to_beat = 0.0f;
    vector<vector<vector<size_t>>> partitions;
    cerr << "searching for partitions...\r" << flush;
    while (true)
    {
      vector<vector<size_t>> partition;
      find_partition_with_efficiency_higher_than(
        preprocessed_inputfiles,
        required_efficiency_to_beat,
        partition);
      float actual_efficiency = efficiency_of_partition(preprocessed_inputfiles, partition);
      cerr << "partition " << preprocessed_inputfiles.size() << " files into " << partition.size()
           << " subsets for " << 100.0f * actual_efficiency
           << " % efficiency"
           << "                  \r" << flush;
      partitions.push_back(partition);
      if (partition.size() == preprocessed_inputfiles.size() || actual_efficiency == 1.0f) {
        break;
      }
      required_efficiency_to_beat = actual_efficiency;
    }
    cerr << "                                                                  " << endl;
    while (true) {
      bool repeat = false;
      for (size_t i = 0; i < partitions.size() - 1; i++) {
        if (partitions[i].size() >= partitions[i+1].size()) {
          partitions.erase(partitions.begin() + i);
          repeat = true;
          break;
        }
      }
      if (!repeat) {
        break;
      }
    }
    for (auto it = partitions.begin(); it != partitions.end(); ++it) {
      print_partition(preprocessed_inputfiles, *it);
    }
  }
};

struct evaluate_defaults_action_t : action_t
{
  struct results_entry_t {
    uint16_t product_size;
    size_triple_t default_block_size;
    uint16_t best_pot_block_size;
    float default_gflops;
    float best_pot_gflops;
    float default_efficiency;
  };
  friend ostream& operator<<(ostream& s, const results_entry_t& entry)
  {
    return s
      << "Product size " << size_triple_t(entry.product_size)
      << ": default block size " << entry.default_block_size
      << " -> " << entry.default_gflops
      << " GFlop/s = " << entry.default_efficiency * 100.0f << " %"
      << " of best POT block size " << size_triple_t(entry.best_pot_block_size)
      << " -> " << entry.best_pot_gflops
      << " GFlop/s" << dec;
  }
  static bool lower_efficiency(const results_entry_t& e1, const results_entry_t& e2) {
    return e1.default_efficiency < e2.default_efficiency;
  }
  virtual const char* invokation_name() const override { return "evaluate-defaults"; }
  void show_usage_and_exit() const
  {
    cerr << "usage: " << invokation_name() << " default-sizes-data all-pot-sizes-data" << endl;
    cerr << "checks how well the performance with default sizes compares to the best "
         << "performance measured over all POT sizes." << endl;
    exit(1);
  }
  virtual void run(const vector<string>& input_filenames) const override
  {
    if (input_filenames.size() != 2) {
      show_usage_and_exit();
    }
    inputfile_t inputfile_default_sizes(input_filenames[0]);
    inputfile_t inputfile_all_pot_sizes(input_filenames[1]);
    if (inputfile_default_sizes.type != inputfile_t::type_t::default_sizes) {
      cerr << inputfile_default_sizes.filename << " is not an input file with default sizes." << endl;
      show_usage_and_exit();
    }
    if (inputfile_all_pot_sizes.type != inputfile_t::type_t::all_pot_sizes) {
      cerr << inputfile_all_pot_sizes.filename << " is not an input file with all POT sizes." << endl;
      show_usage_and_exit();
    }
    vector<results_entry_t> results;
    vector<results_entry_t> cubic_results;

    uint16_t product_size = 0;
    auto it_all_pot_sizes = inputfile_all_pot_sizes.entries.begin();
    for (auto it_default_sizes = inputfile_default_sizes.entries.begin();
         it_default_sizes != inputfile_default_sizes.entries.end();
         ++it_default_sizes)
    {
      if (it_default_sizes->product_size == product_size) {
        continue;
      }
      product_size = it_default_sizes->product_size;
      while (it_all_pot_sizes != inputfile_all_pot_sizes.entries.end() &&
             it_all_pot_sizes->product_size != product_size)
      {
        ++it_all_pot_sizes;
      }
      if (it_all_pot_sizes == inputfile_all_pot_sizes.entries.end()) {
        break;
      }
      uint16_t best_pot_block_size = 0;
      float best_pot_gflops = 0;
      for (auto it = it_all_pot_sizes;
           it != inputfile_all_pot_sizes.entries.end() && it->product_size == product_size;
           ++it)
      {
        if (it->gflops > best_pot_gflops) {
          best_pot_gflops = it->gflops;
          best_pot_block_size = it->pot_block_size;
        }
      }
      results_entry_t entry;
      entry.product_size = product_size;
      entry.default_block_size = it_default_sizes->nonpot_block_size;
      entry.best_pot_block_size = best_pot_block_size;
      entry.default_gflops = it_default_sizes->gflops;
      entry.best_pot_gflops = best_pot_gflops;
      entry.default_efficiency = entry.default_gflops / entry.best_pot_gflops;
      results.push_back(entry);

      size_triple_t t(product_size);
      if (t.k == t.m && t.m == t.n) {
        cubic_results.push_back(entry);
      }
    }

    cout << "All results:" << endl;
    for (auto it = results.begin(); it != results.end(); ++it) {
      cout << *it << endl;
    }
    cout << endl;

    sort(results.begin(), results.end(), lower_efficiency);

    const size_t n = min<size_t>(20, results.size());
    cout << n << " worst results:" << endl;
    for (size_t i = 0; i < n; i++) {
      cout << results[i] << endl;
    }
    cout << endl;

    cout << "cubic results:" << endl;
    for (auto it = cubic_results.begin(); it != cubic_results.end(); ++it) {
      cout << *it << endl;
    }
    cout << endl;

    sort(cubic_results.begin(), cubic_results.end(), lower_efficiency);

    cout.precision(2);
    vector<float> a = {0.5f, 0.20f, 0.10f, 0.05f, 0.02f, 0.01f};
    for (auto it = a.begin(); it != a.end(); ++it) {
      size_t n = min(results.size() - 1, size_t(*it * results.size()));
      cout << (100.0f * n / (results.size() - 1))
           << " % of product sizes have default efficiency <= "
           << 100.0f * results[n].default_efficiency << " %" << endl;
    }
    cout.precision(default_precision);
  }
};

void show_usage_and_exit(int argc, char* argv[],
                         const vector<unique_ptr<action_t>>& available_actions)
{
  cerr << "usage: " << argv[0] << " <action> [options...] <input files...>" << endl;
  cerr << "available actions:" << endl;
  for (auto it = available_actions.begin(); it != available_actions.end(); ++it) {
    cerr << "  " << (*it)->invokation_name() << endl;
  }
  cerr << "the input files should each contain an output of benchmark-blocking-sizes" << endl;
  exit(1);
}

int main(int argc, char* argv[])
{
  cout.precision(default_precision);
  cerr.precision(default_precision);

  vector<unique_ptr<action_t>> available_actions;
  available_actions.emplace_back(new partition_action_t);
  available_actions.emplace_back(new evaluate_defaults_action_t);

  vector<string> input_filenames;

  action_t* action = nullptr;

  if (argc < 2) {
    show_usage_and_exit(argc, argv, available_actions);
  }
  for (int i = 1; i < argc; i++) {
    bool arg_handled = false;
    // Step 1. Try to match action invokation names.
    for (auto it = available_actions.begin(); it != available_actions.end(); ++it) {
      if (!strcmp(argv[i], (*it)->invokation_name())) {
        if (!action) {
          action = it->get();
          arg_handled = true;
          break;
        } else {
          cerr << "can't specify more than one action!" << endl;
          show_usage_and_exit(argc, argv, available_actions);
        }
      }
    }
    if (arg_handled) {
      continue;
    }
    // Step 2. Try to match option names.
    if (argv[i][0] == '-') {
      if (!strcmp(argv[i], "--only-cubic-sizes")) {
        only_cubic_sizes = true;
        arg_handled = true;
      }
      if (!strcmp(argv[i], "--dump-tables")) {
        dump_tables = true;
        arg_handled = true;
      }
      if (!arg_handled) {
        cerr << "Unrecognized option: " << argv[i] << endl;
        show_usage_and_exit(argc, argv, available_actions);
      }
    }
    if (arg_handled) {
      continue;
    }
    // Step 3. Default to interpreting args as input filenames.
    input_filenames.emplace_back(argv[i]);
  }

  if (dump_tables && only_cubic_sizes) {
    cerr << "Incompatible options: --only-cubic-sizes and --dump-tables." << endl;
    show_usage_and_exit(argc, argv, available_actions);
  }

  if (!action) {
    show_usage_and_exit(argc, argv, available_actions);
  }

  action->run(input_filenames);
}


// Boost.Units - A C++ library for zero-overhead dimensional analysis and
// unit/quantity manipulation and conversion
//
// Copyright (C) 2008 Matthias Christian Schabel
// Copyright (C) 2008 Steven Watanabe
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)

/**
\file

\brief test_custom_unit.cpp

\details
Make sure that a minimal + - * / unit class is fully functional.

Output:
@verbatim
@endverbatim
**/

#include <boost/units/quantity.hpp>

#include <boost/core/lightweight_test.hpp>

namespace bu = boost::units;

template<int Mass, int Length, int Time>
struct simple_unit {};

BOOST_TYPEOF_REGISTER_TEMPLATE(simple_unit, (int)(int)(int))

template<int Mass, int Length, int Time>
simple_unit<Mass, Length, Time> operator+(const simple_unit<Mass, Length, Time>&,
                                          const simple_unit<Mass, Length, Time>&)
{
    return simple_unit<Mass, Length, Time>();
}

template<int Mass, int Length, int Time>
simple_unit<Mass, Length, Time> operator-(const simple_unit<Mass, Length, Time>&,
                                          const simple_unit<Mass, Length, Time>&)
{
    return simple_unit<Mass, Length, Time>();
}

template<int Mass1, int Length1, int Time1, int Mass2, int Length2, int Time2>
simple_unit<Mass1 + Mass2, Length1 + Length2, Time1 + Time2>
operator*(const simple_unit<Mass1, Length1, Time1>&,
          const simple_unit<Mass2, Length2, Time2>&)
{
    return simple_unit<Mass1 + Mass2, Length1 + Length2, Time1 + Time2>();
}

template<int Mass1, int Length1, int Time1, int Mass2, int Length2, int Time2>
simple_unit<Mass1 - Mass2, Length1 - Length2, Time1 - Time2>
operator/(const simple_unit<Mass1, Length1, Time1>&,
          const simple_unit<Mass2, Length2, Time2>&)
{
    return simple_unit<Mass1 - Mass2, Length1 - Length2, Time1 - Time2>();
}

int main()
{
    bu::quantity<simple_unit<1, 0, 0> > mass = bu::quantity<simple_unit<1, 0, 0> >::from_value(2);
    bu::quantity<simple_unit<0, 1, 0> > length = bu::quantity<simple_unit<0, 1, 0> >::from_value(4);

    bu::quantity<simple_unit<1, 1, 0> > ml = mass * length;
    bu::quantity<simple_unit<1, -1, 0> > m_per_l = mass/length;

    BOOST_TEST(ml.value() == 8);
    BOOST_TEST(m_per_l.value() == 0.5);

    mass += mass;

    BOOST_TEST(mass.value() == 4);

    length -= length;
    BOOST_TEST(length.value() == 0);

    return boost::report_errors();
}


#include <boost/sort/spreadsort/integer_sort.hpp>


/*
 * Copyright 2018-2019 CNRS-UM LIRMM, CNRS-AIST JRL
 */

#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE Eigen_OSQP
#include <boost/test/unit_test.hpp>
#include <boost/math/constants/constants.hpp>
#include <boost/test/floating_point_comparison.hpp>

#include "eigen-osqp/OSQP.h"
#include "systems.h"

BOOST_FIXTURE_TEST_CASE(DENSE_MATRIX, QP1Dense)
{
  auto checkMatrix = [](const MatrixDense& matA, const MatrixDense& matConvA) {
    BOOST_REQUIRE_EQUAL(matA.rows(), matConvA.rows());
    BOOST_REQUIRE_EQUAL(matA.cols(), matConvA.cols());
    for (Eigen::Index i = 0; i < matA.rows(); ++i)
      for (Eigen::Index j = 0; j < matA.cols(); ++j)
        BOOST_REQUIRE_SMALL(std::abs(matA(i,j) - matConvA(i,j)), 1e-10);
  };

  Eigen::CSCMatrix cscA{};
  cscA.updateDefault(A);
  Eigen::MatrixXd convA = cscA.toDenseEigen();

  checkMatrix(A, convA);

  // Add block identity matrix underneath
  cscA.updateAndAddIdentity(A);
  Eigen::MatrixXd AId(A.rows() + nrvar, A.cols());
  AId.block(0, 0, A.rows(), A.cols()) = A;
  AId.block(A.rows(), 0, nrvar, nrvar).setIdentity();

  convA = cscA.toDenseEigen();

  checkMatrix(AId, convA);
}

BOOST_FIXTURE_TEST_CASE(SPARSE_MATRIX, QP1Sparse)
{
  auto checkMatrix = [](const MatrixSparse& matA, const MatrixSparse& matConvA) {
    BOOST_REQUIRE(matConvA.isCompressed());
    BOOST_REQUIRE_EQUAL(matA.rows(), matConvA.rows());
    BOOST_REQUIRE_EQUAL(matA.cols(), matConvA.cols());
    BOOST_REQUIRE_EQUAL(matA.outerSize(), matConvA.outerSize());
    for(int k = 0; k < matA.outerSize(); ++k)
    {
      for(MatrixSparse::InnerIterator itA(matA, k), itCA(matConvA, k); itA; ++itA, ++itCA)
      {
        BOOST_REQUIRE_EQUAL(itA.row(), itCA.row());
        BOOST_REQUIRE_EQUAL(itA.col(), itCA.col());
        BOOST_REQUIRE_SMALL(std::abs(itA.value() - itCA.value()), 1e-10);
      }
    }
  };

  Eigen::CSCMatrix cscA{};
  cscA.updateDefault(A);
  MatrixSparse convA = cscA.toSparseEigen();

  checkMatrix(A, convA);

  // Add block identity matrix underneath
  cscA.updateAndAddIdentity(A);

  Eigen::MatrixXd tmp(A.rows() + nrvar, A.cols());
  tmp.block(0, 0, A.rows(), A.cols()) = A;
  tmp.block(A.rows(), 0, nrvar, nrvar).setIdentity();
  MatrixSparse AId = tmp.sparseView();
  AId.makeCompressed();

  convA = cscA.toSparseEigen();

  checkMatrix(AId, convA);
}


//==================================================================================================
/*!
  @file

  @copyright 2017 NumScale SAS

  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt)
**/
//==================================================================================================
#ifndef BOOST_SIMD_ALGORITHM_REPLACE_IF_HPP_INCLUDED
#define BOOST_SIMD_ALGORITHM_REPLACE_IF_HPP_INCLUDED

#include <boost/simd/algorithm/transform.hpp>
#include <boost/simd/function/if_else.hpp>
#include <boost/simd/pack.hpp>

namespace boost { namespace simd
{
  /*!
    @ingroup group-algo

    Replaces all elements  for which predicate p returns true
    by new_value in the range [first, last).

    @param first   Beginning of the range of elements
    @param last    End of the range of elements
    @param p       unary predicate which returns @c true if the element value should be replaced.
    @param new_val Replacement value

    @par Requirement

      - @c first, @c last and @c out must be pointer to Vectorizable type.

      - @c new_val must be a scalar value convertible to  pointee type of first.

    @par Example:

       @snippet replace_if.cpp replace_if

    @par possible output:

       @snippet replace_if.txt replace_if

  **/

  template<typename T, typename P>
  void replace_if(T * first, T * last, P const& p, T const & new_val)
  {
    struct local
    {
      using p_t = pack<T>;

      local(P const& p, const T & nv) : p_(p), nv_(nv), pnv_(nv) {}

      T   operator()(T    const& x  ) const { return p_(x) ? nv_ : x;         }
      p_t operator()(p_t  const& x  ) const { return if_else(p_(x), pnv_, x); }

      P p_;
      T nv_;
      p_t pnv_;
    };

    boost::simd::transform(first, last, first, local(p, new_val));
  }

} }

#endif


/*=============================================================================
Copyright (c) 2016 Paul W. Bible

Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
==============================================================================*/
#ifndef TERM_PROBABILITY_MAP
#define TERM_PROBABILITY_MAP

#include <cmath>
#include <string>

#include <ggtk/GoGraph.hpp>
#include <ggtk/AnnotationData.hpp>
#include <ggtk/Accumulators.hpp>

#include <boost/graph/depth_first_search.hpp>
#include <boost/graph/reverse_graph.hpp>

/*! \class TermProbabilityMap
	\brief A class to calculate the probability of a GO term.

	This class provides a map that returns the probability of GO term. This
	  class is used by Information Content methods to determine the prior probability of
	  a term give an instance of AnnotationData.
*/
class TermProbabilityMap{
public:

	//! A parameterized constructor
	/*!
		This constructor takes pointers to GoGraph and AnnotationData objects.
		  Only the parameterized construtor is allowed to ensure these objects are
		  created with valid parameters.
	*/
	inline TermProbabilityMap(GoGraph* graph, AnnotationData* annoData){

		//set the default minimum probablity policy for normalization
		_isSingleAnnoMin = false;

		//Initialize an annotation list the size of verticies in go, each value is 0
		_probabilities = std::vector<double>(graph->getNumVertices(),0);

		//get the (first) root of the ontology.
		GoGraph::GoVertex root = graph->getRoot();

		//TESTING
		//std::cout << root << std::endl;
		//std::cout << graph->getTermStringIdByIndex(root) << std::endl;

		//a variable for the cummulative annotaions of the graph
		std::vector<std::size_t> annotationCounts(graph->getNumVertices(),0);

		//create the visitor abject
		dfs_cumulative_annotations_visitor vis(graph ,annoData, &annotationCounts,&_nameToIndex);

		//get the boost graph from the GoGraph object. Must be done to utilize boost algorithms
		GoGraph::Graph* g = graph->getGraph();

		//call the boost depth first search using our custom visitor
		// revering the graph is necessary otherwise the root vertex would have no edges.
		boost::depth_first_search(boost::make_reverse_graph(*g), boost::visitor(vis).root_vertex(root));

		// Traverse the vertices to find the roots for each ontology
		Accumulators::SimpleAccumulator minMaxBP;
		Accumulators::SimpleAccumulator minMaxMF;
		Accumulators::SimpleAccumulator minMaxCC;

		// Vertex Iterators
		GoGraph::GoVertexIterator vi,vend;
		for(boost::tie(vi,vend) = boost::vertices(*g); vi != vend; ++vi){
			GoGraph::GoVertex v = *vi;
			std::size_t index = graph->getVertexIndex(v);

			double counts = (double)annotationCounts.at(index);
			if(counts == 0.0){
				continue;
			}

			//switch on ontology, find the max for each set
			switch(graph->getTermOntologyByIndex(index)){
				case GO::BP:
					minMaxBP(counts);
					break;

				case GO::MF:
					minMaxMF(counts);
					break;

				case GO::CC:
					minMaxCC(counts);
					break;

				case GO::ONTO_ERROR:
					break;
			}
		}//end for, vertex iterator

		//calculate single annotation minimum normalization factors
		_bp_normalization_min_1anno = 1.0/Accumulators::extractMax(minMaxBP);
		_mf_normalization_min_1anno = 1.0/Accumulators::extractMax(minMaxMF);
		_cc_normalization_min_1anno = 1.0/Accumulators::extractMax(minMaxCC);

		//calculate minimum annotation minimum normalization factors
		_bp_normalization_min_minAnno = Accumulators::extractMin(minMaxBP)/Accumulators::extractMax(minMaxBP);
		_mf_normalization_min_minAnno = Accumulators::extractMin(minMaxMF)/Accumulators::extractMax(minMaxMF);
		_cc_normalization_min_minAnno = Accumulators::extractMin(minMaxCC)/Accumulators::extractMax(minMaxCC);

		//Traverse the cummulative annotation vector, convert to probability, add to probability vector
		for(boost::tie(vi,vend) = boost::vertices(*g); vi != vend; ++vi){
			GoGraph::GoVertex v = *vi;
			std::size_t index = graph->getVertexIndex(v);
			switch (graph->getTermOntologyByIndex(index)){
				case GO::BP:
					_probabilities.at(index) = annotationCounts.at(index)/Accumulators::extractMax(minMaxBP);
					break;
				case GO::MF:
					_probabilities.at(index) = annotationCounts.at(index)/Accumulators::extractMax(minMaxMF);
					break;
				case GO::CC:
					_probabilities.at(index) = annotationCounts.at(index)/Accumulators::extractMax(minMaxCC);
					break;
				case GO::ONTO_ERROR:
					break;
			}
		}
	}//end constructor logic

	//! A default constructor
	/*!
		This constructor initialized the storage structures. Should not be used.
	*/
	inline TermProbabilityMap(){
		//set the default minimum probablity policy for normalization
		_isSingleAnnoMin = true;
		//create empty containers
		_nameToIndex = boost::unordered_map<std::string,std::size_t>();
		_probabilities = std::vector<double>();
	}

	//! A default desctructor
	/*!
		This desctructor clears the containters
	*/
	inline ~TermProbabilityMap(){
		//empty containers
		_nameToIndex.clear();
		_probabilities.clear();
	}

	//! Accessor for probablities vector
	/*!
		Get the vector of values
	*/
	inline std::vector<double> getValues(){
		return _probabilities;
	}

	//! Function to return all the keys in the map
	/*!
		Returns all valid keys in the map.
	*/
	inline std::vector<std::string> getKeys(){
		std::vector<std::string> keys;
		boost::unordered_map<std::string, std::size_t>::iterator it;
		for (it = _nameToIndex.begin(); it != _nameToIndex.end(); ++it){
			keys.push_back(it->first);
		}
		return keys;
	}

	//! Method to test if the id exists in the map
	/*!
		Return true the id is found, false if not
	*/
	inline bool hasTerm(const std::string &testTerm){
		return _nameToIndex.find(testTerm) != _nameToIndex.end();
	}

	//! Return a default value for a term that does not exist.
	/*!
		A value to return if the term is not found (does not exist in the map).
		Returns probability 1 or certanty. This may not be the ideal behavior.
	*/
	inline virtual double badIdValue(){
		return 1.0;
	}

	//! Overloaded [] bracket operator to mimic Map
	/*!
	This defines a bracket operator to access the data inside of the map.
	This is done to mimic the behavior of the map class
	*/
	inline double operator[](std::string termId){
		if (!hasTerm(termId)){
			return badIdValue();
		}
		std::size_t index = _nameToIndex[termId];
		return _probabilities.at(index);
	}

	//! Mapping function to return the value mapped by key
	/*!
	Get the value mapped by the given key. A specified function for the [] operator
	*/
	inline double getValue(std::string termId){
		if (!hasTerm(termId)){
			return badIdValue();
		}
		std::size_t index = _nameToIndex[termId];
		return _probabilities.at(index);
	}

	//-------------------------------------------------------------------

	//! Get the specific minimum probability for BP
	/*!
		This function returns the minimum probablity for the bp ontology
	*/
	inline double getMinBP(){
		return _isSingleAnnoMin ? _bp_normalization_min_1anno : _bp_normalization_min_minAnno;
	}

	//! Get the specific minimum probability for MF
	/*!
		This function returns the minimum probablity for the mf ontology
	*/
	inline double getMinMF(){
		return _isSingleAnnoMin ? _mf_normalization_min_1anno : _mf_normalization_min_minAnno;
	}

	//! Get the specific minimum probability for CC
	/*!
		This function returns the minimum probablity for the cc ontology
	*/
	inline double getMinCC(){
		return _isSingleAnnoMin ? _cc_normalization_min_1anno : _cc_normalization_min_minAnno;
	}

	//! Depth first search boost visitor
	/*!
		This defines a class used by TermProabilityMap to calculate the cumulative annotations
		 of a term based on the true path rule. Basically this class is passed to a boost
		 Depth first search algorithm to add the number of a child's annotaitons to the parent.
	*/
	class dfs_cumulative_annotations_visitor: public boost::default_dfs_visitor{

	  public:
		//! A parameterized constructor passing parameters to the boost default_dfs_visitor
		dfs_cumulative_annotations_visitor(GoGraph* inGraph,AnnotationData* inData,
				std::vector<std::size_t>* annotations,boost::unordered_map<std::string,std::size_t>* nameToIndex)
			:goGraph(inGraph),annoData(inData),annoList(annotations),nameIndexMap(nameToIndex){}

		//! The extended method of the default_dfs_visitor, finish_vertex
		/*!
			This method is called during a depth first search traversal of a graph and called
			  when the visitor finished or leaves a node. This is the last time the algorithm touches
			  the node.

			  Here we calculate the number of cummulative annotations for a term (node) by adding the current
			    term's annotations to the annotations of the term's children.
		*/
		template < typename Vertex, typename Graph >
		void finish_vertex(Vertex u, const Graph & g)
		{

			//get the vertx index
			std::size_t uIndex = get(boost::vertex_index,g)[u];

			//get the termId string
			std::string termId = goGraph->getTermStringIdByIndex(uIndex);

			//use the AnnotationData object to get the actual number of annotations
			std::size_t currentTermAnnos = annoData->getNumAnnotationsForGoTerm(termId);

			//std::cout << termId << std::endl;

			//initialized this map for the TermProbablilityMap, can initialize it for free here
			nameIndexMap->insert(std::make_pair(termId,uIndex));

			//Iterate over the children of the term to add the child annotations
			typename boost::graph_traits< Graph >::out_edge_iterator ei, e_end;
			for(tie(ei, e_end) = boost::out_edges(u, g); ei != e_end; ++ei){

				Vertex v = boost::target(*ei, g);
				std::size_t vIndex = get(boost::vertex_index,g)[v];

				std::string childTermId = goGraph->getTermStringIdByIndex(vIndex);

				//std::cout << "child " << childTermId << std::endl;

				currentTermAnnos += annoList->at(vIndex);

			}//end for, each child

			annoList->at(uIndex) = currentTermAnnos;

			//std::cout << uIndex << std::endl;
			//std::cout << termId << std::endl;
			//std::cout << annoList->at(uIndex) << std::endl;
			//std::cin.get();

		}//end method, finish_vertex

		//! The go graph object
		GoGraph* goGraph;

		//! An AnnotationData object for accessing annotations
		AnnotationData* annoData;

		//! The annotaiton list to hold the and query the cummulative annotations
		std::vector<std::size_t>* annoList;

		//! A map from name To index, initialized in this visitor.
		boost::unordered_map<std::string,std::size_t>* nameIndexMap;
	};//end class, DFS visitor class

protected:
	//! A private map that returns the index of a term.
	/*!
		This map takes string term ids and returns the index for annotation count access.
	*/
	boost::unordered_map<std::string,std::size_t> _nameToIndex;

	//! A private list of term probabilities
	/*!
		This vector of doubles holds the prior probability for each term
	*/
	std::vector<double> _probabilities;

	//! A flag designating the minimum policy
	/*!
		This flag will be true and return true is single annotation probability is used, false otherwise.
	*/
	bool _isSingleAnnoMin;

	//! Normalization factor for calculating normalized simialrites Biological Process
	/*!
		Normalization factor representing the minimum probability using a single annotation
		  devided by the cumulative annotations.
	*/
	double _bp_normalization_min_1anno;

	//! Normalization factor for calculating normalized simialrites for Biological Process
	/*!
		Normalization factor representing the minimum probability using the number of
		  annotations of the least probable term devided by the cumulative annotations.
	*/
	double _bp_normalization_min_minAnno;

	//! Normalization factor for calculating normalized simialrites Molecular Function
	/*!
		Normalization factor representing the minimum probability using a single annotation
		  devided by the cumulative annotations.
	*/
	double _mf_normalization_min_1anno;

	//! Normalization factor for calculating normalized simialrites for Molecular Function
	/*!
		Normalization factor representing the minimum probability using the number of
		  annotations of the least probable term devided by the cumulative annotations.
	*/
	double _mf_normalization_min_minAnno;

	//! Normalization factor for calculating normalized simialrites Cellular Component
	/*!
		Normalization factor representing the minimum probability using a single annotation
		  devided by the cumulative annotations.
	*/
	double _cc_normalization_min_1anno;

	//! Normalization factor for calculating normalized simialrites for Cellular Component
	/*!
		Normalization factor representing the minimum probability using the number of
		  annotations of the least probable term devided by the cumulative annotations.
	*/
	double _cc_normalization_min_minAnno;

};
#endif


#ifndef POLYLINE_EXTRACTOR_HPP_9F3UIS3QOP
#define POLYLINE_EXTRACTOR_HPP_9F3UIS3QOP

#include "algorithm_dbosch.hpp"
#include "algorithm_sauvola.hpp"
#include "algorithm_otsu.hpp"
#include "algorithm_rlsa.hpp"
#include "algorithm_dp_path_finder.hpp"
#include "algorithm_calculate_search_area.hpp"
#include "line_region_list.hpp"
#include "image.hpp"
#include <boost/thread.hpp>
#include <log4cxx/logger.h>
#include <log4cxx/basicconfigurator.h>
#include <log4cxx/helpers/exception.h>
#include <iostream>
#include <fstream>
#include <opencv2/core/core.hpp>

//namespace std {using namespace __gnu_cxx;}
using namespace std;

namespace prhlt
{
    class Polyline_Extractor
    {
        public:
            Polyline_Extractor(string input_image_file_name, string extract_image_file_name);
            Polyline_Extractor(cv::Mat input_mat, cv::Mat extract_mat);
            ~Polyline_Extractor();
            void run(string line_region_file_name);
            void run(vector<int> region_limits, vector<vector<vector<cv::Point> > > limits);
            void run(vector<vector<cv::Point> > ex_regions, vector<vector<vector<cv::Point> > > ex_baselines, int ex_num_workers = 1, float ex_approx_dist = -1, bool ex_enclosing_rect = false, int up_dist = 100, int low_dist = 50, int horizontal_padding=0);
            void run(vector<int> region_limits, cv::Mat ex_limits_mat);
            void set_frontier_calculation_parameters(float ex_standard_deviation_contant, float ex_dynamic_range_constant, int ex_window_size);
            void set_distance_map_parameters(int ex_curv_ratio, float ex_delta, float ex_beta);
            void save_line_images_to_file(string base_file_name);
            void save_line_images_with_alpha_to_file(string base_file_name);
            void save_labeled_image_to_file(string file_name);
            void save_global_polylines_image(string file_name);
            void save_global_polylines_image(cv::Mat tmp, string file_name);
            void save_polylines_tool_format(string file_name);
            void load_polylines(vector<vector<cv::Point2f> > ex_polylines);
            vector<vector<vector<cv::Point> > > get_line_contours();
            void show_polylines();

        private:
            int num_workers;
            vector<thread *> threads;
            bool enclosing_rect;
            int up_dist;
            int low_dist;
            int horizontal_padding;
            float approx_dist_error;
            float standard_deviation_constant;
            float dynamic_range_constant;
            int window_size;
            int curvature_ratio;
            float delta;
            float beta;
            log4cxx::LoggerPtr logger;
            string input_image_file_name;
            string extract_image_file_name;
            string input_region_file_name;
            bool images_calculated;
            Image global_input_image;
            Image global_extract_image;
            Image global_output_image;
            unsigned int *label_image;
            cv::Mat input_mat;
            cv::Mat extract_mat;
            cv::Mat sauvola_mat;
            cv::Mat limits_mat;
            Eigen::MatrixXd distance_mat;
            vector<vector<vector<cv::Point > > > search_restrictions;
            vector<vector<cv::Point2d > > polylines;
            vector<vector<cv::Point2d > > collision_points;
            vector<vector<vector<cv::Point2d > > > area_polylines;
            vector<vector<vector<cv::Point2d > > > area_collision_points;
            vector<vector<vector<cv::Point> > > line_contours;

            vector<vector<cv::Rect> > search_areas;
            vector<vector<int> > search_regions;
            vector<vector<int> > line_limits;
            vector<cv::Mat> line_images;
            vector<cv::Mat> mask_images;
            vector<cv::Point> vector_2d_to_simp(const vector<cv::Point2d> &aux_vector);
            vector<vector<cv::Point2d> > review_loaded_polylines(vector<vector<cv::Point2f> > ex_polylines);
            vector<cv::Point2d> expand_points(cv::Point2f from, cv::Point2f to);
            void show_rectangle(cv::Rect ex_rect);
            void show_lines(vector<vector<cv::Point> > ex_lines);
            void load_input_image(string ex_input_image_file_name);
            void load_extract_image(string ex_extract_image_file_name);
            void set_default_constant_values();
            void calculate_frontiers();
            void calculate_search_areas(vector<vector<cv::Point> > ex_regions, vector<vector<vector<cv::Point> > > ex_baselines);
            void calculate_search_areas2(vector<vector<cv::Point> > ex_regions, vector<vector<vector<cv::Point> > > ex_baselines);
            vector<cv::Point> calculate_contour_from_baseline(const vector<cv::Point > &baseline);
            void calculate_line_contours();
            vector<cv::Point2d> get_clipped_contour(int reg_id, int line_id, string direction);
            void calculate_range_line_frontier_with_search_area(int i, int from, int to);
            void calculate_line_contour(int i, int j);
            void calculate_distance_matrix();
            void calculate_line_frontiers();
            vector<vector<cv::Point> > expand_search_restriction(int reg_index, int res_index, int x_offset = 0, int y_offset = 0);
            void calculate_line_frontiers_with_search_areas();
            void parallel_calculate_line_frontiers_with_search_areas();
            void calculate_single_line_frontier_with_search_area(int i, int j);
            void run();
            void generate_line_images();
            void extract_line(int start_frontier_index, int end_frontier_index);
            void extract_line_full_page(int start_frontier_index, int end_frontier_index);
            void copy_region_to_last_image(int column, int start_row, int end_row, int high_point);
            void label_region(int column, int start_row, int end_row, int line_number);
            void mask_region(int column, int start_row, int end_row, int high_point, int mask_value);
            vector<vector<int> > sequence_frontier(const vector<cv::Point2d> &frontier, int frontier_index, string mode);
            void cicular_sequenced_frontier_correction(vector<vector<int> > &sequence, int frontier_index, string mode, int radius);
            int get_highest_point_in_path(const vector<vector<int> > &sequenced_path);
            int get_highest_point_in_path(const vector<cv::Point2d> &path);
            int get_lowest_point_in_path(const vector<vector<int> > &sequenced_path);
            int get_lowest_point_in_path(const vector<cv::Point2d> &path);
            bool is_too_far_above(cv::Rect current_rect, cv::Rect compared_to);
            bool is_too_far_below(cv::Rect current_rect, cv::Rect compared_to);
            bool it_overlaps(cv::Rect current_rect, cv::Rect compared_to);
            vector<vector<cv::Point> > expand_my_search_restriction(int reg_index, int res_index, int x_offset, int y_offset);
            vector<vector<cv::Point> > expand_other_search_restriction(int reg_index, int res_index, int x_offset, int y_offset);
            vector<vector<cv::Point> > expand_above_search_restrictions(int reg_index, int res_index, int x_offset, int y_offset);
            vector<vector<cv::Point> > expand_below_search_restrictions(int reg_index, int res_index, int x_offset, int y_offset);
            int relative_possition_of_point_to_baseline(cv::Point ex_point, const vector<cv::Point> & ex_baseline);
    };
}; // namespace prhlt

#endif /* end of include guard: ALGORITHM_RLSA_HPP_9F3UIS3QOP*/


/*
 Copyright (C) 2018 Quaternion Risk Management Ltd
 All rights reserved.

 This file is part of ORE, a free-software/open-source library
 for transparent pricing and risk analysis - http://opensourcerisk.org

 ORE is free software: you can redistribute it and/or modify it
 under the terms of the Modified BSD License.  You should have received a
 copy of the license along with this program.
 The license is also available online at <http://opensourcerisk.org>

 This program is distributed on the basis that it will form a useful
 contribution to risk analytics and model standardisation, but WITHOUT
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the license for more details.
*/

#include <ored/portfolio/builders/yoycapfloor.hpp>
#include <ored/utilities/log.hpp>

#include <qle/pricingengines/inflationcapfloorengines.hpp>

#include <boost/make_shared.hpp>

namespace ore {
namespace data {

boost::shared_ptr<PricingEngine> YoYCapFloorEngineBuilder::engineImpl(const string& indexName) {
    Handle<YoYInflationIndex> yoyTs = market_->yoyInflationIndex(indexName, configuration(MarketContext::pricing));
    Handle<YieldTermStructure> discount =
        market_->discountCurve(yoyTs->currency().code(), configuration(MarketContext::pricing));
    Handle<QuantExt::YoYOptionletVolatilitySurface> ovs =
        market_->yoyCapFloorVol(indexName, configuration(MarketContext::pricing));
    if (ovs.empty())
        return boost::make_shared<QuantExt::YoYInflationBlackCapFloorEngine>(
            *yoyTs, Handle<QuantLib::YoYOptionletVolatilitySurface>(), discount);
    switch (ovs->volatilityType()) {
    case ShiftedLognormal:
        if (ovs->displacement() == 0.0) {
            LOG("Build YoYInflationBlackCapFloorEngine for inflation index " << indexName);
            return boost::make_shared<QuantExt::YoYInflationBlackCapFloorEngine>(
                *yoyTs, Handle<QuantLib::YoYOptionletVolatilitySurface>(ovs), discount);
            break;
        } else {
            LOG("Build YoYInflationUnitDisplacedBlackCapFloorEngine for inflation index " << indexName);
            return boost::make_shared<QuantExt::YoYInflationUnitDisplacedBlackCapFloorEngine>(
                *yoyTs, Handle<QuantLib::YoYOptionletVolatilitySurface>(ovs), discount);
            break;
        }
    case Normal:
        LOG("Build YoYInflationBachelierCapFloorEngine for inflation index " << indexName);
        return boost::make_shared<QuantExt::YoYInflationBachelierCapFloorEngine>(
            *yoyTs, Handle<QuantLib::YoYOptionletVolatilitySurface>(ovs), discount);
        break;
    default:
        QL_FAIL("Caplet volatility type, " << ovs->volatilityType() << ", not covered in EngineFactory");
        break;
    }
}
} // namespace data
} // namespace ore


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

#include <boost/random/poisson_distribution.hpp>

namespace clotho {
namespace genetics {

template < class T >
struct crossover_event_distribution_helper;

template < >
struct crossover_event_distribution_helper< double > {
    typedef unsigned int    IntType;

    typedef boost::random::poisson_distribution< IntType, double > type;
};

template < >
struct crossover_event_distribution_helper< float > {
    typedef unsigned int    IntType;
    typedef boost::random::poisson_distribution< IntType, float > type;
};

}   // namespace genetics
}   // namespace cloth

#endif  // CLOTHO_CROSSOVER_EVENT_DISTRIBUTION_HELPER_HPP_


#include "ar_renderer.hpp"
#include "util.hpp"
#include <map>
#include <vector>
#include <Eigen/Dense>

namespace {
namespace cubeModel {
    // issues emerge if using class instead of namespace
    // https://stackoverflow.com/questions/40690260/undefined-reference-error-for-static-constexpr-member

    constexpr float vertices[][3] = {
            { -0.5, -0.5, 0.5 },
            { 0.5, -0.5, 0.5 },
            { -0.5, 0.5, 0.5 },
            { 0.5, 0.5, 0.5 },
            { -0.5, 0.5, -0.5 },
            { 0.5, 0.5, -0.5 },
            { -0.5, -0.5, -0.5 },
            { 0.5, -0.5, -0.5 }
    };

    constexpr float texCoords[][2] = {
            { 0.0, 0.0 },
            { 1.0, 0.0 },
            { 0.0, 1.0 },
            { 1.0, 1.0 }
    };

    constexpr float normals[][3] = {
            { 0.0, 0.0, 1.0 },
            { 0.0, 1.0, 0.0 },
            { 0.0, 0.0, -1.0 },
            { 0.0, -1.0, 0.0 },
            { 1.0, 0.0, 0.0 },
            { -1.0, 0.0, 0.0 }
    };

    constexpr int faceVertices[][3] = {
            { 0, 1, 2 },
            { 2, 1, 3 },
            { 2, 3, 4 },
            { 4, 3, 5 },
            { 4, 5, 6 },
            { 6, 5, 7 },
            { 6, 7, 0 },
            { 0, 7, 1 },
            { 1, 7, 3 },
            { 3, 7, 5 },
            { 6, 0, 4 },
            { 4, 0, 2 }
    };

    constexpr int faceTexCoords[][3] = {
            { 0, 1, 2 },
            { 2, 1, 3 },
            { 0, 1, 2 },
            { 2, 1, 3 },
            { 3, 2, 1 },
            { 1, 2, 0 },
            { 0, 1, 2 },
            { 2, 1, 3 },
            { 0, 1, 2 },
            { 2, 1, 3 },
            { 0, 1, 2 },
            { 2, 1, 3 }
    };

    constexpr int faceNormals[][3] = {
            { 0, 0, 0 },
            { 0, 0, 0 },
            { 1, 1, 1 },
            { 1, 1, 1 },
            { 2, 2, 2 },
            { 2, 2, 2 },
            { 3, 3, 3 },
            { 3, 3, 3 },
            { 4, 4, 4 },
            { 4, 4, 4 },
            { 5, 5, 5 },
            { 5, 5, 5 }
    };

    void generateData(
            const Eigen::Matrix4f &transform,
            std::vector<GLfloat> &vertexData,
            std::vector<GLfloat> &texCoordData,
            std::vector<GLfloat> &normalData) {

        for (const auto &f : faceVertices) {
            for (int j : f) {
                const auto &p0 = vertices[j];
                const Eigen::Vector4f ph(p0[0], p0[1], p0[2], 1);
                const auto p1 = transform*ph;
                for (int k = 0; k < 3; ++k) vertexData.push_back(p1(k));
            }
        }
        for (const auto &f : faceTexCoords) {
            for (int j : f) {
                const auto &p = texCoords[j];
                for (float c : p) {
                    texCoordData.push_back(c);
                }
            }
        }
        for (const auto &f : faceNormals) {
            for (int j : f) {
                const auto &p0 = normals[j];
                const Eigen::Vector4f ph(p0[0], p0[1], p0[2], 0);
                const auto p1 = (transform*ph).normalized();
                for (int k = 0; k < 3; ++k) normalData.push_back(p1(k));
            }
        }
    }
};

struct CubeGridModel {
    EIGEN_MAKE_ALIGNED_OPERATOR_NEW

    float scale = 0.05;
    int gridW = 4;
    int gridH = 4;

    float gridScale = 0.25f;

    Eigen::Vector3f gridCenter = Eigen::Vector3f(0, 0.5f, -0.3f);
    Eigen::Vector3f lightDirection = Eigen::Vector3f(0.1, 0.3, 1.0);
    Eigen::Matrix4f yIsUpTransform;
    bool yIsUp = false;

    CubeGridModel() {
        yIsUpTransform <<
                1,  0,  0,  0,
                0,  0,  1,  0,
                0, -1,  0,  0,
                0,  0,  0,  1;
    }

    std::vector<GLfloat> getLightDirection() const {
        const auto l = lightDirection.normalized();
        Eigen::Vector4f dirh(l.x(), l.y(), l.z(), 0);
        if (yIsUp) {
            dirh = yIsUpTransform*dirh;
        }

        return { dirh.x(), dirh.y(), dirh.z() };
    }

    void generateData(std::vector<GLfloat> &vertexData, std::vector<GLfloat> &texCoordData, std::vector<GLfloat> &normalData) const {
        for (int ix=0; ix<gridW; ++ix) {
            for (int iy=0; iy<gridH; ++iy) {
                // Eigen quirks: do not define this as "const auto center"
                const Eigen::Vector3f center = gridCenter + gridScale * Eigen::Vector3f(
                        gridW <= 1 ? 0 : (ix/float(gridW - 1)-0.5f),
                        gridH <= 1 ? 0 : (iy/float(gridH - 1)-0.5f),
                        0);

                Eigen::Matrix4f transform = Eigen::Matrix4f::Identity();
                transform.block<3, 3>(0, 0) *= scale;
                transform.block<3, 1>(0, 3) = center;

                if (yIsUp) transform = yIsUpTransform * transform;
                cubeModel::generateData(transform, vertexData, texCoordData, normalData);
            }
        }
    }
};

class CubeGridRenderer {
private:
    std::vector<GLfloat> vertexData;
    std::vector<GLfloat> texCoordData;
    std::vector<GLfloat> normalData;

    GLuint shaderProgram = 0;

    GLuint attributeVertex = 0;
    GLuint attributeTexCoord = 0;
    GLuint attributeNormal = 0;

    GLuint uniformModelViewProjection = 0;
    GLuint uniformGridCenter = 0;
    GLuint uniformGridScale = 0;
    GLuint uniformLightDirection = 0;

    CubeGridModel model;

public:
    CubeGridRenderer(bool yIsUp)  {
        log_debug("CubeGridRenderer::CubeGridRenderer");

        const std::string vertexShaderSource = R"(
            uniform mat4 u_ModelViewProjection;
            attribute vec3 a_Position;
            attribute vec3 a_Normal;
            attribute vec2 a_TexCoord;
            varying vec3 v_Normal;
            varying vec2 v_TexCoord;
            varying vec3 v_Position;
            void main() {
                v_Normal = a_Normal;
                v_TexCoord = a_TexCoord;
                v_Position = a_Position;
                gl_Position = u_ModelViewProjection * vec4(a_Position.xyz, 1.0);
            })";

        const std::string fragmentShaderSource = R"(
            precision mediump float;
            varying vec3 v_Normal;
            varying vec2 v_TexCoord;
            varying vec3 v_Position;
            uniform vec3 u_GridCenter;
            uniform vec3 u_LightDirection;
            uniform float u_GridScale;
            void main() {
                const float ambient = 0.4;

                float lighting = max(max(dot(v_Normal, u_LightDirection), 0.0), ambient);

                vec3 relPos = (v_Position.xyz - u_GridCenter.xyz) / u_GridScale + 0.5;
                // NOTE: not the same colors with y=up vs z=up
                vec3 objColor = vec3(relPos.x, relPos.y, 1.0);

                vec2 border = abs(v_TexCoord - 0.5)*2.0;
                if (max(border.x, border.y) > 0.9) {
                    objColor = vec3(1.0);
                }

                gl_FragColor = vec4(objColor * lighting, 1.0);
            }
            )";

        shaderProgram = glUtil::createProgram(vertexShaderSource.c_str(), fragmentShaderSource.c_str());
        assert(shaderProgram && "error compiling shaders");

        // GLSL variables
        attributeVertex = static_cast<GLuint>(glGetAttribLocation(shaderProgram, "a_Position"));
        attributeTexCoord = static_cast<GLuint>(glGetAttribLocation(shaderProgram, "a_TexCoord"));
        attributeNormal = static_cast<GLuint>(glGetAttribLocation(shaderProgram, "a_Normal"));

        uniformModelViewProjection = static_cast<GLuint>(glGetUniformLocation(shaderProgram, "u_ModelViewProjection"));
        uniformGridCenter = static_cast<GLuint>(glGetUniformLocation(shaderProgram, "u_GridCenter"));
        uniformGridScale = static_cast<GLuint>(glGetUniformLocation(shaderProgram, "u_GridScale"));
        uniformLightDirection = static_cast<GLuint>(glGetUniformLocation(shaderProgram, "u_LightDirection"));

        model.yIsUp = yIsUp;
        model.generateData(vertexData, texCoordData, normalData);

        glUtil::checkError("CubeGridRenderer GLSL setup");
    }

    void render(const Eigen::Matrix4f &modelViewProjection) const {
        if (vertexData.empty()) return;

        glUseProgram(shaderProgram);

        glUniformMatrix4fv(uniformModelViewProjection, 1, GL_FALSE, modelViewProjection.data());
        glUniform3fv(uniformGridCenter, 1, model.gridCenter.data());
        glUniform3fv(uniformLightDirection, 1, model.getLightDirection().data());
        glUniform1f(uniformGridScale, model.gridScale);

        glEnableVertexAttribArray(attributeVertex);
        constexpr unsigned coordsPerVertex = 3;
        glVertexAttribPointer(attributeVertex, coordsPerVertex, GL_FLOAT, GL_FALSE, 0,
                              vertexData.data());

        glEnableVertexAttribArray(attributeTexCoord);
        constexpr unsigned coordsPerTex = 2;
        glVertexAttribPointer(attributeTexCoord, coordsPerTex, GL_FLOAT, GL_FALSE, 0,
                              texCoordData.data());

        glEnableVertexAttribArray(attributeNormal);
        glVertexAttribPointer(attributeNormal, coordsPerVertex, GL_FLOAT, GL_FALSE, 0,
                              normalData.data());

        const auto nVertices = static_cast<GLsizei>(vertexData.size() / coordsPerVertex);
        glDrawArrays(GL_TRIANGLES, 0, nVertices);

        glUseProgram(0);
        glDisableVertexAttribArray(attributeVertex);
        glDisableVertexAttribArray(attributeTexCoord);
        glDisableVertexAttribArray(attributeNormal);

        glUtil::checkError("CubeGridRenderer::render");
    }
};

class ArTrackRenderer {
private:
    std::vector<GLfloat> vertexData;

    GLuint shaderProgram = 0;
    GLuint attributeVertex = 0;

    GLuint uniformModelViewProjection = 0;
    GLuint uniformCurrentPosition = 0;

    double lastNodeTimeStamp = 0.0;
    float currentPosition[3] = { 0, 0, 0 };

public:
    ArTrackRenderer()  {
        log_debug("ArTrackRenderer::ArTrackRenderer");

        const std::string vertexShaderSource = R"(
        uniform mat4 u_ModelViewProjection;
        attribute vec3 a_Position;
        varying vec3 v_Position;
        void main() {
            v_Position = a_Position;
            gl_Position = u_ModelViewProjection * vec4(a_Position.xyz, 1.0);
        })";

        const std::string fragmentShaderSource = R"(
        precision mediump float;
        varying vec3 v_Position;
        uniform vec3 u_CurrentPosition;
        void main() {
            const vec3 COLOR = vec3(1.0, 0.0, 1.0);
            const float DIST_TO_FULL_OPACITY = 1.0;
            float dist = length(v_Position - u_CurrentPosition);
            float alpha = min(1.0,  dist / DIST_TO_FULL_OPACITY);
            gl_FragColor = vec4(COLOR, alpha);
        }
        )";

        shaderProgram = glUtil::createProgram(vertexShaderSource.c_str(), fragmentShaderSource.c_str());
        assert(shaderProgram && "error compiling shaders");

        // GLSL variables
        attributeVertex = static_cast<GLuint>(glGetAttribLocation(shaderProgram, "a_Position"));
        uniformModelViewProjection = static_cast<GLuint>(glGetUniformLocation(shaderProgram, "u_ModelViewProjection"));
        uniformCurrentPosition = static_cast<GLuint>(glGetUniformLocation(shaderProgram, "u_CurrentPosition"));

        glUtil::checkError("ArTrackRenderer GLSL setup");
    }

    void setPosition(double t, const float xyz[]) {
        constexpr double NODE_INTERVAL_SEC = 0.02;

        for (int i=0; i<3; ++i) currentPosition[i] = xyz[i];
        if (t > lastNodeTimeStamp + NODE_INTERVAL_SEC) {
            for (int i=0; i<3; ++i) vertexData.push_back(xyz[i]);
            lastNodeTimeStamp = t;
        }

        const auto n = vertexData.size() / 3;
        constexpr int MAX_VERTICES = 100000;
        if (n > MAX_VERTICES) {
            const auto N_TO_ERASE = MAX_VERTICES / 10;
            vertexData.erase(vertexData.begin(), vertexData.begin() + N_TO_ERASE*3);
        }
    }

    void render(const Eigen::Matrix4f &modelViewProjection) const {
        if (vertexData.empty()) return;

        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

        glLineWidth(5);

        glUseProgram(shaderProgram);
        glUniformMatrix4fv(uniformModelViewProjection, 1, GL_FALSE, modelViewProjection.data());
        glUniform3fv(uniformCurrentPosition, 1, currentPosition);

        glEnableVertexAttribArray(attributeVertex);
        constexpr unsigned coordsPerVertex = 3;
        glVertexAttribPointer(attributeVertex, coordsPerVertex, GL_FLOAT, GL_FALSE, 0,
                              vertexData.data());

        const auto nVertices = static_cast<GLsizei>(vertexData.size() / coordsPerVertex);
        glDrawArrays(GL_LINE_STRIP, 0, nVertices);

        glUseProgram(0);
        glDisableVertexAttribArray(attributeVertex);
        glDisable(GL_BLEND);

        glUtil::checkError("ArTrackRenderer::render");
    }
};

class PointCloudRenderer {
private:
    std::vector<GLfloat> vertexData;

    GLuint shaderProgram = 0;
    GLuint attributeVertex = 0;
    GLuint uniformModelViewProjection = 0;

public:
    PointCloudRenderer()  {
        log_debug("PointCloudRenderer::PointCloudRenderer");

        const std::string vertexShaderSource = R"(
        uniform mat4 u_ModelViewProjection;
        attribute vec3 a_Position;
        void main() {
            gl_PointSize = 5.0;
            gl_Position = u_ModelViewProjection * vec4(a_Position.xyz, 1.0);
        })";

        const std::string fragmentShaderSource = R"(
        precision mediump float;
        void main() {
            const vec3 COLOR = vec3(1.0, 1.0, 0.0);
            gl_FragColor = vec4(COLOR, 1.0);
        }
        )";

        shaderProgram = glUtil::createProgram(vertexShaderSource.c_str(), fragmentShaderSource.c_str());
        assert(shaderProgram && "error compiling shaders");

        // GLSL variables
        attributeVertex = static_cast<GLuint>(glGetAttribLocation(shaderProgram, "a_Position"));
        uniformModelViewProjection = static_cast<GLuint>(glGetUniformLocation(shaderProgram, "u_ModelViewProjection"));

        glUtil::checkError("PointCloudRenderer GLSL setup");
    }

    void setPointCloud(const float *flatData, std::size_t n) {
        assert(n % 3 == 0);
        vertexData = std::vector<float>(flatData, flatData + n);
    }

    void render(const Eigen::Matrix4f &modelViewProjection) const {
        if (vertexData.empty()) return;

        glUseProgram(shaderProgram);
        glUniformMatrix4fv(uniformModelViewProjection, 1, GL_FALSE, modelViewProjection.data());

        glEnableVertexAttribArray(attributeVertex);
        constexpr unsigned coordsPerVertex = 3;
        glVertexAttribPointer(attributeVertex, coordsPerVertex, GL_FLOAT, GL_FALSE, 0,
                              vertexData.data());

        const auto nVertices = static_cast<GLsizei>(vertexData.size() / coordsPerVertex);
        glDrawArrays(GL_POINTS, 0, nVertices);

        glUseProgram(0);
        glDisableVertexAttribArray(attributeVertex);

        glUtil::checkError("PointCloudRenderer::render");
    }
};

class ArRendererImplementation : public ArRenderer {
private:
    Eigen::Matrix4f modelViewMatrix;
    Eigen::Matrix4f projectionMatrix;

    const CubeGridRenderer cubeGridRenderer;
    ArTrackRenderer arTrackRenderer;
    PointCloudRenderer pointCloudRenderer;

public:
    ArRendererImplementation(bool yIsUp) : cubeGridRenderer(yIsUp) {}

    void setPose(const double t, const float xyz[], const float quaternion[]) final {
        // just to make it extra clear which element is which
        const float w = quaternion[0];
        const float x = quaternion[1];
        const float y = quaternion[2];
        const float z = quaternion[3];
        const Eigen::Quaternionf q(w, x, y, z);

        const Eigen::Vector3f p(xyz[0], xyz[1], xyz[2]);
        const Eigen::Matrix3f R = q.toRotationMatrix();

        modelViewMatrix = Eigen::Matrix4f::Zero();
        modelViewMatrix.block<3, 3>(0, 0) = R;
        modelViewMatrix.block<3, 1>(0, 3) = -R * p;
        modelViewMatrix(3, 3) = 1;

        arTrackRenderer.setPosition(t, xyz);
    }

    void setPose(const double t, const float mat[]) final {
        modelViewMatrix = Eigen::Map<Eigen::Matrix4f>(const_cast<float*>(mat));

        const Eigen::Matrix3f R = modelViewMatrix.block<3, 3>(0, 0);
        const Eigen::Vector3f p = -R.transpose() * modelViewMatrix.block<3, 1>(0, 3);
        const float xyz[3] = { p(0), p(1), p(2) };
        arTrackRenderer.setPosition(t, xyz);
    }

    void setProjection(int width, int height, float focalLength) final {
        projectionMatrix = Eigen::Matrix4f::Zero();

        // OpenGL near and far clip plane distances (meters)
        constexpr float zNear = 0.01;
        constexpr float zFar = 20;

        projectionMatrix(2, 2) = -(zFar + zNear) / (zFar - zNear);
        projectionMatrix(2, 3) = -2 * zFar * zNear / (zFar - zNear);
        projectionMatrix(3, 2) = -1;

        // normal version
        //projectionMatrix(0, 0) = 2 * focalLength / width;
        //projectionMatrix(1, 1) = 2 * focalLength / height;

        // rotated image: flip X & Y
        projectionMatrix(0, 1) = -2 * focalLength / width;
        projectionMatrix(1, 0) = 2 * focalLength / height;
    }

    void setProjectionMatrix(const float matrix[]) final {
        projectionMatrix = Eigen::Map<Eigen::Matrix4f>(const_cast<float*>(matrix));
    }

    void setPointCloud(const float *flatData, std::size_t n) final {
        pointCloudRenderer.setPointCloud(flatData, n);
    }

    void render() const final {
        const Eigen::Matrix4f modelViewProjection = projectionMatrix * modelViewMatrix;

        glEnable(GL_DEPTH_TEST);
        glDepthMask(GL_TRUE);
        glClear(GL_DEPTH_BUFFER_BIT);

        cubeGridRenderer.render(modelViewProjection);
        arTrackRenderer.render(modelViewProjection);
        pointCloudRenderer.render(modelViewProjection);

        glDisable(GL_DEPTH_TEST);
        glDepthMask(GL_FALSE);
    }
};
}

std::unique_ptr<ArRenderer> ArRenderer::buildWithYIsUp() {
    return std::unique_ptr<ArRenderer>(new ArRendererImplementation(true));
}

std::unique_ptr<ArRenderer> ArRenderer::buildWithZIsUp() {
    return std::unique_ptr<ArRenderer>(new ArRendererImplementation(false));
}


#include <cctbx/boost_python/flex_fwd.h>

#include <cctbx/sgtbx/change_of_basis_op.h>
#include <boost/python/tuple.hpp>
#include <boost/python/class.hpp>
#include <boost/python/args.hpp>
#include <boost/python/return_value_policy.hpp>
#include <boost/python/copy_const_reference.hpp>

namespace cctbx { namespace sgtbx { namespace boost_python {

namespace {

  struct change_of_basis_op_wrappers : boost::python::pickle_suite
  {
    typedef change_of_basis_op w_t;

    static w_t
    new_denominators_int_int(w_t const& o, int r_den, int t_den)
    {
      return o.new_denominators(r_den, t_den);
    }

    static w_t
    new_denominators_w_t(w_t const& o, w_t const& other)
    {
      return o.new_denominators(other);
    }

    static void
    update_w_t(w_t& o, w_t const& other)
    {
      o.update(other);
    }

    static boost::python::tuple
    getinitargs(w_t const& o)
    {
      return boost::python::make_tuple(o.c().as_xyz());
    }

    static void
    wrap()
    {
      using namespace boost::python;
      typedef return_value_policy<copy_const_reference> ccr;
      class_<w_t>("change_of_basis_op", no_init)
        .def(init<rt_mx const&, rt_mx const&>((
          arg("c"), arg("c_inv"))))
        .def(init<rt_mx const&>((arg("c"))))
        .def(init<std::string const&, optional<const char*, int, int> >((
          arg("symbol"),
          arg("stop_chars"),
          arg("r_den"),
          arg("t_den"))))
        .def(init<optional<int, int> >((
          arg("r_den")=cb_r_den,
          arg("t_den")=cb_t_den)))
        .def("is_valid", &w_t::is_valid)
        .def("identity_op", &w_t::identity_op)
        .def("is_identity_op", &w_t::is_identity_op)
        .def("new_denominators", new_denominators_int_int, (
          arg("r_den"),
          arg("t_den")))
        .def("new_denominators", new_denominators_w_t, (arg("other")))
        .def("c", &w_t::c, ccr())
        .def("c_inv", &w_t::c_inv, ccr())
        .def("select", &w_t::select, (arg("inv")), ccr())
        .def("inverse", &w_t::inverse)
        .def("mod_positive_in_place", &w_t::mod_positive_in_place)
        .def("mod_short_in_place", &w_t::mod_short_in_place)
        .def("mod_short", &w_t::mod_short)
        .def("apply",
          (rt_mx(w_t::*)(rt_mx const&) const)
          &w_t::apply, (arg("s")))
        .def("apply",
          (uctbx::unit_cell(w_t::*)(uctbx::unit_cell const&) const)
          &w_t::apply, (arg("unit_cell")))
        .def("apply",
          (miller::index<>(w_t::*)(miller::index<> const&) const)
          &w_t::apply, (arg("miller_index")))
        .def("apply",
          (af::shared<miller::index<> >(w_t::*)
            (af::const_ref<miller::index<> > const&) const)
              &w_t::apply, (arg("miller_indices")))
        .def("apply_results_in_non_integral_indices",
          (af::shared<std::size_t>(w_t::*)
            (af::const_ref<miller::index<> > const&) const)
              &w_t::apply_results_in_non_integral_indices, (
                arg("miller_indices")))
        .def("__call__",
          (fractional<>(w_t::*)(fractional<> const&) const)
          &w_t::operator(), (arg("site_frac")))
        .def("update", update_w_t, (arg("other")))
        .def("__mul__", &w_t::operator*)
        .def("as_xyz", &w_t::as_xyz, (
          arg("decimal")=false,
          arg("t_first")=false,
          arg("symbol_letters")="xyz",
          arg("separator")=","))
        .def("as_hkl", &w_t::as_hkl, (
          arg("decimal")=false,
          arg("letters_hkl")="hkl",
          arg("separator")=","))
        .def("as_abc", &w_t::as_abc, (
          arg("decimal")=false,
          arg("t_first")=false,
          arg("letters_abc")="abc",
          arg("separator")=","))
        .def("symbol", &w_t::symbol)
        .def("__str__", &w_t::symbol)
        .def_pickle(change_of_basis_op_wrappers())
      ;
    }
  };

} // namespace <anoymous>

  void wrap_change_of_basis_op()
  {
    change_of_basis_op_wrappers::wrap();
  }

}}} // namespace cctbx::sgtbx::boost_python


// Copyright 2018, Intel Corporation

#include "tile/codegen/placer.h"

#include <list>
#include <set>
#include <stack>

#include <boost/dynamic_bitset.hpp>
#include <boost/graph/undirected_graph.hpp>

#include "tile/base/shape.h"
#include "tile/codegen/alias.h"
#include "tile/math/util.h"

// Some terminology: we refer to each Refinement that describes a
// newly-created block of memory (i.e. !IsReadDir(r) &&
// !IsWriteDir(r)) as a _Chunk_.
//
// Each chunk has a _Location_, assigned before placement begins; each
// location is exactly one memory arena.
//
// Each chunk also has an _Offset_ into its location's memory arena,
// assigned by this function -- _Placing_ the chunk.
//
// Placing assigns to each chunk a byte offset within the chunk's
// location, such that if two chunks overlap temporally, they do not
// overlap spatially.
//
// This code implements a simple arena placer.  The algorithm assumes:
//   * The order of statements is fixed
//   * All dependencies are accounted for in the statements.
//
// To place the chunks:
//
// * For each statement, we determine the transitive dependency set of
//   the statement -- the set of statements that must have completed
//   by the time the statement has completed.
//
// * For each chunk, we determine the set of statements that access
//   the chunk, and the intersection of the transitive dependency sets
//   of those statements (i.e. the set of statements known to have
//   completed when the chunk's accessors might run).  We also record
//   the index of the first statement to access the chunk.
//
// * We use these sets to construct a graph for each location, in
//   which the vertices are chunks at that location, and the edges
//   connect chunks that temporally overlap at the same location.
//   (Two chunks do not temporally overlap iff they are in different
//   locations, or if all accessors of the chunk with the lower
//   initial accessor index are in the transitive dependency set of
//   all accessors of the chunk with the higher initial accessor
//   index.)
//
// * For each chunk, we assign a placement, by looking at the
//   placements of existing temporally-overlapping chunks and finding
//   an offset in the location's arena where the chunk can safely
//   coexist with all already-placed chunks.
//
//   Note that optimal arena placement is NP-hard.  So instead of
//   finding an optimal placement, we use a simple approximation: we
//   place the chunks in largest-to-smallest order, using best-fit.
//
//   More concretely: for each chunk (in largest-to-smallest order),
//   we take the set of temporally-overlapping chunks, sort them by
//   existing offset, and then walk the list in order, looking for the
//   smallest free range that's big enough for the chunk.

namespace vertexai {
namespace tile {
namespace codegen {
namespace {

constexpr std::size_t kDefaultAlignment = 4;

struct Chunk;

struct InterferenceVertex {
  Chunk* chunk;
};

typedef boost::adjacency_list<boost::listS, boost::listS, boost::undirectedS, InterferenceVertex> InterferenceGraph;

struct Chunk {
  Chunk(stripe::Refinement* ref_, std::size_t alignment, std::size_t stmt_limit)
      : ref{ref_},
        size{math::Align(stripe::Codec::Resolve(ref_->interior_shape)->byte_size(), alignment)},
        accessors{stmt_limit},
        transitive_accessor_deps{stmt_limit},
        subsequent_accessor_deps{stmt_limit} {}

  stripe::Refinement* ref;
  std::size_t size;
  bool placed = false;
  bool saw_first_accessor = false;
  std::size_t first_accessor_idx = 0;
  boost::dynamic_bitset<> accessors;
  boost::dynamic_bitset<> transitive_accessor_deps;
  boost::dynamic_bitset<> subsequent_accessor_deps;
  InterferenceGraph::vertex_descriptor interference_vertex;
};

struct StmtInfo {
  StmtInfo(std::size_t idx_, std::size_t stmt_limit) : idx{idx_}, transitive_deps{stmt_limit} {}

  std::size_t idx;
  boost::dynamic_bitset<> transitive_deps;
};

class ChunkUseRecorder : public stripe::MutableStmtVisitor {
 public:
  ChunkUseRecorder(const StmtInfo* stmt_info, const AliasMap* alias_map,
                   const std::unordered_map<std::string, Chunk*>* chunks)
      : stmt_info_{stmt_info}, alias_map_{alias_map}, chunks_{chunks} {}

  void Visit(stripe::Load* load) final { RecordUse(load->from); }

  void Visit(stripe::Store* store) final { RecordUse(store->into); }

  void Visit(stripe::LoadIndex* load_index) final {}

  void Visit(stripe::Constant*) final {}  // unused

  void Visit(stripe::Special* special) final {
    for (const std::string& input : special->inputs) {
      RecordUse(input);
    }
    for (const std::string& output : special->outputs) {
      RecordUse(output);
    }
  }

  void Visit(stripe::Intrinsic* intrinsic) final {}  // unused

  void Visit(stripe::Block* block) final {
    block_ = block;

    // Different instances of a block may execute in any order.  We
    // support this by having the block statement reference logically
    // use each refined chunk, forcing those chunks to interfere with
    // each other if they occupy the same arena.
    //
    // TODO: Two chunks could safely alias iff we can guarantee that
    // all reads from a particular offset within the chunk (given all
    // parallel instances of the block executing in any order) happen
    // before all writes to that same offset, which often occurs for
    // elementwise operations.
    for (auto& ref : block->refs) {
      if (IsReadDir(ref.dir) || IsWriteDir(ref.dir)) {
        RecordUse(ref.from);
      }
    }
  }

  stripe::Block* block() const { return block_; }

 private:
  void RecordUse(const std::string& name) {
    const AliasInfo& info = alias_map_->at(name);
    auto it = chunks_->find(info.base_name);
    if (it == chunks_->end()) {
      // This must be a chunk type we're not placing.
      return;
    }
    Chunk* chunk = it->second;
    chunk->accessors.set(stmt_info_->idx);
    if (!chunk->saw_first_accessor) {
      chunk->saw_first_accessor = true;
      chunk->first_accessor_idx = stmt_info_->idx;
      chunk->transitive_accessor_deps = stmt_info_->transitive_deps;
    } else {
      chunk->transitive_accessor_deps &= stmt_info_->transitive_deps;
    }
  }

  const StmtInfo* stmt_info_;
  const AliasMap* alias_map_;
  const std::unordered_map<std::string, Chunk*>* chunks_;
  stripe::Block* block_ = nullptr;
};

std::list<Chunk> BuildChunkList(stripe::Block* outermost_block, const std::set<stripe::Location>& locations,
                                std::size_t alignment, std::size_t stmt_limit) {
  // This function:
  //
  // * Logically numbers each statement within the block
  //   (recursively),
  //
  // * Builds the transitive dependency set for each statement,
  //
  // * Determines the set of statements that access each chunk, and
  //   the intersection of the set of transitive dependencies for each
  //   of those statements (i.e. the set of statements known to have
  //   completed when the chunk's accessors might run).
  struct ToDo {
    stripe::Block* block;
    stripe::StatementIt next;
    AliasMap alias_map;
  };

  std::list<Chunk> result;
  std::unordered_map<std::string, Chunk*> chunks;

  auto add_block_chunks = [&](stripe::Block* block, const AliasMap& alias_map) {
    for (auto& ref : block->refs) {
      if (ref.dir == stripe::RefDir::None && locations.count(ref.location)) {
        auto chunk_it = result.emplace(result.end(), Chunk{&ref.mut(), alignment, stmt_limit});
        chunks[alias_map.at(ref.into()).base_name] = &*chunk_it;
      }
    }
  };

  AliasMap root_map;

  std::stack<ToDo> todo;
  todo.emplace(ToDo{outermost_block, outermost_block->stmts.begin(), AliasMap{root_map, outermost_block}});
  add_block_chunks(outermost_block, todo.top().alias_map);

  std::unordered_map<stripe::Statement*, StmtInfo> stmt_infos;

  std::size_t idx_limit = 0;

  while (todo.size()) {
    stripe::Block* block = todo.top().block;
    stripe::StatementIt& it = todo.top().next;
    AliasMap* alias_map = &todo.top().alias_map;

    for (;;) {
      if (it == block->stmts.end()) {
        todo.pop();
        break;
      }
      auto sit_placed = stmt_infos.emplace(it->get(), StmtInfo{idx_limit++, stmt_limit});
      StmtInfo& info = sit_placed.first->second;
      for (stripe::StatementIt& dep : (*it)->deps) {
        const auto& dep_info = stmt_infos.at(dep->get());
        info.transitive_deps.set(dep_info.idx);
        info.transitive_deps |= dep_info.transitive_deps;
      }
      ChunkUseRecorder recorder{&info, alias_map, &chunks};
      (*it)->Accept(&recorder);
      ++it;
      if (recorder.block()) {
        stripe::Block* sub_block = recorder.block();
        todo.emplace(ToDo{sub_block, sub_block->stmts.begin(), AliasMap{*alias_map, sub_block}});
        add_block_chunks(sub_block, todo.top().alias_map);
        break;
      }
    }
  }

  return result;
}

std::size_t CountStatements(stripe::Block* outermost_block) {
  std::size_t result = 0;

  std::queue<stripe::Block*> pending;
  pending.push(outermost_block);

  while (pending.size()) {
    stripe::Block* block = pending.front();
    pending.pop();
    result += block->stmts.size();
    for (const auto& stmt : block->stmts) {
      stripe::Block* child_block = dynamic_cast<stripe::Block*>(stmt.get());
      if (child_block) {
        pending.push(child_block);
      }
    }
  }

  return result;
}

}  // namespace

void PlaceRefinements(stripe::Block* outermost_block, const proto::MemoryPlacementPass& options) {
  std::set<stripe::Location> locations;
  for (const auto& loc : options.locs()) {
    locations.emplace(stripe::FromProto(loc));
  }

  std::size_t stmt_limit = CountStatements(outermost_block);
  auto alignment = options.alignment() ? options.alignment() : kDefaultAlignment;

  std::list<Chunk> chunks = BuildChunkList(outermost_block, locations, alignment, stmt_limit);

  // Edge case: no chunks means nothing to do.  And then after this,
  // we can assume there's at least one chunk.
  if (!chunks.size()) {
    return;
  }

  // Ensure chunks are sorted by earliest accessor.
  chunks.sort([](const Chunk& lhs, const Chunk& rhs) { return lhs.first_accessor_idx < rhs.first_accessor_idx; });

  // Initialize subsequent accessor dependencies.
  {
    boost::dynamic_bitset<> deps = chunks.back().transitive_accessor_deps;
    for (auto it = chunks.rbegin(); it != chunks.rend(); ++it) {
      deps &= it->transitive_accessor_deps;
      it->subsequent_accessor_deps = deps;
    }
  }

  // Initialize the graph.
  InterferenceGraph interference_graph;
  for (auto& chunk : chunks) {
    chunk.interference_vertex = boost::add_vertex(InterferenceVertex{&chunk}, interference_graph);
  }

  // Compute the interference edges.  Note that this is O(N^2) in the
  // worst case.  :-/
  for (auto earlier = chunks.begin(); earlier != chunks.end(); ++earlier) {
    auto later = earlier;
    ++later;
    for (; later != chunks.end(); ++later) {
      if (earlier->ref->location != later->ref->location) {
        // These cannot spatially overlap; they never interfere with
        // each other.
        continue;
      }
      if (earlier->accessors.is_subset_of(later->subsequent_accessor_deps)) {
        // All accessors of the earlier chunk are transitive
        // dependencies of this and every subsequent chunk; there can
        // be no interference, so we are done checking chunks later
        // than the current earliest.
        break;
      }
      if (earlier->accessors.is_subset_of(later->transitive_accessor_deps)) {
        // All accessors of the earlier chunk are transitive
        // dependencies of all of the accessors of the later chunk --
        // these chunks do not temporally overlap, and cannot
        // interfere with each other.
        continue;
      }
      // Otherwise, these chunks may be alive at the same time in the
      // same location; they may interfere with each other.
      add_edge(earlier->interference_vertex, later->interference_vertex, interference_graph);
    }
  }

  // Re-sort chunks by size, since we want to place them in
  // largest-to-smallest order.
  chunks.sort([](const Chunk& lhs, const Chunk& rhs) { return lhs.size > rhs.size; });

  // Place the chunks.
  std::vector<Chunk*> already_placed;
  for (auto& chunk : chunks) {
    // Build a vector of already-placed chunks that we need to
    // consider when placing this chunk.
    already_placed.clear();
    auto edges = out_edges(chunk.interference_vertex, interference_graph);
    for (auto it = edges.first; it != edges.second; ++it) {
      InterferenceGraph::vertex_descriptor tgt = target(*it, interference_graph);
      Chunk* candidate = interference_graph[tgt].chunk;
      if (candidate->placed) {
        already_placed.push_back(candidate);
      }
    }

    // Sort the vector by placement offset, lowest-offset first.
    std::sort(already_placed.begin(), already_placed.end(),
              [](const Chunk* lhs, const Chunk* rhs) { return lhs->ref->offset < rhs->ref->offset; });

    // Scan for usable gaps in the already_placed vector.
    // N.B. The already_placed vector may contain overlapping chunks.
    //
    // We keep track of the overall limit offset we've seen so far, as
    // well as whether we've seen a usable gap at all, the location of
    // that gap, and the known size of that gap.
    //
    // Note that the gap we're considering is always
    // [offset_limit...current->ref->offset).  This is because either:
    //
    // * We don't have a gap yet; some earlier-processed
    //   already-placed chunk must have started at an offset that's
    //   too low for the current chunk we're placing to fit before it,
    //   and subsequent chunks have either been overlapping or had too
    //   small a gap for us to consider.  offset_limit is the first
    //   offset that's known to be past all these earlier-processed
    //   chunks.
    //
    // * We do have a candidate gap.  It's the best gap we know about
    //   given all the chunks we've seen so far, which cover the
    //   entire space up to offset_limit.  So any better gap has to
    //   start at offset_limit.
    std::size_t offset_limit = 0;
    bool have_gap = false;
    std::size_t gap_offset = 0;
    std::size_t gap_size = 0;

    for (Chunk* placed_chunk : already_placed) {
      // See whether we have a usable gap here.
      if (offset_limit < placed_chunk->ref->offset) {
        std::size_t candidate_size = placed_chunk->ref->offset - offset_limit;
        if (chunk.size <= candidate_size && (!have_gap || (candidate_size < gap_size))) {
          // This is a usable gap, and either we don't have a gap or
          // the candidate gap is smaller than the best gap we've
          // found so far.
          have_gap = true;
          gap_offset = offset_limit;
          gap_size = candidate_size;
        }
      }

      // Update offset_limit.
      std::size_t placed_chunk_limit = placed_chunk->ref->offset + placed_chunk->size;
      if (offset_limit < placed_chunk_limit) {
        offset_limit = placed_chunk_limit;
      }
    }

    // If we don't have a gap yet, offset_limit is where we have to put the current chunk.
    if (!have_gap) {
      gap_offset = offset_limit;
    }

    // We have an offset for this chunk.
    chunk.ref->offset = gap_offset;
    chunk.placed = true;
  }
}

void MemoryPlacementPass::Apply(CompilerState* state) const {
  auto reqs = stripe::FromProto(options_.reqs());
  RunOnBlocks(state->entry(), reqs, [&](const AliasMap& map, stripe::Block* block) {  //
    PlaceRefinements(block, options_);
  });
}

namespace {
[[gnu::unused]] char reg = []() -> char {
  CompilePassFactory<MemoryPlacementPass, proto::MemoryPlacementPass>::Register();
  return 0;
}();
}  // namespace
}  // namespace codegen
}  // namespace tile
}  // namespace vertexai