
#ifndef COMPARISON_BENCHMARKS_BASE_UNBALANCED_H_
#define COMPARISON_BENCHMARKS_BASE_UNBALANCED_H_

#include <cstdint>
#include <array>
#include <vector>

#include "picosha2.h"

namespace comparison_benchmarks {
namespace base {
namespace unbalanced {

const int SEED = 42;
const int ROOT_CHILDREN = 140;
const double Q = 0.124875;
const int NORMAL_CHILDREN = 8;

const int NUM_NODES = 71069;

const int NUM_ITERATIONS = 50;
const int WARMUP_ITERATIONS = 5;

using node_state = std::array<uint8_t, 20>;

/**
 * Node of an unballanced binomial tree (https://www.cs.unc.edu/~olivier/LCPC06.pdf).
 * To build up the tree recursivly call spawn_child_nodes on each node until leaves are reached.
 * The tree is not built up directly in memory, but rather by the recursive calls.
 */
class node {
  // The state is used to allow a deterministic tree construction using sha256 hashes.
  node_state state_;

  // Number of children for the current node
  int num_children_;

  // Set this to a positive number for the root node to start the tree with a specific size
  int root_children_;

  // general branching factors
  double q_;
  int b_;

  // Private constructor for children
  node(node_state state, double q, int b) : state_{state},
                                            num_children_{0},
                                            root_children_{-1},
                                            q_{q},
                                            b_{b} { init_num_children(); }

  std::array<uint8_t, 20> generate_child_state(uint32_t index);
  double get_state_random();
  void init_num_children() {
    double state_random = get_state_random();
    if (root_children_ > 0) {
      num_children_ = root_children_; // Root always spawns children
    } else if (state_random < q_) {
      num_children_ = b_;
    } else {
      num_children_ = 0;
    }
  }

 public:
  node(uint32_t seed, int root_children, double q, int b)
      : state_({{}}), num_children_{0}, root_children_{root_children}, q_{q}, b_{b} {

    for (int i = 0; i < 16; i++) {
      state_[i] = 0;
    }
    state_[16] = static_cast<uint8_t>(0xFFu & (seed >> 24u));
    state_[17] = static_cast<uint8_t>(0xFFu & (seed >> 16u));
    state_[18] = static_cast<uint8_t>(0xFFu & (seed >> 8u));
    state_[19] = static_cast<uint8_t>(0xFFu & (seed >> 0u));

    picosha2::hash256_one_by_one hasher;
    hasher.process(state_.begin(), state_.end());
    hasher.finish();
    hasher.get_hash_bytes(state_.begin(), state_.end());

    init_num_children();
  }

  int get_num_children() const { return num_children_; }

  node spawn_child_node(int index) {
    return {generate_child_state(index), q_, b_};
  }
};

}
}
}

#endif //COMPARISON_BENCHMARKS_BASE_UNBALANCED_H_
