11#ifndef __BOXES_CACHE_HPP__ 
   12#define __BOXES_CACHE_HPP__ 
   14#include "compiler.hpp" 
   19#include <unordered_map> 
   26template <
typename K, 
typename V> 
class LRU {
 
   28  virtual ~LRU() = 
default;
 
   38  LRU(std::size_t maxSize) : maxSize{maxSize} {}
 
   40  using AccessQueue = std::list<K>;
 
   41  using Iterator = 
typename AccessQueue::iterator;
 
   51  virtual void clear() { access.clear(); }
 
   53  virtual void onEvict(
const K &, Value &value) { access.erase(value.it); }
 
   55  virtual void onAccess(
const K &key, Value &value) {
 
   56    access.erase(value.it);
 
   57    value.it = access.insert(access.begin(), key);
 
   60  virtual Value onInsert(
const K &key, V value) {
 
   61    return Value{std::move(value), access.insert(access.begin(), key)};
 
   73  virtual K &front() { 
return access.front(); }
 
   84  virtual K &back() { 
return access.back(); }
 
   94  virtual Iterator begin() { 
return access.begin(); }
 
  101  virtual Iterator end() { 
return access.end(); }
 
  103  virtual const K &getKey(Iterator it) { 
return *it; }
 
  114  virtual const K *elect() {
 
  115    if (access.size() < maxSize) {
 
  118    return &this->access.back();
 
  122  const std::size_t maxSize;
 
  133  using AccessQueue = std::list<K>;
 
  134  using Iterator = 
typename AccessQueue::iterator;
 
  146  const K *
elect() { 
return nullptr; }
 
  153  void onEvict(
const K &, Value &) {}
 
  155  void onAccess(
const K &, Value &) {}
 
  157  Value onInsert(
const K &key, V value) {
 
  158    return Value{std::move(value), access.insert(access.end(), key)};
 
  169  K &
front() { 
return access.front(); }
 
  179  K &
back() { 
return access.back(); }
 
  186  Iterator 
begin() { 
return access.begin(); }
 
  194  Iterator 
end() { 
return access.end(); }
 
  196  const K &getKey(Iterator it) { 
return *it; }
 
  214template <
typename K, 
typename V> 
class MRU : 
public LRU<K, V> {
 
  216  using LRU<K, V>::LRU;
 
  218  void onAccess(
const K &key, 
typename LRU<K, V>::Value &value)
 override {
 
  219    this->access.erase(value.it);
 
  220    value.it = this->access.insert(this->access.rbegin().base(), key);
 
  223  typename LRU<K, V>::Value onInsert(
const K &key, V value)
 override {
 
  224    return typename LRU<K, V>::Value{
 
  226        this->access.insert(this->access.rbegin().base(), key)};
 
  238template <
typename K, 
typename V> 
class LFU {
 
  240  explicit LFU(std::size_t maxSize) : maxSize{maxSize} {}
 
  242  using FrequencyMap = std::multimap<std::size_t, K>;
 
  243  using Iterator = 
typename FrequencyMap::reverse_iterator;
 
  255  void onAccess(
const K &key, Value &value) {
 
  256    auto it = value.it.base();
 
  257    const auto freq = it->first;
 
  259    value.it = std::make_reverse_iterator(freqs.emplace(freq + 1, key));
 
  262  Value onInsert(
const K &key, V &&value) {
 
  263    constexpr std::size_t initialFreq = 0;
 
  264    auto rit = std::make_reverse_iterator(freqs.emplace(initialFreq, key));
 
  265    return Value{std::forward<V>(value), rit};
 
  268  void onEvict(
const K &, Value &value) { freqs.erase(value.it.base()); }
 
  270  const K &getKey(Iterator it) { 
return it->second; }
 
  279  K &
front() { 
return freqs.rbegin()->second; }
 
  288  K &
back() { 
return freqs.begin()->second; }
 
  297  Iterator 
begin() { 
return freqs.rbegin(); }
 
  305  Iterator 
end() { 
return freqs.rend(); }
 
  314    if (freqs.size() < maxSize) {
 
  318    return &freqs.begin()->second;
 
  322  const std::size_t maxSize;
 
  335template <
typename K, 
typename V,
 
  336          template <
typename, 
typename> 
class CachePolicy>
 
  339  using Policy = CachePolicy<K, V>;
 
  340  using PolicyIterator = 
typename Policy::Iterator;
 
  344    using iterator_category = std::bidirectional_iterator_tag;
 
  345    using value_type = std::pair<K, V>;
 
  346    using difference_type = std::ptrdiff_t;
 
  349        : cache{cache}, it{it} {}
 
  351    iterator &operator++() {
 
  356    iterator operator++(
int) {
 
  357      iterator copy = *
this;
 
  362    iterator &operator--() {
 
  367    iterator operator--(
int) {
 
  368      iterator copy = *
this;
 
  373    value_type &operator*() {
 
  374      const auto &key = cache.policy.getKey(it);
 
  375      const auto &pv = cache.data[key];
 
  376      valueProxy = std::make_pair(key, pv.value);
 
  380    value_type *operator->() {
 
  381      const auto &key = cache.policy.getKey(it);
 
  382      auto &pv = cache.data[key];
 
  383      valueProxy = std::make_pair(key, pv.value);
 
  387    bool operator==(
const iterator &other)
 const { 
return it == other.it; }
 
  389    bool operator!=(
const iterator &other)
 const { 
return it != other.it; }
 
  394    std::pair<K, V> valueProxy;
 
  402  explicit Cache(Policy policy) : policy{std::move(policy)} {}
 
  416  std::size_t 
size()
 const { 
return data.size(); }
 
  429  std::size_t 
max_size()
 const { 
return data.max_size(); }
 
  436  bool empty()
 const { 
return data.empty(); }
 
  457  template <
typename KArg, 
typename VArg>
 
  459    if (
auto it = data.find(key); it != data.end()) {
 
  460      policy.onAccess(key, it->second);
 
  464      if (
auto elected = policy.elect()) {
 
  465        if (
auto it = data.find(*elected); it != data.end()) {
 
  466          policy.onEvict(*elected, it->second);
 
  471      data.emplace(key, policy.onInsert(key, std::forward<VArg>(value)));
 
  484  bool contains(
const K &key)
 const { 
return data.find(key) != data.end(); }
 
  493  iterator 
begin() { 
return iterator{*
this, policy.begin()}; }
 
  503  iterator 
end() { 
return iterator{*
this, policy.end()}; }
 
  531    if (
auto it = data.find(key); it != data.end()) {
 
  532      policy.onAccess(key, it->second);
 
  533      return iterator{*
this, it->second.it};
 
  548  V &
at(
const K &key) {
 
  549    if (
auto it = data.find(key); it != data.end()) {
 
  550      policy.onAccess(key, it->second);
 
  551      return it->second.value;
 
  554    throw std::out_of_range{
"Key not found"};
 
  563  const V &
at(
const K &key)
 const { 
return const_cast<Cache *
>(
this)->
at(key); }
 
  573  K &
front() { 
return policy.front(); }
 
  583  K &
back() { 
return policy.back(); }
 
  600  using Value = 
typename Policy::Value;
 
  601  using Map = std::unordered_map<K, Value>;
 
  604  mutable Policy policy;
 
  615template <
typename K, 
typename V>
 
  628template <
typename K, 
typename V>
 
  640template <
typename K, 
typename V>
 
  653template <
typename K, 
typename V>
 
Cache< K, V, policy::LRU > makeLRU(std::size_t size)
Create a new LRU cache.
 
Cache< K, V, policy::MRU > makeMRU(std::size_t size)
Create a new MRU cache.
 
Cache< K, V, policy::NoEviction > makeNoEviction()
Create a new no eviction cache.
 
Cache< K, V, policy::LFU > makeLFU(std::size_t size)
Create a new LFU cache.
 
Generic cache with pluggable eviction policies.
 
iterator begin()
Returns an iterator to the first element in the cache.
 
iterator end()
Returns an iterator to the element following the last element in the cache.
 
Cache(Policy policy)
Construct a new Cache object.
 
const V & at(const K &key) const
Returns a const reference to the value associated with the given key.
 
void reserve(std::size_t size)
Reserve space for the cache to avoid unnecessary reallocations.
 
bool contains(const K &key) const
Check if the cache contains a key.
 
const K & front() const
Returns const reference the first element in the cache.
 
K & back()
Returns the reference the last element in the cache.
 
const K & back() const
Returns const reference the last element in the cache.
 
std::size_t max_size() const
Returns the maximum number of elements the cache can ever hold.
 
bool insert(KArg &&key, VArg &&value)
Inserts a key-value pair into the cache.
 
std::size_t size() const
Returns the number of elements in the cache.
 
iterator cend() const
Returns a const iterator to the element following the last element in the cache.
 
V & at(const K &key)
Returns a reference to the value associated with the given key.
 
iterator find(const K &key)
Returns an iterator to the element with the given key.
 
iterator cbegin() const
Returns a const iterator to the first element in the cache.
 
K & front()
Returns the reference the first element in the cache.
 
bool empty() const
Returns whether the cache is empty.
 
void clear()
Removes all elements from the cache.
 
Eviction policy that evicts the least frequently used element.
 
const K * elect()
Elects the least frequently used element for eviction.
 
Iterator begin()
Returns an iterator to the first element in the cache.
 
Iterator end()
Returns an iterator to the element following the last element in the cache.
 
K & back()
Returns the reference to the last element in the cache.
 
K & front()
Returns the reference to the first element in the cache.
 
void clear()
Clears the eviction policy.
 
Constructs a new MRU eviction policy.
 
Eviction policy that never evicts elements.
 
K & front()
Returns the reference to the first element in the cache.
 
Iterator end()
Returns an iterator to the element following the last element in the cache.
 
Iterator begin()
Returns an iterator to the first element in the cache.
 
const K * elect()
Never elects an element for eviction.
 
void clear()
Clears the eviction policy.
 
K & back()
Returns the reference to the last element in the cache.