Skip to content

tag_container.hpp

#include "noya/tag_container.hpp"

View on GitHub

#ifndef NOYA_TAG_CONTAINER_HPP
#define NOYA_TAG_CONTAINER_HPP 1

#include "noya/hashmap.hpp"
#include <algorithm>
#include <cassert>
#include <functional>
#include <map>
#include <vector>

namespace noya {
/// @brief HashMap with a global additive tag, supporting mergeable insert and bulk offset.
template <class K, class V, auto op> struct tag_HashMap {
  HashMap<K, V> S;
  V tag = V();
  /// @brief Insert or merge a key-value pair (value is the true value before tag).
  void insert(K key, V value) {
    auto it = S.find(key);
    if (it == S.end()) {
      S[key] = value - tag;
    } else {
      S[key] = op(it->second, value - tag);
    }
  }

  void erase(K key) { S.erase(key); }
  int size() const { return (int)S.size(); }

  /// @brief Merge another tag_HashMap into this one (small-to-large).
  void join(tag_HashMap<K, V, op> &other) {
    if (size() > other.size()) {
      for (auto &[key, value] : other.S) {
        insert(key, value + other.tag);
      }
    } else {
      S.swap(other.S);
      swap(tag, other.tag);
      for (auto &[key, value] : other.S) {
        insert(key, value + other.tag);
      }
    }
  }

  /// @brief Add x to the global tag (offsets all stored values).
  void add(V x) { tag += x; }

  // return the true value
  V operator[](const K &x) {
    if (S.find(x) == S.end()) {
      return V();
    } else {
      V value = S[x];
      return value + tag;
    }
  }
};

} // namespace noya

#endif // NOYA_TAG_CONTAINER_HPP
#include <algorithm>
#include <cassert>
#include <chrono>
#include <functional>
#include <map>
#include <unordered_map>
#include <vector>

namespace noya {
/// @brief Randomized splitmix64 hash to prevent hash collision attacks.
struct splitmix64_hash {
  static uint64_t splitmix64(uint64_t x) {
    // http://xorshift.di.unimi.it/splitmix64.c
    x += 0x9e3779b97f4a7c15;
    x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
    x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
    return x ^ (x >> 31);
  }
  size_t operator()(uint64_t x) const {
    static const uint64_t FIXED_RANDOM =
        std::chrono::steady_clock::now().time_since_epoch().count();
    return splitmix64(x + FIXED_RANDOM);
  }
};

/// @brief Unordered map with anti-hash randomized hash function.
template <typename K, typename V, typename Hash = splitmix64_hash>
using HashMap = std::unordered_map<K, V, Hash>;

} // namespace noya

namespace noya {
/// @brief HashMap with a global additive tag, supporting mergeable insert and bulk offset.
template <class K, class V, auto op> struct tag_HashMap {
  HashMap<K, V> S;
  V tag = V();
  /// @brief Insert or merge a key-value pair (value is the true value before tag).
  void insert(K key, V value) {
    auto it = S.find(key);
    if (it == S.end()) {
      S[key] = value - tag;
    } else {
      S[key] = op(it->second, value - tag);
    }
  }

  void erase(K key) { S.erase(key); }
  int size() const { return (int)S.size(); }

  /// @brief Merge another tag_HashMap into this one (small-to-large).
  void join(tag_HashMap<K, V, op> &other) {
    if (size() > other.size()) {
      for (auto &[key, value] : other.S) {
        insert(key, value + other.tag);
      }
    } else {
      S.swap(other.S);
      swap(tag, other.tag);
      for (auto &[key, value] : other.S) {
        insert(key, value + other.tag);
      }
    }
  }

  /// @brief Add x to the global tag (offsets all stored values).
  void add(V x) { tag += x; }

  // return the true value
  V operator[](const K &x) {
    if (S.find(x) == S.end()) {
      return V();
    } else {
      V value = S[x];
      return value + tag;
    }
  }
};

} // namespace noya