/*
    This file is part of TON Blockchain Library.
    TON Blockchain Library is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.
    TON Blockchain Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.
    You should have received a copy of the GNU Lesser General Public License
    along with TON Blockchain Library.  If not, see .
    Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include "common/bitstring.h"
#include "vm/cells.h"
#include "vm/cellslice.h"
#include "vm/stack.hpp"
#include 
namespace vm {
using td::BitSlice;
using td::Ref;
namespace dict {
struct LabelParser {
  enum { chk_none = 0, chk_min = 1, chk_size = 2, chk_all = 3 };
  Ref remainder;
  int l_offs;
  int l_same;
  int l_bits;
  unsigned s_bits;
  LabelParser(Ref cs, int max_label_len, int auto_validate = chk_all);
  LabelParser(Ref| cell, int max_label_len, int auto_validate = chk_all);
  int is_valid() const {
    return l_offs;
  }
  void validate() const;
  void validate_simple(int n) const;
  void validate_ext(int n) const;
  bool is_prefix_of(td::ConstBitPtr key, int len) const;
  bool has_prefix(td::ConstBitPtr key, int len) const;
  int common_prefix_len(td::ConstBitPtr key, int len) const;
  int extract_label_to(td::BitPtr to);
  int copy_label_prefix_to(td::BitPtr to, int max_len) const;
  td::ConstBitPtr bits() const {
    return remainder->data_bits();
  }
  td::ConstBitPtr bits_end() const {
    return bits() + l_bits;
  }
  void skip_label() {
    remainder.write().advance(s_bits);
  }
  void clear() {
    remainder.clear();
  }
 private:
  bool parse_label(CellSlice& cs, int max_label_len);
};
struct AugmentationData {
  virtual ~AugmentationData() = default;
  virtual bool skip_extra(vm::CellSlice& cs) const = 0;
  virtual bool eval_leaf(vm::CellBuilder& cb, vm::CellSlice& val_cs) const = 0;
  virtual bool eval_fork(vm::CellBuilder& cb, vm::CellSlice& left_cs, vm::CellSlice& right_cs) const = 0;
  virtual bool eval_empty(vm::CellBuilder& cb) const = 0;
  virtual bool check_leaf(vm::CellSlice& cs, vm::CellSlice& val_cs) const;
  virtual bool check_fork(vm::CellSlice& cs, vm::CellSlice& left_cs, vm::CellSlice& right_cs) const;
  virtual bool check_empty(vm::CellSlice& cs) const;
  virtual bool check_leaf_key_extra(vm::CellSlice& val_cs, vm::CellSlice& extra_cs, td::ConstBitPtr key,
                                    int key_len) const {
    return check_leaf(extra_cs, val_cs);
  }
  Ref extract_extra(vm::CellSlice& cs) const;
  Ref extract_extra(Ref cs_ref) const;
  bool extract_extra_to(vm::CellSlice& cs, Ref& extra_csr) const {
    return (extra_csr = extract_extra(cs)).not_null();
  }
  bool extract_extra_to(Ref cs_ref, Ref& extra_csr) const {
    return (extra_csr = extract_extra(std::move(cs_ref))).not_null();
  }
  bool extract_extra_to(vm::CellSlice& cs, vm::CellSlice& extra) const;
};
static inline bool store_cell_dict(vm::CellBuilder& cb, Ref dict_root) {
  return dict_root.not_null() ? cb.store_long_bool(1, 1) && cb.store_ref_bool(std::move(dict_root))
                              : cb.store_long_bool(0, 1);
}
}  // namespace dict
struct CombineError {};  // thrown by Dictionary::combine_with
struct CombineErrorValue {
  int arg_;
};
struct DictNonEmpty {};
struct DictAdvance {};
class DictionaryBase {
 protected:
  mutable Ref root;
  Ref | root_cell;
  int key_bits;
  mutable int flags;
  enum { f_valid = 1, f_root_cached = 2, f_invalid = 0x80 };
 public:
  enum class SetMode : int { Set = 3, Replace = 1, Add = 2 };
  enum { max_key_bits = 1023, max_key_bytes = (max_key_bits + 7) / 8 };
  typedef std::function store_value_func_t;
  DictionaryBase(int _n, bool validate = true);
  DictionaryBase(Ref _root, int _n, bool validate = true);
  DictionaryBase(const CellSlice& root_cs, int _n, bool validate = true);
  DictionaryBase(DictAdvance, CellSlice& root_cs, int _n, bool validate = true);
  DictionaryBase(Ref | cell, int _n, bool validate = true);
  DictionaryBase(DictNonEmpty, Ref _root, int _n, bool validate = true);
  DictionaryBase(DictNonEmpty, const CellSlice& root_cs, int _n, bool validate = true);
  virtual ~DictionaryBase() = default;
  static Ref | construct_root_from(const CellSlice& root_node_cs);
  Ref get_root() const;
  Ref extract_root() &&;
  Ref | get_root_cell() const {
    return root_cell;
  }
  Ref | extract_root_cell() && {
    return std::move(root_cell);
  }
  bool append_dict_to_bool(CellBuilder& cb) &&;
  bool append_dict_to_bool(CellBuilder& cb) const &;
  int get_key_bits() const {
    return key_bits;
  }
  bool is_valid() const {
    return flags & f_valid;
  }
  void reset() {
    root.clear();
    root_cell.clear();
    flags = f_valid;
  }
  virtual bool validate();
  void force_validate();
  bool is_empty() const {
    return root_cell.is_null();
  }
  static Ref get_empty_dictionary();
 protected:
  bool init_root_for_nonempty(const CellSlice& cs);
  bool invalidate() {
    flags |= f_invalid;
    return false;
  }
  bool compute_root() const;
  static Ref new_empty_dictionary();
  void set_root_cell(Ref | cell) {
    root_cell = std::move(cell);
    flags &= ~f_root_cached;
  }
};
class DictIterator;
template 
std::pair dict_range(T&& dict, bool rev = false, bool sgnd = false) {
  return std::pair{std::forward(dict), (int)rev + 2 * (int)sgnd};
}
class DictionaryFixed : public DictionaryBase {
 public:
  typedef std::function filter_func_t;
  typedef std::function, Ref)> simple_combine_func_t;
  typedef std::function, Ref, td::ConstBitPtr, int)> combine_func_t;
  typedef std::function, td::ConstBitPtr, int)> foreach_func_t;
  typedef std::function, Ref)> scan_diff_func_t;
  DictionaryFixed(int _n, bool validate = true) : DictionaryBase(_n, validate) {
  }
  DictionaryFixed(Ref _root, int _n, bool validate = true) : DictionaryBase(std::move(_root), _n, validate) {
  }
  DictionaryFixed(const CellSlice& root_cs, int _n, bool validate = true) : DictionaryBase(root_cs, _n, validate) {
  }
  DictionaryFixed(DictAdvance, CellSlice& root_cs, int _n, bool validate = true)
      : DictionaryBase(DictAdvance(), root_cs, _n, validate) {
  }
  DictionaryFixed(Ref | cell, int _n, bool validate = true) : DictionaryBase(std::move(cell), _n, validate) {
  }
  DictionaryFixed(DictNonEmpty, Ref _root, int _n, bool validate = true)
      : DictionaryBase(DictNonEmpty(), std::move(_root), _n, validate) {
  }
  DictionaryFixed(DictNonEmpty, const CellSlice& root_cs, int _n, bool validate = true)
      : DictionaryBase(DictNonEmpty(), root_cs, _n, validate) {
  }
  static BitSlice integer_key(td::RefInt256 x, unsigned n, bool sgnd = true, unsigned char buffer[128] = 0,
                              bool quiet = false);
  static bool integer_key_simple(td::RefInt256 x, unsigned n, bool sgnd, td::BitPtr buffer, bool quiet = false);
  td::RefInt256 key_as_integer(td::ConstBitPtr key, bool sgnd = false) const {
    return td::bits_to_refint(key, key_bits, sgnd);
  }
  bool key_exists(td::ConstBitPtr key, int key_len);
  bool int_key_exists(long long key);
  bool uint_key_exists(unsigned long long key);
  Ref lookup(td::ConstBitPtr key, int key_len);
  Ref lookup_delete(td::ConstBitPtr key, int key_len);
  Ref get_minmax_key(td::BitPtr key_buffer, int key_len, bool fetch_max = false, bool invert_first = false);
  Ref extract_minmax_key(td::BitPtr key_buffer, int key_len, bool fetch_max = false,
                                    bool invert_first = false);
  Ref lookup_nearest_key(td::BitPtr key_buffer, int key_len, bool fetch_next = false, bool allow_eq = false,
                                    bool invert_first = false);
  bool has_common_prefix(td::ConstBitPtr prefix, int prefix_len);
  int get_common_prefix(td::BitPtr buffer, unsigned buffer_len);
  bool cut_prefix_subdict(td::ConstBitPtr prefix, int prefix_len, bool remove_prefix = false);
  Ref extract_prefix_subdict_root(td::ConstBitPtr prefix, int prefix_len, bool remove_prefix = false);
  bool check_for_each(const foreach_func_t& foreach_func, bool invert_first = false, bool shuffle = false);
  int filter(filter_func_t check);
  bool combine_with(DictionaryFixed& dict2, const combine_func_t& combine_func, int mode = 0);
  bool combine_with(DictionaryFixed& dict2, const simple_combine_func_t& simple_combine_func, int mode = 0);
  bool combine_with(DictionaryFixed& dict2);
  bool scan_diff(DictionaryFixed& dict2, const scan_diff_func_t& diff_func, int check_augm = 0);
  bool validate_check(const foreach_func_t& foreach_func, bool invert_first = false);
  bool validate_all();
  DictIterator null_iterator();
  DictIterator init_iterator(bool backw = false, bool invert_first = false);
  DictIterator make_iterator(int mode);
  DictIterator begin();
  DictIterator end();
  DictIterator cbegin();
  DictIterator cend();
  DictIterator rbegin();
  DictIterator rend();
  DictIterator crbegin();
  DictIterator crend();
  template 
  bool key_exists(const T& key) {
    return key_exists(key.bits(), key.size());
  }
  template 
  Ref lookup(const T& key) {
    return lookup(key.bits(), key.size());
  }
  template 
  Ref lookup_delete(const T& key) {
    return lookup_delete(key.bits(), key.size());
  }
  template 
  Ref get_minmax_key(T& key_buffer, bool fetch_max = false, bool invert_first = false) {
    return get_minmax_key(key_buffer.bits(), key_buffer.size(), fetch_max, invert_first);
  }
  template 
  Ref lookup_nearest_key(T& key_buffer, bool fetch_next = false, bool allow_eq = false,
                                    bool invert_first = false) {
    return lookup_nearest_key(key_buffer.bits(), key_buffer.size(), fetch_next, allow_eq, invert_first);
  }
 protected:
  virtual int label_mode() const {
    return dict::LabelParser::chk_all;
  }
  virtual Ref extract_leaf_value(Ref leaf) const {
    return leaf;
  }
  virtual Ref | finish_create_leaf(CellBuilder& cb, const CellSlice& value) const;
  virtual Ref | finish_create_fork(CellBuilder& cb, Ref | c1, Ref | c2, int n) const;
  virtual bool check_fork(CellSlice& cs, Ref | c1, Ref | c2, int n) const {
    return true;
  }
  virtual bool check_leaf(CellSlice& cs, td::ConstBitPtr key, int key_len) const {
    return true;
  }
  bool check_leaf(Ref cs_ref, td::ConstBitPtr key, int key_len) const {
    return check_leaf(cs_ref.write(), key, key_len);
  }
  bool check_fork_raw(Ref cs_ref, int n) const;
  friend class DictIterator;
 private:
  std::pair [, Ref]| > dict_lookup_delete(Ref | dict, td::ConstBitPtr key, int n) const;
  Ref dict_lookup_minmax(Ref | dict, td::BitPtr key_buffer, int n, int mode) const;
  Ref dict_lookup_nearest(Ref | dict, td::BitPtr key_buffer, int n, bool allow_eq, int mode) const;
  std::pair [, bool> extract_prefix_subdict_internal(Ref]| dict, td::ConstBitPtr prefix, int prefix_len,
                                                             bool remove_prefix = false) const;
  bool dict_check_for_each(Ref | dict, td::BitPtr key_buffer, int n, int total_key_len,
                           const foreach_func_t& foreach_func, bool invert_first = false, bool shuffle = false) const;
  std::pair [, int> dict_filter(Ref]| dict, td::BitPtr key, int n, const filter_func_t& check_leaf,
                                        int& skip_rest) const;
  Ref | dict_combine_with(Ref | dict1, Ref | dict2, td::BitPtr key_buffer, int n, int total_key_len,
                              const combine_func_t& combine_func, int mode = 0, int skip1 = 0, int skip2 = 0) const;
  bool dict_scan_diff(Ref | dict1, Ref | dict2, td::BitPtr key_buffer, int n, int total_key_len,
                      const scan_diff_func_t& diff_func, int mode = 0, int skip1 = 0, int skip2 = 0) const;
  bool dict_validate_check(Ref | dict, td::BitPtr key_buffer, int n, int total_key_len,
                           const foreach_func_t& foreach_func, bool invert_first = false) const;
};
class DictIterator {
  const DictionaryFixed* dict_{nullptr};
  Ref | root_;
  int label_mode_{dict::LabelParser::chk_size};
  int key_bits_;
  int flags_;
  int order_;
  unsigned char key_buffer[DictionaryBase::max_key_bytes];
  bool prevalidate(int mode = -1);
  enum { f_valid = 4 };
 protected:
  struct Fork {
    Ref | next, alt;
    int pos;
    bool v;
    Fork() : pos(-1) {
    }
    Fork(Ref | _next, Ref | _alt, int _pos, bool _v)
        : next(std::move(_next)), alt(std::move(_alt)), pos(_pos), v(_v) {
    }
    void rotate(td::BitPtr key) {
      std::swap(next, alt);
      key[pos] = (v ^= true);
    }
  };
  std::vector path_;
  Ref leaf_;
  td::BitPtr key(int offs = 0) {
    return td::BitPtr{key_buffer, offs};
  }
  td::ConstBitPtr key(int offs = 0) const {
    return td::ConstBitPtr{key_buffer, offs};
  }
  td::ConstBitPtr ckey(int offs = 0) const {
    return td::ConstBitPtr{key_buffer, offs};
  }
 public:
  DictIterator() : key_bits_(0), flags_(0), order_(0) {
  }
  // mode: 0 = bidir, +4 = fwd only, +8 = back only; +1 = reverse directions, +2 = signed int keys
  enum { it_reverse = 1, it_signed = 2 };
  DictIterator(Ref | root_cell, int key_bits, int mode = 0)
      : root_(std::move(root_cell)), key_bits_(key_bits), flags_(mode >> 2) {
    prevalidate(mode & 3);
  }
  DictIterator(const DictionaryFixed& dict, int mode = 0)
      : DictIterator(dict.get_root_cell(), dict.get_key_bits(), mode) {
    dict_ = &dict;
    label_mode_ = dict.label_mode();
  }
  bool is_valid() const {
    return flags_ & f_valid;
  }
  bool eof() const {
    return leaf_.is_null();
  }
  bool reset() {
    dict_ = nullptr;
    root_.clear();
    path_.clear();
    leaf_.clear();
    return true;
  }
  td::ConstBitPtr cur_pos() const {
    return eof() ? td::ConstBitPtr{nullptr} : key();
  }
  Ref | get_root_cell() const {
    return root_;
  }
  int get_key_bits() const {
    return key_bits_;
  }
  bool is_bound() const {
    return dict_;
  }
  bool is_bound_to(const DictionaryFixed& dict) const {
    return root_.not_null() == dict.get_root_cell().not_null() &&
           (root_.not_null() ? root_.get() == dict.get_root_cell().get() : key_bits_ == dict.get_key_bits());
  }
  bool bind(const DictionaryFixed& dict, int do_rewind = 0);
  bool rebind_to(const DictionaryFixed& dict, int do_rewind = 0);
  bool rewind(bool to_end = false);
  bool next(bool backw = false);
  bool prev() {
    return next(true);
  }
  bool lookup(td::ConstBitPtr pos, int pos_bits, bool strict_after = false, bool backw = false);
  template 
  bool lookup(const T& key, bool strict_after = false, bool backw = false) {
    return lookup(key.bits(), key.size(), strict_after, backw);
  }
  Ref cur_value() const {
    return dict_ ? dict_->extract_leaf_value(leaf_) : Ref{};
  }
  Ref cur_value_raw() const {
    return leaf_;
  }
  std::pair> operator*() const {
    return std::make_pair(cur_pos(), cur_value());
  }
  bool bound_to_same(const DictIterator& other) const {
    return dict_ && dict_ == other.dict_;
  }
  bool operator==(const DictIterator& other) const {
    return bound_to_same(other) && eof() == other.eof() && (eof() || key().equals(other.key(), key_bits_));
  }
  int compare_keys(td::ConstBitPtr a, td::ConstBitPtr b) const;
  bool operator<(const DictIterator& other) const {
    return bound_to_same(other) && !eof() && (other.eof() || compare_keys(key(), other.key()) < 0);
  }
  bool operator!=(const DictIterator& other) const {
    return !(operator==(other));
  }
  bool operator>(const DictIterator& other) const {
    return other < *this;
  }
  DictIterator& operator++() {
    next();
    return *this;
  }
  DictIterator& operator--() {
    next(true);
    return *this;
  }
 private:
  bool dive(int mode);
};
template 
DictIterator begin(std::pair dictm) {
  return dictm.first.make_iterator(dictm.second);
}
template 
DictIterator end(std::pair dictm) {
  return dictm.first.null_iterator();
}
class Dictionary final : public DictionaryFixed {
 public:
  typedef std::function)> simple_map_func_t;
  typedef std::function, td::ConstBitPtr, int)> map_func_t;
  Dictionary(int _n, bool validate = true) : DictionaryFixed(_n, validate) {
  }
  Dictionary(Ref _root, int _n, bool validate = true) : DictionaryFixed(std::move(_root), _n, validate) {
  }
  Dictionary(const CellSlice& root_cs, int _n, bool validate = true) : DictionaryFixed(root_cs, _n, validate) {
  }
  Dictionary(DictAdvance, CellSlice& root_cs, int _n, bool validate = true)
      : DictionaryFixed(DictAdvance(), root_cs, _n, validate) {
  }
  Dictionary(Ref | cell, int _n, bool validate = true) : DictionaryFixed(std::move(cell), _n, validate) {
  }
  Dictionary(DictNonEmpty, Ref _root, int _n, bool validate = true)
      : DictionaryFixed(DictNonEmpty(), std::move(_root), _n, validate) {
  }
  Dictionary(DictNonEmpty, const CellSlice& root_cs, int _n, bool validate = true)
      : DictionaryFixed(DictNonEmpty(), root_cs, _n, validate) {
  }
  Ref | lookup_ref(td::ConstBitPtr key, int key_len);
  Ref | lookup_delete_ref(td::ConstBitPtr key, int key_len);
  bool set(td::ConstBitPtr key, int key_len, Ref value, SetMode mode = SetMode::Set);
  bool set_ref(td::ConstBitPtr key, int key_len, Ref | val_ref, SetMode mode = SetMode::Set);
  bool set_builder(td::ConstBitPtr key, int key_len, Ref val_b, SetMode mode = SetMode::Set);
  bool set_builder(td::ConstBitPtr key, int key_len, const CellBuilder& val_b, SetMode mode = SetMode::Set);
  bool set_gen(td::ConstBitPtr key, int key_len, const store_value_func_t& store_val, SetMode mode = SetMode::Set);
  Ref lookup_set(td::ConstBitPtr key, int key_len, Ref value, SetMode mode = SetMode::Set);
  Ref | lookup_set_ref(td::ConstBitPtr key, int key_len, Ref | val_ref, SetMode mode = SetMode::Set);
  Ref lookup_set_builder(td::ConstBitPtr key, int key_len, Ref val_b,
                                    SetMode mode = SetMode::Set);
  Ref lookup_set_gen(td::ConstBitPtr key, int key_len, const store_value_func_t& store_val,
                                SetMode mode = SetMode::Set);
  Ref | get_minmax_key_ref(td::BitPtr key_buffer, int key_len, bool fetch_max = false, bool invert_first = false);
  Ref | extract_minmax_key_ref(td::BitPtr key_buffer, int key_len, bool fetch_max = false,
                                   bool invert_first = false);
  void map(const map_func_t& map_func);
  void map(const simple_map_func_t& simple_map_func);
  template 
  Ref | lookup_ref(const T& key) {
    return lookup_ref(key.bits(), key.size());
  }
  template 
  Ref | lookup_delete_ref(const T& key) {
    return lookup_delete_ref(key.bits(), key.size());
  }
  template 
  bool set(const T& key, Ref value, SetMode mode = SetMode::Set) {
    return set(key.bits(), key.size(), std::move(value), mode);
  }
  template 
  bool set_ref(const T& key, Ref | val_ref, SetMode mode = SetMode::Set) {
    return set_ref(key.bits(), key.size(), std::move(val_ref), mode);
  }
  template 
  bool set_builder(const T& key, const CellBuilder& val_b, SetMode mode = SetMode::Set) {
    return set_builder(key.bits(), key.size(), val_b, mode);
  }
  template 
  bool set_builder(const T& key, Ref val_ref, SetMode mode = SetMode::Set) {
    return set_builder(key.bits(), key.size(), std::move(val_ref), mode);
  }
  template 
  Ref lookup_set(const T& key, Ref value, SetMode mode = SetMode::Set) {
    return lookup_set(key.bits(), key.size(), std::move(value), mode);
  }
  template 
  Ref | lookup_set_ref(const T& key, Ref | val_ref, SetMode mode = SetMode::Set) {
    return lookup_set_ref(key.bits(), key.size(), std::move(val_ref), mode);
  }
  template 
  Ref lookup_set_builder(const T& key, const CellBuilder& val_b, SetMode mode = SetMode::Set) {
    return lookup_set_builder(key.bits(), key.size(), val_b, mode);
  }
  template 
  Ref lookup_set_builder(const T& key, Ref val_ref, SetMode mode = SetMode::Set) {
    return lookup_set_builder(key.bits(), key.size(), std::move(val_ref), mode);
  }
  auto range(bool rev = false, bool sgnd = false) {
    return dict_range(*this, rev, sgnd);
  }
 private:
  bool check_fork(CellSlice& cs, Ref | c1, Ref | c2, int n) const override {
    return cs.empty_ext();
  }
  static Ref | extract_value_ref(Ref cs);
  std::pair [, int> dict_filter(Ref]| dict, td::BitPtr key, int n, const filter_func_t& check_leaf) const;
};
class PrefixDictionary final : public DictionaryBase {
 public:
  PrefixDictionary(int _n, bool validate = true) : DictionaryBase(_n, validate) {
  }
  PrefixDictionary(Ref _root, int _n, bool validate = true)
      : DictionaryBase(std::move(_root), _n, validate) {
  }
  PrefixDictionary(Ref | cell, int _n, bool validate = true) : DictionaryBase(std::move(cell), _n, validate) {
  }
  Ref lookup(td::ConstBitPtr key, int key_len);
  std::pair [, int> lookup_prefix(td::ConstBitPtr key, int key_len);
  Ref lookup_delete(td::ConstBitPtr key, int key_len);
  bool set(td::ConstBitPtr key, int key_len, Ref value, SetMode mode = SetMode::Set);
  bool set_builder(td::ConstBitPtr key, int key_len, Ref val_b, SetMode mode = SetMode::Set);
  bool set_gen(td::ConstBitPtr key, int key_len, const store_value_func_t& store_val, SetMode mode = SetMode::Set);
};
using dict::AugmentationData;
class AugmentedDictionary final : public DictionaryFixed {
  const AugmentationData& aug;
 public:
  typedef std::function, Ref, td::ConstBitPtr, int)> foreach_extra_func_t;
  // return value of traverse_func: < 0 = error, 0 = skip, 1 = visit only left, 2 = visit only right, 5 = visit right, then left, 6 = visit left, then right
  // for leaf nodes, all >0 values mean accept and return node as the final result, 0 = skip (continue scanning)
  typedef std::function extra, Ref value)>
      traverse_func_t;
  AugmentedDictionary(int _n, const AugmentationData& _aug, bool validate = true);
  AugmentedDictionary(Ref _root, int _n, const AugmentationData& _aug, bool validate = true);
  AugmentedDictionary(Ref]| cell, int _n, const AugmentationData& _aug, bool validate = true);
  AugmentedDictionary(DictNonEmpty, Ref _root, int _n, const AugmentationData& _aug, bool validate = true);
  Ref get_empty_dictionary() const;
  Ref get_root() const;
  Ref extract_root() &&;
  bool append_dict_to_bool(CellBuilder& cb) &&;
  bool append_dict_to_bool(CellBuilder& cb) const &;
  Ref get_root_extra() const;
  Ref lookup(td::ConstBitPtr key, int key_len);
  Ref | lookup_ref(td::ConstBitPtr key, int key_len);
  Ref lookup_with_extra(td::ConstBitPtr key, int key_len);
  std::pair [, Ref> lookup_extra(td::ConstBitPtr key, int key_len);
  std::pair][, Ref> lookup_ref_extra(td::ConstBitPtr key, int key_len);
  Ref lookup_delete(td::ConstBitPtr key, int key_len);
  Ref]| lookup_delete_ref(td::ConstBitPtr key, int key_len);
  Ref lookup_delete_with_extra(td::ConstBitPtr key, int key_len);
  std::pair [, Ref> lookup_delete_extra(td::ConstBitPtr key, int key_len);
  std::pair][, Ref> lookup_delete_ref_extra(td::ConstBitPtr key, int key_len);
  bool set(td::ConstBitPtr key, int key_len, const CellSlice& value, SetMode mode = SetMode::Set);
  bool set(td::ConstBitPtr key, int key_len, Ref value, SetMode mode = SetMode::Set);
  bool set_ref(td::ConstBitPtr key, int key_len, Ref]| val_ref, SetMode mode = SetMode::Set);
  bool set_builder(td::ConstBitPtr key, int key_len, const CellBuilder& value, SetMode mode = SetMode::Set);
  bool check_for_each_extra(const foreach_extra_func_t& foreach_extra_func, bool invert_first = false);
  std::pair [, Ref> traverse_extra(td::BitPtr key_buffer, int key_len,
                                                           const traverse_func_t& traverse_node);
  bool validate_check_extra(const foreach_extra_func_t& foreach_extra_func, bool invert_first = false);
  bool validate() override;
  template 
  Ref lookup(const T& key) {
    return lookup(key.bits(), key.size());
  }
  template 
  Ref]| lookup_ref(const T& key) {
    return lookup_ref(key.bits(), key.size());
  }
  template 
  bool set(const T& key, Ref val_ref, SetMode mode = SetMode::Set) {
    return set(key.bits(), key.size(), std::move(val_ref), mode);
  }
  template 
  bool set(const T& key, const CellSlice& value, SetMode mode = SetMode::Set) {
    return set(key.bits(), key.size(), value, mode);
  }
  template 
  bool set_ref(const T& key, Ref | val_ref, SetMode mode = SetMode::Set) {
    return set_ref(key.bits(), key.size(), std::move(val_ref), mode);
  }
  template 
  bool set_builder(const T& key, const CellBuilder& val_b, SetMode mode = SetMode::Set) {
    return set_builder(key.bits(), key.size(), val_b, mode);
  }
  template 
  Ref lookup_delete(const T& key) {
    return lookup_delete(key.bits(), key.size());
  }
  template 
  Ref | lookup_delete_ref(const T& key) {
    return lookup_delete_ref(key.bits(), key.size());
  }
  auto range(bool rev = false, bool sgnd = false) {
    return dict_range(*this, rev, sgnd);
  }
  Ref extract_value(Ref value_extra) const;
  Ref | extract_value_ref(Ref value_extra) const;
  std::pair [, Ref> decompose_value_extra(Ref value_extra) const;
  std::pair][, Ref> decompose_value_ref_extra(Ref value_extra) const;
 private:
  bool compute_root() const;
  Ref get_node_extra(Ref]| cell_ref, int n) const;
  Ref extract_leaf_value(Ref leaf) const override;
  bool check_leaf(CellSlice& cs, td::ConstBitPtr key, int key_len) const override;
  bool check_fork(CellSlice& cs, Ref | c1, Ref | c2, int n) const override;
  Ref | finish_create_leaf(CellBuilder& cb, const CellSlice& value) const override;
  Ref | finish_create_fork(CellBuilder& cb, Ref | c1, Ref | c2, int n) const override;
  std::pair [, bool> dict_set(Ref]| dict, td::ConstBitPtr key, int n, const CellSlice& value,
                                      SetMode mode = SetMode::Set) const;
  int label_mode() const override {
    return dict::LabelParser::chk_size;
  }
  std::pair [, Ref> dict_traverse_extra(Ref]| dict, td::BitPtr key_buffer, int n,
                                                                const traverse_func_t& traverse_node) const;
};
}  // namespace vm |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |