# Copyright Redis Ltd. 2021 - present
# Licensed under your choice of the Redis Source Available License 2.0 (RSALv2) or
# the Server Side Public License v1 (SSPLv1).

import matplotlib.pyplot as plt
import numpy as np

from sklearn.tree import DecisionTreeClassifier
from sklearn import tree
'''------------------- batches HNSW -----------------'''


def print_tree(clf):
    tree.plot_tree(clf)
    plt.savefig('batches_vs_adhoc-HNSW.pdf')
    plt.show()
    n_nodes = clf.tree_.node_count
    children_left = clf.tree_.children_left
    children_right = clf.tree_.children_right
    feature = clf.tree_.feature
    threshold = clf.tree_.threshold

    node_depth = np.zeros(shape=n_nodes, dtype=np.int64)
    is_leaves = np.zeros(shape=n_nodes, dtype=bool)
    stack = [(0, 0)]  # start with the root node id (0) and its depth (0)
    while len(stack) > 0:
        # `pop` ensures each node is only visited once
        node_id, depth = stack.pop()
        node_depth[node_id] = depth

        # If the left and right child of a node is not the same we have a split
        # node
        is_split_node = children_left[node_id] != children_right[node_id]
        # If a split node, append left and right children and depth to `stack`
        # so we can loop through them
        if is_split_node:
            stack.append((children_left[node_id], depth + 1))
            stack.append((children_right[node_id], depth + 1))
        else:
            is_leaves[node_id] = True

    print("The binary tree structure has {n} nodes and has the following tree structure:\n".format(n=n_nodes))
    for i in range(n_nodes):
        if is_leaves[i]:
            print("{space}node={node} is a leaf node.".format(space=node_depth[i] * "\t", node=i))
        else:
            print("{space}node={node} is a split node: go to node {left} if X[:, {feature}] <= {threshold} "
                  "else to node {right}.".format(space=node_depth[i] * "\t", node=i, left=children_left[i],
                                                 feature=feature[i], threshold=threshold[i], right=children_right[i]))


if __name__ == '__main__':
    clf = DecisionTreeClassifier(max_leaf_nodes=20)

    # features: (k, index_size, dim, M, r)
    # labels: 1: adhoc better, -1: batches better
    y = []

    # The following labels represent the results for experiments that were held for the following combinations:
    # k = 5, 50, 500
    # index_sizes: 1000, 10000, 100000, 1M, 10M
    # dim: 10, 100, 1000 (except for size=1M or 10M with dim==100,1000)
    # M: 4, 16, 64
    # ratio: 0.02-0.2 with steps of 0.02
    X_1 = np.array([[5*k, 10**i, 10**j, 4**m, 2*l/100] for k in range(1, 4) for i in range(3, 8)
                    for j in range(1, 4) for m in range(1, 4) for l in range(1, 21)
                    if i+j <= 8 and not (i == 6 and j == 2)])
    # (5, 1000, 10, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # (5, 1000, 100, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # (5, 1000, 1000, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # (5, 10000, 10, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    # (5, 10000, 100, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1])
    # (5, 10000, 1000, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # (5, 100k, 10, [4,16,64], [0.02-0.2])
    y.extend([1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    # (5, 100k, 100, [4,16,64], [0.02-0.2])
    y.extend([1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    # (5, 100k, 1000, [4,16,64], [0.02-0.2])
    y.extend([1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    # (5, 1M, 10, [4,16,64], [0.02-0.2])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    # (5, 10M, 10, [4,16,64], [0.02-0.2])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    # (50, 1000, 10, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # (50, 1000, 100, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # (50, 1000, 1000, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # (50, 10000, 10, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    # (50, 10000, 100, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # (50, 10000, 1000, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # (50, 100k, 10, [4,16,64], [0.02-0.2])
    y.extend([1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    # (50, 100k, 100, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    # (50, 100k, 1000, [4,16,64], [0.02-0.2])
    y.extend([1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1])
    # (50, 1M, 10, [4,16,64], [0.02-0.2])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    # (50, 10M, 10, [4,16,64], [0.02-0.2])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    # (500, 1000, 10, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # (500, 1000, 100, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # (500, 1000, 1000, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # (500, 10000, 10, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1])
    # (500, 10000, 100, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # (500, 10000, 1000, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # (500, 100k, 10, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    # (500, 100k, 100, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # (500, 100k, 1000, [4,16,64], [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # (500, 1M, 10, [4,16,64], [0.02-0.2])
    y.extend([1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    # (500, 10M, 10, [4,16,64], [0.02-0.2])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1])

    # The following labels represent the results for experiments that were held for the following combinations:
    # k = 5, 50, 500
    # index_sizes: 1M
    # dim: 100
    # M: 50
    # ratio: 0.02-0.2 with steps of 0.02
    X_2 = np.array([[5*k, 1e6, 100, 50, 2*l/100] for k in range(1, 4) for l in range(1, 11)])
    # (5, 1M, 100, 50, [0.02-0.2])
    y.extend([1, 1, -1, -1, -1, -1, -1, -1, -1, -1])
    # (50, 1M, 100, 50, [0.02-0.2])
    y.extend([1, 1, 1, -1, -1, -1, -1, -1, -1, -1])
    # (500, 1M, 100, 50, [0.02-0.2])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, -1, -1])

    # The following labels represent the results for experiments that were held for the following combinations:
    # k = 5, 50, 500
    # index_sizes: 50000, 500000, 5M
    # dim: 5, 50, 500 (except for size=5M with dim=500)
    # M: 24, 48
    # ratio: 0.05-0.5 with steps of 0.05
    X_3 = np.array([[5*k, 5*10**i, 5*10**j, m, 5*l/100] for i in range(4, 7) for j in range(3)
                    for m in [24, 48] for k in range(1, 4) for l in range(1, 11)
                    if i+j < 8])
    # ([5,50,500], 50k, 5, 24, [0.05-0.5])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, -1, -1, -1, -1, -1, -1, -1])
    # ([5,50,500], 50k, 5, 48, [0.05-0.5])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, -1, -1, -1, -1, -1, -1, -1])
    # ([5,50,500], 50k, 50, 24, [0.05-0.5])
    y.extend([1, 1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, -1, -1])
    # ([5,50,500], 50k, 50, 48, [0.05-0.5])
    y.extend([1, 1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, -1])
    # ([5,50,500], 50k, 500, 24, [0.05-0.5])
    y.extend([1, 1, 1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # ([5,50,500], 50k, 50, 48, [0.05-0.5])
    y.extend([1, 1, 1, 1, 1, 1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, -1])
    # ([5,50,500], 0.5M, 5, 24, [0.05-0.5])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    # ([5,50,500], 0.5M, 5, 48, [0.05-0.5])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    # ([5,50,500], 0.5M, 50, 24, [0.05-0.5])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, -1, -1, -1, -1, -1, -1, -1])
    # ([5,50,500], 0.5M, 50, 48, [0.05-0.5])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, -1, -1, -1, -1, -1, -1, -1])
    # ([5,50,500], 0.5M, 500, 24, [0.05-0.5])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, -1, -1, -1, -1, -1, -1, -1])
    # ([5,50,500], 0.5M, 500, 48, [0.05-0.5])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, -1, -1, -1, -1, -1, -1])
    # ([5,50,500], 5M, 5, 24, [0.05-0.5])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    # ([5,50,500], 5M, 5, 48, [0.05-0.5])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    # ([5,50,500], 5M, 50, 24, [0.05-0.5])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    # ([5,50,500], 5M, 50, 48, [0.05-0.5])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([-1, -1, -1, -1, -1, -1, -1, -1, -1, -1])

    # The following labels represent the results for experiments that were held for the following combinations:
    # k = 5, 50, 500
    # index_sizes: 500000, 5M
    # dim: 5, 50, 500 (except for size=5M with dim=500)
    # M: 24, 48
    # ratio: 0.002-0.02 with steps of 0.002
    X_4 = np.array([[5*k, 5*10**i, 5*10**j, m, 2*l/1000] for i in range(5, 7) for j in range(3)
                    for m in [24, 48] for k in range(1, 4) for l in range(1, 11)
                    if i+j < 8])
    # ([5,50,500], 0.5M, 5, 24, [0.002-0.02])
    y.extend([1, 1, 1, 1, 1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # ([5,50,500], 0.5M, 5, 48, [0.002-0.02])
    y.extend([1, 1, 1, 1, 1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # ([5,50,500], 0.5M, 50, 24,[0.002-0.02])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # ([5,50,500], 0.5M, 50, 48, [0.002-0.02])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # ([5,50,500], 0.5M, 500, 24, [0.002-0.02])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # ([5,50,500], 0.5M, 500, 48, [0.002-0.02])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # ([5,50,500], 5M, 5, 24, [0.002-0.02])
    y.extend([1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # ([5,50,500], 5M, 5, 48, [0.002-0.02])
    y.extend([1, -1, -1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # ([5,50,500], 5M, 50, 24, [0.002-0.02])
    y.extend([1, 1, 1, -1, -1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    # ([5,50,500], 5M, 50, 48, [0.002-0.02])
    y.extend([1, 1, 1, 1, 1, -1, -1, -1, -1, -1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])
    y.extend([1, 1, 1, 1, 1, 1, 1, 1, 1, 1])

    # The total train set is the concatenation of the 4 combinations sets above.
    X = np.concatenate((X_1, X_2, X_3, X_4))
    clf = clf.fit(X, y)

    print_tree(clf)
    # (k, index_size, dim, M, r)
    print(clf.predict([[10, 800000, 100, 50, 0.0001]]))
