<commit_before>#include "Palette.h"

namespace sfc {

void Palette::add(const rgba_t color) {
  std::vector<rgba_t> v = {color};
  add(v);
}

// add colors by appending to first subpalette with enough free colors
void Palette::add(const std::vector<rgba_t>& colors) {
  auto rc = reduce_colors(colors, _mode);

  if (!_subpalettes.size()) {
    // always add initial color
    Subpalette& new_sp = add_subpalette();
    new_sp.add(rc);
    return;
  }

  std::set<rgba_t> cs(rc.begin(), rc.end());
  cs.erase(transparent_color);
  if (cs.size() > _max_colors_per_subpalette) throw std::runtime_error("Colors don't fit in palette");

  Subpalette* best_candidate = nullptr;
  unsigned best_diff = UINT_MAX;
  for (Subpalette& sp : _subpalettes) {
    unsigned diff = sp.diff(cs);
    if (diff > 0 && sp.is_full()) continue;
    if (diff < best_diff && diff <= sp.capacity_left()) {
      best_diff = diff;
      best_candidate = &sp;
    }
  }

  if (best_diff == 0) {
    return;
  } else if (!best_candidate || best_candidate->is_full()) {
    Subpalette& new_sp = add_subpalette();
    new_sp.add(std::vector<rgba_t>(cs.begin(), cs.end()));
  } else {
    best_candidate->add(std::vector<rgba_t>(cs.begin(), cs.end()));
  }
}

// add colors from ImageCrops
void Palette::add(std::vector<sfc::ImageCrop> palette_tiles) {
  unsigned palette_errors = 0;

  std::sort(palette_tiles.begin(), palette_tiles.end(), [](const sfc::ImageCrop& a, const sfc::ImageCrop& b) -> bool {
    return a.colors.size() > b.colors.size();
  });

  for (auto& t : palette_tiles) {
    try {
      add(t.colors_v());
    } catch (...) {
      fmt::print("Can't fit colors for tile [{},{}] in available palettes (at {},{} in source image)\n",
                 (unsigned)(t.coord_x() / t.width()), (unsigned)(t.coord_y() / t.height()), t.coord_x(), t.coord_y());
      ++palette_errors;
    }
  }

  if (palette_errors > 0) throw std::runtime_error("Colors in image do not fit in available palettes. Aborting.");
}

// add colors without reordering or discarding duplicates
void Palette::add_noremap(const std::vector<rgba_t>& colors, bool reduce) {
  auto rc = colors;
  if (reduce) rc = reduce_colors(rc, _mode);
  auto splits = split_vector(rc, _max_colors_per_subpalette);
  for (auto& sv : splits) {
    Subpalette sp(_mode, _max_colors_per_subpalette);
    sp.add(sv, true);
    _subpalettes.push_back(sp);
  }
}

void Palette::sort() {
  for (auto& sp : _subpalettes) sp.sort();
}

void Palette::pad() {
  for (auto& sp : _subpalettes) sp.pad();
}

const Subpalette& Palette::subpalette_at(unsigned index) const {
  if (index > _subpalettes.size()) throw std::runtime_error("Subpalette doesn't exist");
  return _subpalettes.at(index);
}

// get first subpalette containing all colors in image
const Subpalette& Palette::subpalette_matching(const Image& image) const {
  auto rc = reduce_colors(image.rgba_data(), _mode);
  std::set<rgba_t> cs(rc.begin(), rc.end());
  cs.erase(transparent_color);
  if (cs.size() > _max_colors_per_subpalette) throw std::runtime_error("Colors don't fit in palette"); // TODO: catch and report position

  const Subpalette* matching_sp = nullptr;
  for (const Subpalette& sp : _subpalettes) {
    if (sp.diff(cs) == 0) {
      matching_sp = &sp;
      break;
    }
  }

  if (matching_sp == nullptr) throw std::runtime_error("No matching palette for image"); // TODO: catch and report position
  return *matching_sp;
}

std::vector<const Subpalette*> Palette::subpalettes_matching(const Image& image) const {
  std::vector<const Subpalette*> sv;
<commit_msg>A touch of <algorithm><commit_after>#include "Palette.h"

namespace sfc {

void Palette::add(const rgba_t color) {
  std::vector<rgba_t> v = {color};
  add(v);
}

// add colors by appending to first subpalette with enough free colors
void Palette::add(const std::vector<rgba_t>& colors) {
  auto rc = reduce_colors(colors, _mode);

  if (!_subpalettes.size()) {
    // always add initial color
    Subpalette& new_sp = add_subpalette();
    new_sp.add(rc);
    return;
  }

  std::set<rgba_t> cs(rc.begin(), rc.end());
  cs.erase(transparent_color);
  if (cs.size() > _max_colors_per_subpalette) throw std::runtime_error("Colors don't fit in palette");

  Subpalette* best_candidate = nullptr;
  unsigned best_diff = UINT_MAX;
  for (Subpalette& sp : _subpalettes) {
    unsigned diff = sp.diff(cs);
    if (diff > 0 && sp.is_full()) continue;
    if (diff < best_diff && diff <= sp.capacity_left()) {
      best_diff = diff;
      best_candidate = &sp;
    }
  }

  if (best_diff == 0) {
    return;
  } else if (!best_candidate || best_candidate->is_full()) {
    Subpalette& new_sp = add_subpalette();
    new_sp.add(std::vector<rgba_t>(cs.begin(), cs.end()));
  } else {
    best_candidate->add(std::vector<rgba_t>(cs.begin(), cs.end()));
  }
}

// add colors from ImageCrops
void Palette::add(std::vector<sfc::ImageCrop> palette_tiles) {
  unsigned palette_errors = 0;

  std::sort(palette_tiles.begin(), palette_tiles.end(), [](const sfc::ImageCrop& a, const sfc::ImageCrop& b) -> bool {
    return a.colors.size() > b.colors.size();
  });

  for (auto& t : palette_tiles) {
    try {
      add(t.colors_v());
    } catch (...) {
      fmt::print("Can't fit colors for tile [{},{}] in available palettes (at {},{} in source image)\n",
                 (unsigned)(t.coord_x() / t.width()), (unsigned)(t.coord_y() / t.height()), t.coord_x(), t.coord_y());
      ++palette_errors;
    }
  }

  if (palette_errors > 0) throw std::runtime_error("Colors in image do not fit in available palettes. Aborting.");
}

// add colors without reordering or discarding duplicates
void Palette::add_noremap(const std::vector<rgba_t>& colors, bool reduce) {
  auto rc = colors;
  if (reduce) rc = reduce_colors(rc, _mode);
  auto splits = split_vector(rc, _max_colors_per_subpalette);
  for (auto& sv : splits) {
    Subpalette sp(_mode, _max_colors_per_subpalette);
    sp.add(sv, true);
    _subpalettes.push_back(sp);
  }
}

void Palette::sort() {
  for (auto& sp : _subpalettes) sp.sort();
}

void Palette::pad() {
  for (auto& sp : _subpalettes) sp.pad();
}

const Subpalette& Palette::subpalette_at(unsigned index) const {
  if (index > _subpalettes.size()) throw std::runtime_error("Subpalette doesn't exist");
  return _subpalettes.at(index);
}

// get first subpalette containing all colors in image
const Subpalette& Palette::subpalette_matching(const Image& image) const {
  auto rc = reduce_colors(image.rgba_data(), _mode);
  std::set<rgba_t> cs(rc.begin(), rc.end());
  cs.erase(transparent_color);
  if (cs.size() > _max_colors_per_subpalette) throw std::runtime_error("Colors don't fit in palette"); // TODO: catch and report position

  auto match = std::find_if(_subpalettes.begin(), _subpalettes.end(), [&](const auto& val) -> bool {
    return val.diff(cs) == 0;
  });

  if (match == _subpalettes.end()) throw std::runtime_error("No matching palette for image"); // TODO: catch and report position
  return *match;
}

std::vector<const Subpalette*> Palette::subpalettes_matching(const Image& image) const {
  std::vector<const Subpalette*> sv;

{'fraction_non_alphanumeric': 0.15614478114478114, 'fraction_numerical': 0.0014029180695847362, 'mean_word_length': 4.1472924187725635, 'pattern_counts': {'":': 0, '<': 28, '<?xml version=': 0, '>': 43, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2590066', 'n_tokens_mistral': 2563, 'n_tokens_neox': 2494, 'n_words': 597}