def get_prefix_nodes_info(root_node) -> None:
    """
    Calculate number of nodes which were allocated without regarding the allocation policy. Final result will be printed
    to the standard output
    :param root_node: pointer to the root node in trie structure, which represents address sapce
    :return: None. Just print final result
    """
    helper = Helper()
    if not root_node:
        return

    node_path = list()
    node_path.append(root_node)
    prefixes_test = list()

    while node_path:

        node = node_path.pop()

        if node.prefix_flag:
            prefixes_test.append(node)

        if node.right_child:
            node_path.append(node.right_child)

        if node.left_child:
            node_path.append(node.left_child)

    incorrect_nodes = {0: 0, 1: 0, 2: 0, 3: 0, 4: 0}

    for prefix_node in prefixes_test:

        full_prefix_path = AbstractTrie.get_just_prefix_path(prefix_node)[1:]

        org_level = helper.get_organisation_level_by_depth(prefix_node.depth)

        if len(full_prefix_path) == 0:
            incorrect_nodes[org_level] += 1
            continue

        if org_level != 4:
            converted_to_org_level = [
                helper.get_organisation_level_by_depth(n.depth)
                for n in full_prefix_path if org_level -
                helper.get_organisation_level_by_depth(n.depth) == 1
            ]
        else:
            converted_to_org_level = [
                helper.get_organisation_level_by_depth(n.depth)
                for n in full_prefix_path
                if org_level - helper.get_organisation_level_by_depth(n.depth)
                == 1 or org_level -
                helper.get_organisation_level_by_depth(n.depth == 2)
            ]

        if not converted_to_org_level:
            incorrect_nodes[org_level] += 1

    print(f"Number of nodes were allocated incorrectly: {incorrect_nodes}")
    def start_generating(self) -> List[str]:
        """Start generating process.

        :return: list with generated prefixes
        """

        # Generate new RIR nodes and add them to binary trie
        if Helper.distribution_random_plan:
            if self.stats:
                print(
                    "[RANDOM GENERATING]: Start generating prefixes randomly")

            Randomizer = RandomGenerator(
                self._binary_trie,
                self.Help,
                distribution_plan=Helper.distribution_random_plan,
                stats=self.stats)
            Randomizer.random_generate()

            if self.stats:
                print(
                    "[RANDOM GENERATING]: Random generating phase successfully done"
                )
        else:
            if self.stats:
                print(
                    "[RANDOM GENERATING]: Random generating phase is skipped. Only RIR prefixes that have "
                    "a length in interval 12  - 31 could be generated randomly"
                )

        if self.stats:
            print(
                "[TRIE TRAVERSING GENERATING]: Start generating prefixes using constructed trie"
            )

        self._binary_trie.generate_prefixes()

        if self.stats:
            print(
                "[TRIE TRAVERSING GENERATING]: Traversing trie generating phase successfully done"
            )

        new_prefixes = set(
            AbstractTrie.get_prefix_nodes(self._binary_trie.root_node))

        output_converter = Converter(new_prefixes)
        converted_prefixes = output_converter.convert_prefixes()

        return converted_prefixes
    def start_generating(self):

        if self.rgr != 1:
            self._binary_trie.trie_traversal("generate")

        # second phase of generating - random generating
        if self.rgr != 0:

            # Generate prefixes that couldn't be added to trie by generating process
            self._random_generate(self.Help.distribution_random_plan)

            if self.Help.distribution_plan:
                self._random_generate(self.Help.distribution_plan, True)

        new_prefixes = set(AbstractTrie.get_prefix_nodes(self._binary_trie.root_node))

        output_converter = Converter(new_prefixes)
        converted_prefixes = output_converter.convert_prefixes()

        return converted_prefixes
def create_stats(output_prefixes: List[str], name: str, root_node) -> None:
    """
    This method used for creating statistic information about dataset and run the same tests as __main__
    :param output_prefixes: prefix dataset which will be tested
    :param name: path to the output folder which will be used for saving graphs
    :param root_node: pointer to the root node
    :return: None
    """
    depth_distribution = {key: 0 for key in range(65)}

    # get_prefix_nodes_info(root_node)

    for single_prefix in output_prefixes:
        prefix_len = int(single_prefix.split('/')[1])
        depth_distribution[prefix_len] += 1

    depth_distribution_graph(depth_distribution, name)

    level_distribution_stats = AbstractTrie.level_stats(root_node)
    level_distribution(level_distribution_stats, name)
    def add_node(self,
                 node_value: str,
                 parent_node: Optional[Node] = None,
                 creating_phase: bool = True) -> Node:
        """Add new node to binary trie.

        :raises  PrefixAlreadyExists in case if new node already exists in binary trie. Method isn't called in
                    creating phase
        :raises  MaximumLevelException in case if after adding a new node to binary trie level changes and greater than max possible value

        :param node_value: string; string representation of node
        :param parent_node None or Node; node object which represent the parent for added node
        :param creating_phase boolean; signalize phase of generator when node is added while binary trie is initializing

        :return: constructed node object
        """
        if not parent_node:
            current_node = self.root_node
        else:
            current_node = parent_node

        if not creating_phase and AbstractTrie.is_exist(
                current_node, node_value):
            raise PrefixAlreadyExists

        for curr_len, bit in enumerate(node_value, 1):

            if bit == '0':

                if not current_node.left_child:
                    current_node.left_child = Node(bit, current_node.depth + 1)
                    current_node.left_child.path = current_node

                current_node = current_node.left_child

            else:

                if not current_node.right_child:
                    current_node.right_child = Node(bit,
                                                    current_node.depth + 1)
                    current_node.right_child.path = current_node

                current_node = current_node.right_child

        try:
            self.calculate_level(current_node)

        except MaximumLevelException:

            if not current_node.right_child and not current_node.left_child:
                AbstractTrie.delete_node_from_trie(current_node)

            raise

        current_node.prefix_flag = True
        self._prefix_nodes[current_node.depth] += 1

        if not creating_phase:
            current_node.allow_generate = False

        if current_node.depth > self._trie_depth:
            self._trie_depth = current_node.depth

        return current_node
    plt.xlabel('Počet adres')
    plt.ylabel('Spotřebovaný čas [s]')

    plt.legend()
    plt.savefig(f"statistics/time.png", format='png', dpi=850)


if __name__ == '__main__':
    prefix_sets = ['dataset2007', 'dataset2019']

    for current_set in prefix_sets:
        path = f"formated_datasets/{current_set}"

        prefixes = InputArgumentsValidator.read_seed_file(path)
        print(
            f"Number of uniq prefixes which could be generated or used for generating process {len(prefixes)}"
        )

        binary_trie = Trie()
        depth_distribution_stats = {key: 0 for key in range(65)}

        for prefix in prefixes:
            depth_distribution_stats[len(prefix)] += 1
            binary_trie.add_node(prefix)

        depth_distribution_graph(depth_distribution_stats, current_set)

        level_distribution_stats = AbstractTrie.level_stats(
            binary_trie.root_node)
        level_distribution(level_distribution_stats, current_set)