Skip to main content
Add template tag
Link
JimmyHu
  • 5.5k
  • 2
  • 10
  • 40
Source Link

C++ template and inheritance - Generic Sqrt Decomposition

So I tried implementing a easily extendable solution for Sqrt decompostion, I deduced that only identity value, operation and block update logic change and rest of the code is same. So i created 3 functions

  • T identity()
  • T operation(const T&a, const T&b)
  • T block_update_calc(const T old_block_value, const T, const T &old_data_value)

I earlier tried inheritance, where these were pure virtual functions in base class and these are implemented in derived classes. But I then found out that we can't call virtual functions of derived class in constructor of base class which can be mitigated at cost of syntax.

Current solution is template based and AdditionSqrtDecomposition and MultiplicationSqrtDecomposition behave exactly like I want, but code looks bit messy, how can I improve this code?

Usage:

vector<int> nums(N);

AdditionSqrtDecomposition<int> sd_addition(nums);
MultiplicationSqrtDecomposition<int> sd_multiplication(nums);

Implementation

#ifndef SQRT_DECOMPOSITION_HPP
#define SQRT_DECOMPOSITION_HPP

#include <cmath>
#include <vector>

template <typename T, class Core> class SqrtDecomposition {
public:
  std::vector<T> _data, _decomp;
  int _block_size;

  // constructors, asssignment, destructor
  explicit SqrtDecomposition(const std::vector<T> &data)
      : _data(data), _decomp(ceil(sqrt(data.size())), Core::identitiy()),
        _block_size(::sqrt(data.size())) {

    int curr_block = -1;
    for (int i = 0; i < _data.size(); ++i) {
      if (i % _block_size == 0) {
        curr_block++;
      }
      auto &d = _decomp[curr_block];
      d = Core::operation(d, _data[i]);
    }
  }

  T query(int l, int r) {
    T result = Core::identitiy(); // identitiy
    for (; l <= r and l % _block_size != 0; l++) {
      // individuals before a block
      result = Core::operation(result, _data[l]);
    }

    for (int i = l / _block_size; l + _block_size <= r; ++i, l += _block_size) {
      // block representatives
      result = Core::operation(result, _decomp[i]);
    }

    for (; l <= r; ++l) {
      // individuals after a block
      result = Core::operation(result, _data[l]);
    }
    return result;
  }

  void update(int index, T value) {
    int representating_block = index / _block_size;
    _decomp[representating_block] = Core::block_update_calc(
        _decomp[representating_block], value, _data[index]);
    _data[index] = value;
  }
};

// interface
template <typename T> class OperationCore {
public:
  static T identitiy() = 0;
  static T operation(const T &a, const T &b) = 0;
  static T block_update_calc(const T old_block_value, const T new_data_value,
                             const T &old_data_value) = 0;
};

template <typename T> class AdditionCore {
public:
  static T identitiy() { return 0; }
  static T operation(const T &a, const T &b) { return a + b; }
  static T block_update_calc(const T old_block_value, const T new_data_value,
                             const T &old_data_value) {
    return old_block_value + new_data_value - old_data_value;
  }
};

template <typename T> class MultiplicationCore {
public:
  static T identitiy() { return 1; }
  static T operation(const T &a, const T &b) { return a * b; }
  static T block_update_calc(const T old_block_value, const T new_data_value,
                             const T &old_data_value) {
    return (old_block_value * new_data_value) / old_block_value;
  }
};

template <typename T>
class AdditionSqrtDecomposition : public SqrtDecomposition<T, AdditionCore<T>> {
  using SqrtDecomposition<T, AdditionCore<T>>::SqrtDecomposition;
};

template <typename T>
class MultiplicationSqrtDecomposition
    : public SqrtDecomposition<T, MultiplicationCore<T>> {
  using SqrtDecomposition<T, MultiplicationCore<T>>::SqrtDecomposition;
};

#endif // SQRT_DECOMPOSITION_HPP