docs: def fit(x, y, t, randomized, max_tree_nodes, min_samples_leaf, min_samples_split, class_majority, measure, accuracy,
          separate_max):
      """
      Function builds a binary decision tree with given dataset and it expand tree nodes in priority order. Tree model is
      stored in a dictionary and it has following structure:
      {parent_identifier: [(child_identifier, highest_estimated_feature_index , split_value, distribution_of_labels,
      depth, feature_type)]}
  
      x: numpy array - dataset with features
      y: numpy array - dataset with labels
      t: list - features types
      randomized: boolean - if True, algorithm estimates sqrt(num_of_features)+1 randomly selected features each
      iteration.
      If False, it estimates all features in each iteration.
      max_tree_nodes: integer - number of tree nodes to expand.
      min_samples_leaf: float - minimal number of samples in leafs.
      class_majority: float - purity of the classes in leafs.
      measure: measure function - information gain or mdl.
      split_fun: split function - discretization of continuous features can be made randomly or with equal label frequency.
      """
  
      depth = 0  # depth of the tree
      node_id = 1  # node identifier
  
      # conditions of continuous and discrete features.
      operation = {"c": (np.less_equal, np.greater), "d": (np.in1d, np.in1d)}
      tree = {0: [(node_id, -1, "", dict(Counter(y)), depth, "")]}  # initialize tree model
      mapping = range(len(x[0]))  # global features indices
  
      # sqrt(num_of_features) is estimated if randomized == True.
      # If randomized == False, all indices are estimated at each node.
      rand_attr = int(np.ceil(np.sqrt(len(x[0]))))
      recalculate = True
      while recalculate:
          recalculate = False
          est_indices = rand_indices(x, rand_attr) if randomized else range(len(x[0]))
          # estimate indices with given measure
          est = [measure(x[:, i], y, t[i], accuracy, separate_max) for i in est_indices]
          try:
              max_est, split = max(est)  # find highest estimated split
          except:
              recalculate = True
  
      best = est_indices[est.index((max_est, split))]  # select feature index with highest estimate
  
      queue = Queue.PriorityQueue()  # initialize priority queue
      # put datasets in the queue
      queue.put((max_est, (node_id, x, y, mapping, best, split, depth)))
  
      while not queue.empty() and len(tree) * 2 < max_tree_nodes:
          _, (parent_id, x, y, mapping, best, split, depth) = queue.get()
  
          # features indices are mapped due to constantly changing subsets of data
          best_map = mapping[best]
  
          for j in range(2):  # for left and right branch of the tree
              selection = range(len(x[0]))  # select all indices for the new subset
              new_mapping = [i for i in mapping]  # create a mapping of indices for a new subset
  
              if t[best_map] == "d" and len(split[j]) == 1:
                  # if feature is discrete with one value in split, we cannot split it further.
                  selection.remove(best)  # remove feature from new dataset
                  new_mapping.remove(best_map)  # remove mapping of feature
  
              # select rows of new dataset that satisfy condition (less than, greater than or in)
              indices = operation[t[best_map]][j](x[:, best], split[j]).nonzero()[0]
              # create new subsets of data
              sub_x, sub_y = x[indices.reshape(len(indices), 1), selection], y[indices]
              if j == 0 and (len(sub_y) < min_samples_leaf or len(x) - len(sub_y) < min_samples_leaf):
                  break
  
              node_id += 1  # increase node identifier
              y_dist = Counter(sub_y)  # distribution of labels in the new node
  
              # connect child node with its parent and update tree model
              tree[parent_id] = tree.get(parent_id, []) + [(node_id, best_map,
                                                            set(split[j]) if t[best_map] == "d" else split[j],
                                                            dict(y_dist), depth + 1, t[best_map])]
  
              # select new indices for estimation
              est_indices = rand_indices(sub_x, rand_attr) if randomized and len(sub_x[0]) > rand_attr else range(
                  len(sub_x[0]))
              # check label majority
              current_majority = y_dist[max(y_dist, key=y_dist.get)] / float(len(sub_y))
  
              # if new node satisfies following conditions it can be further split
              if current_majority < class_majority and len(sub_y) > min_samples_split and est_indices != -1:
                  # estimate selected indices
                  est = [measure(sub_x[:, i], sub_y, t[new_mapping[i]], accuracy, separate_max) for i in est_indices]
                  try:
                      max_est, new_split = max(est)  # find highest estimated split
                  except:
                      continue
                  # select feature index with highest estimate
                  new_best = est_indices[est.index((max_est, new_split))]
                  # put new datasets in the queue with inverse value of estimate (priority order)
                  queue.put(
                      (max_est * -1 * len(sub_y), (node_id, sub_x, sub_y, new_mapping, new_best, new_split, depth + 1)))
      return tree