#!/usr/bin/env python3
# 
# This script can re-construct a single result file - an HTML file from a TXT file 
# (including older BETA versions of v3.3.0-BETA1 release).
# It can also convert v3.0.0 --> v3.3.0 result.
#
import sys
import logging
import re
import math
import argparse
from typing import Optional
from enum import Enum, IntEnum
import os

# THIS CODE IS AUTOGENERATED
def py_print(file, result):
    file.write('Evolution Benchmark report\n\nVersion            - ')
    file.write(f'{result["system_info"]["version_info"]:}')
    file.write('\nDate               - ')
    file.write(f'{result["timestamp"]:}')
    file.write('\nCompiler           - ')
    file.write(f'{result["system_info"]["compiler_info"]:}')
    file.write('\nLogical cores      - ')
    file.write(f'{result["system_info"]["cpu_core_count"]:}')
    file.write('\nCPU name           - ')
    file.write(f'{result["system_info"]["cpu_name"]:}')
    file.write('\nComment            - ')
    file.write(f'{result["comment"]:}')
    file.write('\n\nOVERALL RESULT     - ')
    file.write(f'{result["full_result"][0]["score"]:.{(1 > result["full_result"][0]["score"]) + (10 > result["full_result"][0]["score"]) + (100 > result["full_result"][0]["score"])}f}')
    file.write('\n\n')
    if result["full_result"][0]["detailed"][1]["valid"]:
        file.write('Single core        - ')
        file.write(f'{result["full_result"][0]["detailed"][1]["score"]:.{(1 > result["full_result"][0]["detailed"][1]["score"]) + (10 > result["full_result"][0]["detailed"][1]["score"]) + (100 > result["full_result"][0]["detailed"][1]["score"])}f}')
        file.write('\n')
    if result["full_result"][0]["detailed"][2]["valid"]:
        file.write('Multi core         - ')
        file.write(f'{result["full_result"][0]["detailed"][2]["score"]:.{(1 > result["full_result"][0]["detailed"][2]["score"]) + (10 > result["full_result"][0]["detailed"][2]["score"]) + (100 > result["full_result"][0]["detailed"][2]["score"])}f}')
        file.write(' (MP ratio x')
        file.write(f'{result["full_result"][0]["detailed"][2]["ratio"]:.{1}f}')
        file.write(')\n')
    if result["full_result"][0]["detailed"][3]["valid"]:
        file.write('Quad core          - ')
        file.write(f'{result["full_result"][0]["detailed"][3]["score"]:.{(1 > result["full_result"][0]["detailed"][3]["score"]) + (10 > result["full_result"][0]["detailed"][3]["score"]) + (100 > result["full_result"][0]["detailed"][3]["score"])}f}')
        file.write(' (MP ratio x')
        file.write(f'{result["full_result"][0]["detailed"][3]["ratio"]:.{1}f}')
        file.write(')\n')
    if result["full_result"][0]["detailed"][4]["valid"]:
        file.write('Workstation        - ')
        file.write(f'{result["full_result"][0]["detailed"][4]["score"]:.{(1 > result["full_result"][0]["detailed"][4]["score"]) + (10 > result["full_result"][0]["detailed"][4]["score"]) + (100 > result["full_result"][0]["detailed"][4]["score"])}f}')
        file.write(' (MP ratio x')
        file.write(f'{result["full_result"][0]["detailed"][4]["ratio"]:.{1}f}')
        file.write(')\n')
    if result["full_result"][0]:
        file.write('\n+---------------+')
        if result["full_result"][0]["detailed"][0]["valid"]:
            file.write('-------------+')
        if result["full_result"][0]["detailed"][1]["valid"]:
            file.write('-------------+')
        if result["full_result"][0]["detailed"][2]["valid"]:
            file.write('-------------+')
        if result["full_result"][0]["detailed"][3]["valid"]:
            file.write('-------------+')
        if result["full_result"][0]["detailed"][4]["valid"]:
            file.write('-------------+')
        file.write('\n| Non optimized |')
        if result["full_result"][0]["detailed"][0]["valid"]:
            file.write('    Manual   |')
        if result["full_result"][0]["detailed"][1]["valid"]:
            file.write(' Single core |')
        if result["full_result"][0]["detailed"][2]["valid"]:
            file.write('  Multi core |')
        if result["full_result"][0]["detailed"][3]["valid"]:
            file.write('  Quad core  |')
        if result["full_result"][0]["detailed"][4]["valid"]:
            file.write(' Workstation |')
        file.write('\n+---------------+')
        if result["full_result"][0]["detailed"][0]["valid"]:
            file.write('-------------+')
        if result["full_result"][0]["detailed"][1]["valid"]:
            file.write('-------------+')
        if result["full_result"][0]["detailed"][2]["valid"]:
            file.write('-------------+')
        if result["full_result"][0]["detailed"][3]["valid"]:
            file.write('-------------+')
        if result["full_result"][0]["detailed"][4]["valid"]:
            file.write('-------------+')
        file.write('\n| Maximum       |')
        for i in range(0, 5):
            if result["full_result"][0]["detailed"][i]["valid"]:
                file.write(' ')
                file.write(f'{result["full_result"][0]["detailed"][i]["maximum"]:11.{(1 > result["full_result"][0]["detailed"][i]["maximum"]) + (10 > result["full_result"][0]["detailed"][i]["maximum"]) + (100 > result["full_result"][0]["detailed"][i]["maximum"])}f}')
                file.write(' |')
        file.write('\n| Average       |')
        for i in range(0, 5):
            if result["full_result"][0]["detailed"][i]["valid"]:
                file.write(' ')
                file.write(f'{result["full_result"][0]["detailed"][i]["average"]:11.{(1 > result["full_result"][0]["detailed"][i]["average"]) + (10 > result["full_result"][0]["detailed"][i]["average"]) + (100 > result["full_result"][0]["detailed"][i]["average"])}f}')
                file.write(' |')
        file.write('\n| Geometric     |')
        for i in range(0, 5):
            if result["full_result"][0]["detailed"][i]["valid"]:
                file.write(' ')
                file.write(f'{result["full_result"][0]["detailed"][i]["geometric"]:11.{(1 > result["full_result"][0]["detailed"][i]["geometric"]) + (10 > result["full_result"][0]["detailed"][i]["geometric"]) + (100 > result["full_result"][0]["detailed"][i]["geometric"])}f}')
                file.write(' |')
        file.write('\n| Harmonic      |')
        for i in range(0, 5):
            if result["full_result"][0]["detailed"][i]["valid"]:
                file.write(' ')
                file.write(f'{result["full_result"][0]["detailed"][i]["harmonic"]:11.{(1 > result["full_result"][0]["detailed"][i]["harmonic"]) + (10 > result["full_result"][0]["detailed"][i]["harmonic"]) + (100 > result["full_result"][0]["detailed"][i]["harmonic"])}f}')
                file.write(' |')
        file.write('\n| Minimum       |')
        for i in range(0, 5):
            if result["full_result"][0]["detailed"][i]["valid"]:
                file.write(' ')
                file.write(f'{result["full_result"][0]["detailed"][i]["minimum"]:11.{(1 > result["full_result"][0]["detailed"][i]["minimum"]) + (10 > result["full_result"][0]["detailed"][i]["minimum"]) + (100 > result["full_result"][0]["detailed"][i]["minimum"])}f}')
                file.write(' |')
        file.write('\n+---------------+')
        if result["full_result"][0]["detailed"][0]["valid"]:
            file.write('-------------+')
        if result["full_result"][0]["detailed"][1]["valid"]:
            file.write('-------------+')
        if result["full_result"][0]["detailed"][2]["valid"]:
            file.write('-------------+')
        if result["full_result"][0]["detailed"][3]["valid"]:
            file.write('-------------+')
        if result["full_result"][0]["detailed"][4]["valid"]:
            file.write('-------------+')
        file.write('\n')
        for i in range(0, 24):
            file.write('| ')
            file.write(f'{i + 1:<13}')
            file.write(' |')
            for j in range(0, 5):
                if result["full_result"][0]["detailed"][j]["valid"]:
                    file.write(' ')
                    file.write(f'{result["full_result"][0]["detailed"][j]["kernels"][i]:11.{(1 > result["full_result"][0]["detailed"][j]["kernels"][i]) + (10 > result["full_result"][0]["detailed"][j]["kernels"][i]) + (100 > result["full_result"][0]["detailed"][j]["kernels"][i])}f}')
                    file.write(' |')
            file.write('\n')
        file.write('+---------------+')
        if result["full_result"][0]["detailed"][0]["valid"]:
            file.write('-------------+')
        if result["full_result"][0]["detailed"][1]["valid"]:
            file.write('-------------+')
        if result["full_result"][0]["detailed"][2]["valid"]:
            file.write('-------------+')
        if result["full_result"][0]["detailed"][3]["valid"]:
            file.write('-------------+')
        if result["full_result"][0]["detailed"][4]["valid"]:
            file.write('-------------+')
        file.write('\n')
        if result["full_result"][1]:
            file.write('\nOptimized score - ')
            file.write(f'{result["full_result"][1]["score"]:.{(1 > result["full_result"][1]["score"]) + (10 > result["full_result"][1]["score"]) + (100 > result["full_result"][1]["score"])}f}')
            file.write(' (optimized ratio x')
            file.write(f'{result["full_result"][1]["optimized_ratio"]:.{1}f}')
            file.write(')\n\n')
            if result["full_result"][1]["detailed"][1]["valid"]:
                file.write('Single core        - ')
                file.write(f'{result["full_result"][1]["detailed"][1]["score"]:.{(1 > result["full_result"][1]["detailed"][1]["score"]) + (10 > result["full_result"][1]["detailed"][1]["score"]) + (100 > result["full_result"][1]["detailed"][1]["score"])}f}')
                file.write('\n')
            if result["full_result"][1]["detailed"][2]["valid"]:
                file.write('Multi core         - ')
                file.write(f'{result["full_result"][1]["detailed"][2]["score"]:.{(1 > result["full_result"][1]["detailed"][2]["score"]) + (10 > result["full_result"][1]["detailed"][2]["score"]) + (100 > result["full_result"][1]["detailed"][2]["score"])}f}')
                file.write(' (MP ratio x')
                file.write(f'{result["full_result"][1]["detailed"][2]["ratio"]:.{1}f}')
                file.write(')\n')
            if result["full_result"][1]["detailed"][3]["valid"]:
                file.write('Quad core          - ')
                file.write(f'{result["full_result"][1]["detailed"][3]["score"]:.{(1 > result["full_result"][1]["detailed"][3]["score"]) + (10 > result["full_result"][1]["detailed"][3]["score"]) + (100 > result["full_result"][1]["detailed"][3]["score"])}f}')
                file.write(' (MP ratio x')
                file.write(f'{result["full_result"][1]["detailed"][3]["ratio"]:.{1}f}')
                file.write(')\n')
            if result["full_result"][1]["detailed"][4]["valid"]:
                file.write('Workstation        - ')
                file.write(f'{result["full_result"][1]["detailed"][4]["score"]:.{(1 > result["full_result"][1]["detailed"][4]["score"]) + (10 > result["full_result"][1]["detailed"][4]["score"]) + (100 > result["full_result"][1]["detailed"][4]["score"])}f}')
                file.write(' (MP ratio x')
                file.write(f'{result["full_result"][1]["detailed"][4]["ratio"]:.{1}f}')
                file.write(')\n')
            file.write('\n+---------------+')
            if result["full_result"][1]["detailed"][0]["valid"]:
                file.write('-------------+')
            if result["full_result"][1]["detailed"][1]["valid"]:
                file.write('-------------+')
            if result["full_result"][1]["detailed"][2]["valid"]:
                file.write('-------------+')
            if result["full_result"][1]["detailed"][3]["valid"]:
                file.write('-------------+')
            if result["full_result"][1]["detailed"][4]["valid"]:
                file.write('-------------+')
            file.write('\n| Optimized     |')
            if result["full_result"][1]["detailed"][0]["valid"]:
                file.write('    Manual   |')
            if result["full_result"][1]["detailed"][1]["valid"]:
                file.write(' Single core |')
            if result["full_result"][1]["detailed"][2]["valid"]:
                file.write('  Multi core |')
            if result["full_result"][1]["detailed"][3]["valid"]:
                file.write('  Quad core  |')
            if result["full_result"][1]["detailed"][4]["valid"]:
                file.write(' Workstation |')
            file.write('\n+---------------+')
            if result["full_result"][1]["detailed"][0]["valid"]:
                file.write('-------------+')
            if result["full_result"][1]["detailed"][1]["valid"]:
                file.write('-------------+')
            if result["full_result"][1]["detailed"][2]["valid"]:
                file.write('-------------+')
            if result["full_result"][1]["detailed"][3]["valid"]:
                file.write('-------------+')
            if result["full_result"][1]["detailed"][4]["valid"]:
                file.write('-------------+')
            file.write('\n| Maximum       |')
            for i in range(0, 5):
                if result["full_result"][1]["detailed"][i]["valid"]:
                    file.write(' ')
                    file.write(f'{result["full_result"][1]["detailed"][i]["maximum"]:11.{(1 > result["full_result"][1]["detailed"][i]["maximum"]) + (10 > result["full_result"][1]["detailed"][i]["maximum"]) + (100 > result["full_result"][1]["detailed"][i]["maximum"])}f}')
                    file.write(' |')
            file.write('\n| Average       |')
            for i in range(0, 5):
                if result["full_result"][1]["detailed"][i]["valid"]:
                    file.write(' ')
                    file.write(f'{result["full_result"][1]["detailed"][i]["average"]:11.{(1 > result["full_result"][1]["detailed"][i]["average"]) + (10 > result["full_result"][1]["detailed"][i]["average"]) + (100 > result["full_result"][1]["detailed"][i]["average"])}f}')
                    file.write(' |')
            file.write('\n| Geometric     |')
            for i in range(0, 5):
                if result["full_result"][1]["detailed"][i]["valid"]:
                    file.write(' ')
                    file.write(f'{result["full_result"][1]["detailed"][i]["geometric"]:11.{(1 > result["full_result"][1]["detailed"][i]["geometric"]) + (10 > result["full_result"][1]["detailed"][i]["geometric"]) + (100 > result["full_result"][1]["detailed"][i]["geometric"])}f}')
                    file.write(' |')
            file.write('\n| Harmonic      |')
            for i in range(0, 5):
                if result["full_result"][1]["detailed"][i]["valid"]:
                    file.write(' ')
                    file.write(f'{result["full_result"][1]["detailed"][i]["harmonic"]:11.{(1 > result["full_result"][1]["detailed"][i]["harmonic"]) + (10 > result["full_result"][1]["detailed"][i]["harmonic"]) + (100 > result["full_result"][1]["detailed"][i]["harmonic"])}f}')
                    file.write(' |')
            file.write('\n| Minimum       |')
            for i in range(0, 5):
                if result["full_result"][1]["detailed"][i]["valid"]:
                    file.write(' ')
                    file.write(f'{result["full_result"][1]["detailed"][i]["minimum"]:11.{(1 > result["full_result"][1]["detailed"][i]["minimum"]) + (10 > result["full_result"][1]["detailed"][i]["minimum"]) + (100 > result["full_result"][1]["detailed"][i]["minimum"])}f}')
                    file.write(' |')
            file.write('\n+---------------+')
            if result["full_result"][1]["detailed"][0]["valid"]:
                file.write('-------------+')
            if result["full_result"][1]["detailed"][1]["valid"]:
                file.write('-------------+')
            if result["full_result"][1]["detailed"][2]["valid"]:
                file.write('-------------+')
            if result["full_result"][1]["detailed"][3]["valid"]:
                file.write('-------------+')
            if result["full_result"][1]["detailed"][4]["valid"]:
                file.write('-------------+')
            file.write('\n')
            for i in range(0, 24):
                file.write('| ')
                file.write(f'{i + 1:<13}')
                file.write(' |')
                for j in range(0, 5):
                    if result["full_result"][1]["detailed"][j]["valid"]:
                        file.write(' ')
                        file.write(f'{result["full_result"][1]["detailed"][j]["kernels"][i]:11.{(1 > result["full_result"][1]["detailed"][j]["kernels"][i]) + (10 > result["full_result"][1]["detailed"][j]["kernels"][i]) + (100 > result["full_result"][1]["detailed"][j]["kernels"][i])}f}')
                        file.write(' |')
                file.write('\n')
            file.write('+---------------+')
            if result["full_result"][1]["detailed"][0]["valid"]:
                file.write('-------------+')
            if result["full_result"][1]["detailed"][1]["valid"]:
                file.write('-------------+')
            if result["full_result"][1]["detailed"][2]["valid"]:
                file.write('-------------+')
            if result["full_result"][1]["detailed"][3]["valid"]:
                file.write('-------------+')
            if result["full_result"][1]["detailed"][4]["valid"]:
                file.write('-------------+')
            file.write('\n')

# THIS CODE IS AUTOGENERATED
def py_print_html(file, result):
    file.write('<html xmlns=\"http://www.w3.org/1999/html\">\n<head>\n\t<title>Waverian Evolution benchmark results</title>\n\t<style>\n        body {\n            font-family: system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, \"Noto Sans\", \"Liberation Sans\", sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\", \"Segoe UI Symbol\", \"Noto Color Emoji\"\n        }\n\n        .container {\n            margin-left: auto;\n            margin-right: auto;\n            min-width: 30em;\n            max-width: 50em;\n            text-align: center;\n        }\n\n        .thin_font {\n            font-weight: 300;\n            margin: auto;\n        }\n\n        .white_on_blue {\n            background-color: #1e90ff;\n            color: #f0f8ff;\n        }\n\n\t\t.white_on_grey {\n\t\t\tbackground-color: #909090;\n\t\t\tcolor: #f0f8ff;\n\t\t}\n\n\t\ttable.blue_border td{\n\t\t\tborder: 1px solid #1e90ff;\n\t\t}\n\n\t\ttable.grey_border td{\n            border: 1px solid #909090;\n        }\n\n        h1 {\n            font-size: 5em;\n        }\n\n        h2 {\n            font-size: 3em;\n        }\n\n        h3 {\n            font-size: 1.8em;\n        }\n\n        h4 {\n            font-size: 1.2em;\n        }\n\n        table {\n            border-collapse: collapse;\n            border-width: 0;\n            width: 100%;\n            text-align: left;\n        }\n\n        p {\n            text-align: left;\n        }\n\n        .logo {\n            text-align: left;\n            padding: 10px;\n        }\n\n        .no_border {\n            border: none;\n        }\n\n        .sub_result_div {\n            text-align: center;\n            padding-bottom: 10px;\n        }\n\t</style>\n</head>\n<body>\n<div class=\"container\">\n\t<div class=\"logo white_on_blue\">\n\t\t<h3 class=\"thin_font\">Waverian Evolution benchmark</h3>\n\t</div>\n\t<table class=\"blue_border\">\n\t\t<thead></thead>\n\t\t<tbody>\n\t\t<tr>\n\t\t\t<td>Version</td>\n\t\t\t<td colspan=\"4\">')
    file.write(f'{result["system_info"]["version_info"]:}')
    file.write('</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>Date</td>\n\t\t\t<td colspan=\"4\">')
    file.write(f'{result["timestamp"]:}')
    file.write('</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>Compiler</td>\n\t\t\t<td colspan=\"4\">')
    file.write(f'{result["system_info"]["compiler_info"]:}')
    file.write('</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>Logical cores</td>\n\t\t\t<td colspan=\"4\">')
    file.write(f'{result["system_info"]["cpu_core_count"]:}')
    file.write('</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>CPU name</td>\n\t\t\t<td colspan=\"4\">')
    file.write(f'{result["system_info"]["cpu_name"]:}')
    file.write('</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>Comment</td>\n\t\t\t<td colspan=\"4\">')
    file.write(f'{result["comment"]:}')
    file.write('</td>\n\t\t</tr>\n\t\t</tbody>\n\t</table>\n\t<div>\n\t\t<h1 class=\"thin_font\">')
    file.write(f'{result["full_result"][0]["score"]:.{(1 > result["full_result"][0]["score"]) + (10 > result["full_result"][0]["score"]) + (100 > result["full_result"][0]["score"])}f}')
    file.write('</h1>\n\t\t<h3 class=\"thin_font\">Overall score</h3>\n\t</div>\n\t<br>\n\t<div>\n\t\t<table>\n\t\t\t<tr>\n\t\t\t\t<td class=\"no_border\">\n\t\t\t\t\t<div class=\"sub_result_div white_on_blue\">\n\t\t\t\t\t\t<h2 class=\"thin_font\">')
    if result["full_result"][0]["detailed"][1]["valid"]:
        file.write(' ')
        file.write(f'{result["full_result"][0]["detailed"][1]["score"]:.{(1 > result["full_result"][0]["detailed"][1]["score"]) + (10 > result["full_result"][0]["detailed"][1]["score"]) + (100 > result["full_result"][0]["detailed"][1]["score"])}f}')
        file.write(' ')
    else:
        file.write(' N/A ')
    file.write('</h2>\n\t\t\t\t\t\t<h4 class=\"thin_font\">-</h4>\n\t\t\t\t\t\t<h3 class=\"thin_font\">Singlecore</h3>\n\t\t\t\t\t</div>\n\t\t\t\t</td>\n\t\t\t\t<td class=\"no_border\">\n\t\t\t\t\t<div class=\"sub_result_div white_on_blue\">\n\t\t\t\t\t\t<h2 class=\"thin_font\">')
    if result["full_result"][0]["detailed"][2]["valid"]:
        file.write(' ')
        file.write(f'{result["full_result"][0]["detailed"][2]["score"]:.{(1 > result["full_result"][0]["detailed"][2]["score"]) + (10 > result["full_result"][0]["detailed"][2]["score"]) + (100 > result["full_result"][0]["detailed"][2]["score"])}f}')
        file.write(' ')
    else:
        file.write(' N/A ')
    file.write('</h2>\n\t\t\t\t\t\t<h4 class=\"thin_font\">')
    if result["full_result"][0]["detailed"][2]["valid"]:
        file.write('MP ratio x')
        file.write(f'{result["full_result"][0]["detailed"][2]["ratio"]:.{1}f}')
    else:
        file.write('-')
    file.write('</h4>\n\t\t\t\t\t\t<h3 class=\"thin_font\">Multicore</h3>\n\t\t\t\t\t</div>\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t\t<tr>\n\t\t\t\t<td class=\"no_border\">\n\n\t\t\t\t\t<div class=\"sub_result_div white_on_blue\">\n\t\t\t\t\t\t<h2 class=\"thin_font\">')
    if result["full_result"][0]["detailed"][3]["valid"]:
        file.write(' ')
        file.write(f'{result["full_result"][0]["detailed"][3]["score"]:.{(1 > result["full_result"][0]["detailed"][3]["score"]) + (10 > result["full_result"][0]["detailed"][3]["score"]) + (100 > result["full_result"][0]["detailed"][3]["score"])}f}')
        file.write(' ')
    else:
        file.write(' N/A ')
    file.write('</h2>\n\t\t\t\t\t\t<h4 class=\"thin_font\">')
    if result["full_result"][0]["detailed"][3]["valid"]:
        file.write('MP ratio x')
        file.write(f'{result["full_result"][0]["detailed"][3]["ratio"]:.{1}f}')
    else:
        file.write('-')
    file.write('</h4>\n\t\t\t\t\t\t<h3 class=\"thin_font\">Quadcore</h3>\n\t\t\t\t\t</div>\n\t\t\t\t</td>\n\t\t\t\t<td class=\"no_border\">\n\t\t\t\t\t<div class=\"sub_result_div white_on_blue\">\n\t\t\t\t\t\t<h2 class=\"thin_font\">')
    if result["full_result"][0]["detailed"][4]["valid"]:
        file.write(' ')
        file.write(f'{result["full_result"][0]["detailed"][4]["score"]:.{(1 > result["full_result"][0]["detailed"][4]["score"]) + (10 > result["full_result"][0]["detailed"][4]["score"]) + (100 > result["full_result"][0]["detailed"][4]["score"])}f}')
        file.write(' ')
    else:
        file.write(' N/A ')
    file.write('</h2>\n\t\t\t\t\t\t<h4 class=\"thin_font\">')
    if result["full_result"][0]["detailed"][4]["valid"]:
        file.write('MP ratio x')
        file.write(f'{result["full_result"][0]["detailed"][4]["ratio"]:.{1}f}')
    else:
        file.write('-')
    file.write('</h4>\n\t\t\t\t\t\t<h3 class=\"thin_font\">Workstation</h3>\n\t\t\t\t\t</div>\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t</table>\n\t</div>\n\t<div>\n\t\t<table class=\"blue_border\">\n\t\t\t<br>\n\t\t\t<tr class=\"white_on_blue\">\n\t\t\t\t<td></td>\n\t\t\t\t<td><b>Non optimized</b></td>\n\t\t\t\t')
    if result["full_result"][0]["detailed"][0]["valid"]:
        file.write('<td>Manual</td>')
    file.write('\n\t\t\t\t')
    if result["full_result"][0]["detailed"][1]["valid"]:
        file.write('<td>Single core</td>')
    file.write('\n\t\t\t\t')
    if result["full_result"][0]["detailed"][2]["valid"]:
        file.write('<td>Multi core</td>')
    file.write('\n\t\t\t\t')
    if result["full_result"][0]["detailed"][3]["valid"]:
        file.write('<td>Quad core</td>')
    file.write('\n\t\t\t\t')
    if result["full_result"][0]["detailed"][4]["valid"]:
        file.write('<td>Workstation</td>')
    file.write('\n\t\t\t</tr>\n\t\t\t<tr>\n\t\t\t\t<td></td>\n\t\t\t\t<td>Maximum</td>\n\t\t\t\t')
    for i in range(0, 5):
        if result["full_result"][0]["detailed"][i]["valid"]:
            file.write('<td>')
            file.write(f'{result["full_result"][0]["detailed"][i]["maximum"]:.{(1 > result["full_result"][0]["detailed"][i]["maximum"]) + (10 > result["full_result"][0]["detailed"][i]["maximum"]) + (100 > result["full_result"][0]["detailed"][i]["maximum"])}f}')
            file.write('</td>')
    file.write('\n\t\t\t</tr>\n\t\t\t<tr>\n\t\t\t\t<td></td>\n\t\t\t\t<td>Average</td>\n\t\t\t\t')
    for i in range(0, 5):
        if result["full_result"][0]["detailed"][i]["valid"]:
            file.write('<td>')
            file.write(f'{result["full_result"][0]["detailed"][i]["average"]:.{(1 > result["full_result"][0]["detailed"][i]["average"]) + (10 > result["full_result"][0]["detailed"][i]["average"]) + (100 > result["full_result"][0]["detailed"][i]["average"])}f}')
            file.write('</td>')
    file.write('\n\t\t\t</tr>\n\t\t\t<tr>\n\t\t\t\t<td></td>\n\t\t\t\t<td>Geometric</td>\n\t\t\t\t')
    for i in range(0, 5):
        if result["full_result"][0]["detailed"][i]["valid"]:
            file.write('<td>')
            file.write(f'{result["full_result"][0]["detailed"][i]["geometric"]:.{(1 > result["full_result"][0]["detailed"][i]["geometric"]) + (10 > result["full_result"][0]["detailed"][i]["geometric"]) + (100 > result["full_result"][0]["detailed"][i]["geometric"])}f}')
            file.write('</td>')
    file.write('\n\t\t\t</tr>\n\t\t\t<tr>\n\t\t\t\t<td></td>\n\t\t\t\t<td>Harmonic</td>\n\t\t\t\t')
    for i in range(0, 5):
        if result["full_result"][0]["detailed"][i]["valid"]:
            file.write('<td>')
            file.write(f'{result["full_result"][0]["detailed"][i]["harmonic"]:.{(1 > result["full_result"][0]["detailed"][i]["harmonic"]) + (10 > result["full_result"][0]["detailed"][i]["harmonic"]) + (100 > result["full_result"][0]["detailed"][i]["harmonic"])}f}')
            file.write('</td>')
    file.write('\n\t\t\t</tr>\n\t\t\t<tr>\n\t\t\t\t<td></td>\n\t\t\t\t<td>Minimum</td>\n\t\t\t    ')
    for i in range(0, 5):
        if result["full_result"][0]["detailed"][i]["valid"]:
            file.write('<td>')
            file.write(f'{result["full_result"][0]["detailed"][i]["minimum"]:.{(1 > result["full_result"][0]["detailed"][i]["minimum"]) + (10 > result["full_result"][0]["detailed"][i]["minimum"]) + (100 > result["full_result"][0]["detailed"][i]["minimum"])}f}')
            file.write('</td>')
    file.write('\n\t\t\t</tr>\n\t\t\t<tr class=\"white_on_blue\">\n\t\t\t\t<td>#</td>\n\t\t\t\t<td>Kernel name</td>\n                ')
    for i in range(0, 5):
        if result["full_result"][0]["detailed"][i]["valid"]:
            file.write('<td></td>')
    file.write('\n\t\t\t</tr>\n\t\t\t')
    for i in range(0, 24):
        file.write('<tr>\n                <td>')
        file.write(f'{i + 1:}')
        file.write('.</td>\n                <td>')
        if i==0:
            file.write('Hydro fragment')
        if i==1:
            file.write('ICCG excerpt (Incomplete Cholesky Conjugate Gradient)')
        if i==2:
            file.write('Inner product')
        if i==3:
            file.write('Banded linear equations')
        if i==4:
            file.write('Tri-diagonal elimination, below diagonal')
        if i==5:
            file.write('General linear recurrence equations')
        if i==6:
            file.write('Equation of state fragment')
        if i==7:
            file.write('ADI integration')
        if i==8:
            file.write('Integrate predictors')
        if i==9:
            file.write('Difference predictors')
        if i==10:
            file.write('First sum')
        if i==11:
            file.write('First difference')
        if i==12:
            file.write('2-D PIC (Particle In Cell)')
        if i==13:
            file.write('1-D PIC (Particle In Cell)')
        if i==14:
            file.write('Casual Fortran.  Development version')
        if i==15:
            file.write('Monte Carlo search loop')
        if i==16:
            file.write('Implicit, conditional computation')
        if i==17:
            file.write('2-D explicit hydrodynamics fragment')
        if i==18:
            file.write('General linear recurrence equations')
        if i==19:
            file.write('Discrete ordinates transport, conditional recurrence on xx')
        if i==20:
            file.write('Matrix*matrix product')
        if i==21:
            file.write('Planckian distribution')
        if i==22:
            file.write('2-D implicit hydrodynamics fragment')
        if i==23:
            file.write('Find location of first minimum in array')
        file.write('</td>\n                ')
        for j in range(0, 5):
            if result["full_result"][0]["detailed"][j]["valid"]:
                file.write('<td>')
                file.write(f'{result["full_result"][0]["detailed"][j]["kernels"][i]:.{(1 > result["full_result"][0]["detailed"][j]["kernels"][i]) + (10 > result["full_result"][0]["detailed"][j]["kernels"][i]) + (100 > result["full_result"][0]["detailed"][j]["kernels"][i])}f}')
                file.write('</td>')
        file.write('\n\t\t\t  </tr>')
    file.write('\n\t\t\t</table>\n\t\t<br>\n\t\t<div>\n\t\t\t<h2 class=\"thin_font\">')
    if result["full_result"][1]["score"]:
        file.write(f'{result["full_result"][1]["score"]:.{(1 > result["full_result"][1]["score"]) + (10 > result["full_result"][1]["score"]) + (100 > result["full_result"][1]["score"])}f}')
    else:
        file.write('N/A')
    file.write('</h2>\n\t\t\t<h4 class=\"thin_font\">Optimized ratio ')
    if result["full_result"][1]["score"]:
        file.write('x')
        file.write(f'{result["full_result"][1]["optimized_ratio"]:.{1}f}')
    else:
        file.write('-')
    file.write('</h4>\n\t\t\t<h3 class=\"thin_font\">Optimized score</h3>\n\t\t</div>\n\t\t<br>\n\t\t<div>\n\t\t\t<table>\n\t\t\t\t<tr>\n\t\t\t\t\t<td class=\"no_border\">\n\t\t\t\t\t\t<div class=\"sub_result_div white_on_grey\">\n\t\t\t\t\t\t\t<h2 class=\"thin_font\">')
    if result["full_result"][1]["detailed"][1]["valid"]:
        file.write(' ')
        file.write(f'{result["full_result"][1]["detailed"][1]["score"]:.{(1 > result["full_result"][1]["detailed"][1]["score"]) + (10 > result["full_result"][1]["detailed"][1]["score"]) + (100 > result["full_result"][1]["detailed"][1]["score"])}f}')
        file.write(' ')
    else:
        file.write(' N/A ')
    file.write('</h2>\n                            <h4 class=\"thin_font\">-</h4>\n                            <h3 class=\"thin_font\">Singlecore</h3>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</td>\n\t\t\t\t\t<td class=\"no_border\">\n\t\t\t\t\t\t<div class=\"sub_result_div white_on_grey\">\n                            <h2 class=\"thin_font\">')
    if result["full_result"][1]["detailed"][2]["valid"]:
        file.write(' ')
        file.write(f'{result["full_result"][1]["detailed"][2]["score"]:.{(1 > result["full_result"][1]["detailed"][2]["score"]) + (10 > result["full_result"][1]["detailed"][2]["score"]) + (100 > result["full_result"][1]["detailed"][2]["score"])}f}')
        file.write(' ')
    else:
        file.write(' N/A ')
    file.write('</h2>\n\t\t\t\t\t\t    <h4 class=\"thin_font\">')
    if result["full_result"][1]["detailed"][2]["valid"]:
        file.write('MP ratio x')
        file.write(f'{result["full_result"][1]["detailed"][2]["ratio"]:.{1}f}')
    else:
        file.write('-')
    file.write('</h4>\n\t\t\t\t\t\t\t<h3 class=\"thin_font\">Multicore</h3>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t\t<tr>\n\t\t\t\t\t<td class=\"no_border\">\n\t\t\t\t\t\t<div class=\"sub_result_div white_on_grey\">\n                            <h2 class=\"thin_font\">')
    if result["full_result"][1]["detailed"][3]["valid"]:
        file.write(' ')
        file.write(f'{result["full_result"][1]["detailed"][3]["score"]:.{(1 > result["full_result"][1]["detailed"][3]["score"]) + (10 > result["full_result"][1]["detailed"][3]["score"]) + (100 > result["full_result"][1]["detailed"][3]["score"])}f}')
        file.write(' ')
    else:
        file.write(' N/A ')
    file.write('</h2>\n\t\t\t\t\t\t    <h4 class=\"thin_font\">')
    if result["full_result"][1]["detailed"][3]["valid"]:
        file.write('MP ratio x')
        file.write(f'{result["full_result"][1]["detailed"][3]["ratio"]:.{1}f}')
    else:
        file.write('-')
    file.write('</h4>\n\t\t\t\t\t\t\t<h3 class=\"thin_font\">Quadcore</h3>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</td>\n\t\t\t\t\t<td class=\"no_border\">\n\t\t\t\t\t\t<div class=\"sub_result_div white_on_grey\">\n                            <h2 class=\"thin_font\">')
    if result["full_result"][1]["detailed"][4]["valid"]:
        file.write(' ')
        file.write(f'{result["full_result"][1]["detailed"][4]["score"]:.{(1 > result["full_result"][1]["detailed"][4]["score"]) + (10 > result["full_result"][1]["detailed"][4]["score"]) + (100 > result["full_result"][1]["detailed"][4]["score"])}f}')
        file.write(' ')
    else:
        file.write(' N/A ')
    file.write('</h2>\n\t\t\t\t\t\t    <h4 class=\"thin_font\">')
    if result["full_result"][1]["detailed"][4]["valid"]:
        file.write('MP ratio x')
        file.write(f'{result["full_result"][1]["detailed"][4]["ratio"]:.{1}f}')
    else:
        file.write('-')
    file.write('</h4>\n\t\t\t\t\t\t\t<h3 class=\"thin_font\">Workstation</h3>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</td>\n\t\t\t\t</tr>\n\t\t\t</table>\n\t\t</div>\n\t\t<br>\n\t\t<div>\n            <table class=\"grey_border\">\n                <tr class=\"white_on_grey\">\n                    <td></td>\n                    <td><b>Optimized</b></td>\n                    ')
    if result["full_result"][1]["detailed"][0]["valid"]:
        file.write('<td>Manual</td>')
    file.write('\n                    ')
    if result["full_result"][1]["detailed"][1]["valid"]:
        file.write('<td>Single core</td>')
    file.write('\n                    ')
    if result["full_result"][1]["detailed"][2]["valid"]:
        file.write('<td>Multi core</td>')
    file.write('\n                    ')
    if result["full_result"][1]["detailed"][3]["valid"]:
        file.write('<td>Quad core</td>')
    file.write('\n                    ')
    if result["full_result"][1]["detailed"][4]["valid"]:
        file.write('<td>Workstation</td>')
    file.write('\n                </tr>\n                <tr>\n                    <td></td>\n                    <td>Maximum</td>\n                    ')
    for i in range(0, 5):
        if result["full_result"][1]["detailed"][i]["valid"]:
            file.write('<td>')
            file.write(f'{result["full_result"][1]["detailed"][i]["maximum"]:.{(1 > result["full_result"][1]["detailed"][i]["maximum"]) + (10 > result["full_result"][1]["detailed"][i]["maximum"]) + (100 > result["full_result"][1]["detailed"][i]["maximum"])}f}')
            file.write('</td>')
    file.write('\n                </tr>\n                <tr>\n                    <td></td>\n                    <td>Average</td>\n                    ')
    for i in range(0, 5):
        if result["full_result"][1]["detailed"][i]["valid"]:
            file.write('<td>')
            file.write(f'{result["full_result"][1]["detailed"][i]["average"]:.{(1 > result["full_result"][1]["detailed"][i]["average"]) + (10 > result["full_result"][1]["detailed"][i]["average"]) + (100 > result["full_result"][1]["detailed"][i]["average"])}f}')
            file.write('</td>')
    file.write('\n                </tr>\n                <tr>\n                    <td></td>\n                    <td>Geometric</td>\n                    ')
    for i in range(0, 5):
        if result["full_result"][1]["detailed"][i]["valid"]:
            file.write('<td>')
            file.write(f'{result["full_result"][1]["detailed"][i]["geometric"]:.{(1 > result["full_result"][1]["detailed"][i]["geometric"]) + (10 > result["full_result"][1]["detailed"][i]["geometric"]) + (100 > result["full_result"][1]["detailed"][i]["geometric"])}f}')
            file.write('</td>')
    file.write('\n                </tr>\n                <tr>\n                    <td></td>\n                    <td>Harmonic</td>\n                    ')
    for i in range(0, 5):
        if result["full_result"][1]["detailed"][i]["valid"]:
            file.write('<td>')
            file.write(f'{result["full_result"][1]["detailed"][i]["harmonic"]:.{(1 > result["full_result"][1]["detailed"][i]["harmonic"]) + (10 > result["full_result"][1]["detailed"][i]["harmonic"]) + (100 > result["full_result"][1]["detailed"][i]["harmonic"])}f}')
            file.write('</td>')
    file.write('\n                </tr>\n                <tr>\n                    <td></td>\n                    <td>Minimum</td>\n                    ')
    for i in range(0, 5):
        if result["full_result"][1]["detailed"][i]["valid"]:
            file.write('<td>')
            file.write(f'{result["full_result"][1]["detailed"][i]["minimum"]:.{(1 > result["full_result"][1]["detailed"][i]["minimum"]) + (10 > result["full_result"][1]["detailed"][i]["minimum"]) + (100 > result["full_result"][1]["detailed"][i]["minimum"])}f}')
            file.write('</td>')
    file.write('\n                </tr>\n                <tr class=\"white_on_grey\">\n                    <td>#</td>\n                    <td>Kernel name</td>\n                    ')
    for i in range(0, 5):
        if result["full_result"][1]["detailed"][i]["valid"]:
            file.write('<td></td>')
    file.write('\n                </tr>\n                ')
    for i in range(0, 24):
        file.write('<tr>\n                    <td>')
        file.write(f'{i + 1:}')
        file.write('.</td>\n                    <td>')
        if i==0:
            file.write('Hydro fragment')
        if i==1:
            file.write('ICCG excerpt (Incomplete Cholesky Conjugate Gradient)')
        if i==2:
            file.write('Inner product')
        if i==3:
            file.write('Banded linear equations')
        if i==4:
            file.write('Tri-diagonal elimination, below diagonal')
        if i==5:
            file.write('General linear recurrence equations')
        if i==6:
            file.write('Equation of state fragment')
        if i==7:
            file.write('ADI integration')
        if i==8:
            file.write('Integrate predictors')
        if i==9:
            file.write('Difference predictors')
        if i==10:
            file.write('First sum')
        if i==11:
            file.write('First difference')
        if i==12:
            file.write('2-D PIC (Particle In Cell)')
        if i==13:
            file.write('1-D PIC (Particle In Cell)')
        if i==14:
            file.write('Casual Fortran.  Development version')
        if i==15:
            file.write('Monte Carlo search loop')
        if i==16:
            file.write('Implicit, conditional computation')
        if i==17:
            file.write('2-D explicit hydrodynamics fragment')
        if i==18:
            file.write('General linear recurrence equations')
        if i==19:
            file.write('Discrete ordinates transport, conditional recurrence on xx')
        if i==20:
            file.write('Matrix*matrix product')
        if i==21:
            file.write('Planckian distribution')
        if i==22:
            file.write('2-D implicit hydrodynamics fragment')
        if i==23:
            file.write('Find location of first minimum in array')
        file.write('</td>\n                    ')
        for j in range(0, 5):
            if result["full_result"][1]["detailed"][j]["valid"]:
                file.write('<td>')
                file.write(f'{result["full_result"][1]["detailed"][j]["kernels"][i]:.{(1 > result["full_result"][1]["detailed"][j]["kernels"][i]) + (10 > result["full_result"][1]["detailed"][j]["kernels"][i]) + (100 > result["full_result"][1]["detailed"][j]["kernels"][i])}f}')
                file.write('</td>')
        file.write('\n                </tr>')
    file.write('\n            </table>\n        </div>\n\t</div>\n</div>\n</body>\n</html>')

def get_logger() -> logging.Logger:
    try:
        parser = argparse.ArgumentParser(add_help=False, exit_on_error=False)
    except TypeError:
        parser = argparse.ArgumentParser(add_help=False)
        
    parser.add_argument('-d', '--debug', action='count', default=0)
    known_args, _ = parser.parse_known_args()

    verbosity = known_args.debug

    level = logging.ERROR
    if verbosity == 1:
        level = logging.WARNING
    elif verbosity == 2:
        level = logging.INFO
    elif verbosity >= 3:
        level = logging.DEBUG

    logging.basicConfig(level=level)
    return logging.getLogger("ResultParser")

logger = get_logger()

class ResultBuilder:
    def __init__(self):
        self.__cpu_core_count: Optional[int] = None
        self.__cpu_name: Optional[str] = None
        self.__date: Optional[str] = None
        self.__compiler: Optional[str] = None
        self.__version: Optional[str] = None
        self.__comment: Optional[str] = None
        self.__results_dataframe = None

    def get(self):
        if not self.__results_dataframe:
            raise ValueError("No valid dataframe table found in the input file. The file might be corrupted or in an unsupported format.")
            
        return {
            'system_info': {
                'compiler_info': self.__compiler,
                'version_info': self.__version if self.__version else 'Unknown',
                'cpu_name': self.__cpu_name,
                'cpu_core_count': self.__cpu_core_count,
            },
            'timestamp': self.__date,
            'comment': self.__comment,
            'full_result': self.__parse_dataframe()
        }

    def set_comment(self, comment: str):
        self.__comment = comment
        return self

    def set_version(self, version: str):
        self.__version = version
        return self

    def set_compiler(self, compiler: str):
        self.__compiler = compiler
        return self

    def set_date(self, date: str):
        self.__date = date
        return self

    def set_cpu_name(self, cpu_name: str):
        self.__cpu_name = cpu_name
        return self

    def set_cpu_core_count(self, cpu_core_count: int):
        self.__cpu_core_count = cpu_core_count
        return self

    def set_result_dataframe(self, dataframe):
        if len(dataframe) != 48:
            raise RuntimeError(f"Results dataframe received is not expected size of 48, received {len(dataframe)}")
        self.__results_dataframe = dataframe
        return self

    def __parse_dataframe(self):
        class Optimization(IntEnum):
            NON_OPTIMIZED = 0
            OPTIMIZED = 1

        class RunType(IntEnum):
            MANUAL = 0
            SINGLE_CORE = 1
            MULTI_CORE = 2
            QUAD_CORE = 3
            WORKSTATION = 4

        column_count = len(self.__results_dataframe[0])

        full_result = list()

        first_dataframe, second_dataframe = self.__split_dataframe(self.__results_dataframe)
        if self.__get_dataframe_average(first_dataframe) > self.__get_dataframe_average(second_dataframe):
            optimized_dataframe, non_optimized_dataframe = first_dataframe, second_dataframe
        else:
            optimized_dataframe, non_optimized_dataframe = second_dataframe, first_dataframe

        for optimization in Optimization:
            full_result.append({'detailed': []})
            for _ in RunType:
                full_result[optimization]['detailed'].append({'valid': False})

        runs_types = {
            1: [RunType.MANUAL],
            2: [RunType.SINGLE_CORE, RunType.MULTI_CORE],
            3: [RunType.SINGLE_CORE, RunType.MULTI_CORE, RunType.QUAD_CORE],
            4: [RunType.SINGLE_CORE, RunType.MULTI_CORE, RunType.QUAD_CORE, RunType.WORKSTATION]
        }[column_count]

        for i, run in enumerate(runs_types):
            full_result[Optimization.NON_OPTIMIZED]['detailed'][run]['valid'] = True
            full_result[Optimization.NON_OPTIMIZED]['detailed'][run]['kernels'] = []
            for row in non_optimized_dataframe:
                full_result[Optimization.NON_OPTIMIZED]['detailed'][run]['kernels'].append(row[i])

            full_result[Optimization.OPTIMIZED]['detailed'][run]['valid'] = True
            full_result[Optimization.OPTIMIZED]['detailed'][run]['kernels'] = []
            for row in optimized_dataframe:
                full_result[Optimization.OPTIMIZED]['detailed'][run]['kernels'].append(row[i])

        for run in full_result:
            for run_type in runs_types:
                run_type = run['detailed'][run_type]
                kernels = run_type['kernels']
                run_type['maximum'] = max(kernels)
                run_type['average'] = sum(kernels) / len(kernels)
                run_type['minimum'] = min(kernels)

                run_type['geometric'] = self.__geometric(kernels)
                run_type['harmonic'] = self.__harmonic(kernels)
                run_type['score'] = run_type['geometric']

        for run in full_result:
            for run_type in runs_types:
                run_type = run['detailed'][run_type]
                run_type['ratio'] = run_type['score'] / run['detailed'][RunType.SINGLE_CORE]['score']
            run['score'] = self.__geometric([run['detailed'][x]['geometric'] for x in runs_types if x is not RunType.WORKSTATION])
            run['optimized_ratio'] = run['score'] / full_result[Optimization.NON_OPTIMIZED]['score']

        return full_result
        
    @staticmethod
    def __geometric(iterable):
        return math.exp(sum(math.log(x) for x in iterable) / len(iterable))

    @staticmethod
    def __harmonic(iterable):
        return len(iterable) / sum(1 / x for x in iterable)

    @staticmethod
    def __split_dataframe(data):
        mid = len(data) // 2
        return data[:mid], data[mid:]

    @staticmethod
    def __get_dataframe_average(data):
        sum = 0
        count = 0
        for data_row in data:
            for data_cell in data_row:
                sum += data_cell
                count += 1
        return sum / count

class ResultParser:
    def __init__(self, data):
        self.__result = ResultBuilder()
        self.data = data
        pass

    def find(self, pattern, flags=re.IGNORECASE | re.MULTILINE):
        match = re.search(pattern, self.data, flags=flags)
        if match:
            return match.groups()[-1]
        return None

    @staticmethod
    def __to_dataframe(data, remove_first_column: bool = True):
        while type(data) is list and type(data[0]) in [list, str]:
            new_data = []
            for value in data:
                if type(value) is str:
                    value = re.findall(r'\d+\.?\d*', value)
                    new_data.extend([float(value) for value in value])
                elif type(value) is list:
                    new_data.extend(value)
            data = new_data

        columns = len(data) // (24 * 2)
        if columns == 0:
             raise ValueError("Extracted columns calculate to 0. Invalid data formatting.")
             
        rows = len(data) // columns

        data = [data[i * columns + (1 if remove_first_column else 0):(i + 1) * columns] for i in range(rows)]
        return data

    def parse_text(self):
        logger.debug(f"{__name__} enter")

        version_match = re.search(r'(?:version).*?- (.*)', self.data, flags=re.IGNORECASE)
        if version_match:
            self.__result.set_version(version_match.groups()[0])

        compiler_match = re.search(r'(?:compiler).*?- (.*)', self.data, flags=re.IGNORECASE)
        if compiler_match:
            self.__result.set_compiler(compiler_match.groups()[0])

        cpu_name_match = re.search(r'(?:cpu name|cpu|processor).*?- (.*)', self.data, flags=re.IGNORECASE)
        if cpu_name_match:
            self.__result.set_cpu_name(cpu_name_match.groups()[0])

        cpu_core_count_match = re.search(r'(?:logical cores|threads|cores|core count).*?- (.*)', self.data, flags=re.IGNORECASE)
        if cpu_core_count_match:
            self.__result.set_cpu_core_count(int(cpu_core_count_match.groups()[0]))

        date_match = re.search(r'(?:date|timestamp).*?- (.*)', self.data, flags=re.IGNORECASE)
        if date_match:
            self.__result.set_date(date_match.groups()[0])

        comment_match = re.search(r'(?:comment).*?- (.*)', self.data, flags=re.IGNORECASE)
        if comment_match:
            self.__result.set_comment(comment_match.groups()[0])

        parsed_successfully = False
        for table_column_count in [5, 4, 3, 2]:
            logger.debug(f"Trying {table_column_count} columns parsing")
            if len(re.findall(r'((?:[^\n\w\.]+[\.\d]+){{{}}})'.format(table_column_count), self.data)) < 48:
                continue
            raw_tables_values = re.findall(r'\n((?:\W+[\.\d]+){{{}}})'.format(table_column_count * 24), self.data)
            df = self.__to_dataframe(raw_tables_values)
            self.__result.set_result_dataframe(df)
            parsed_successfully = True
            break
            
        if not parsed_successfully:
             raise ValueError("Could not extract a valid 24-loop table matrix from the TXT data.")

        return self.__result.get()

    def parse_html(self):
        self.data = self.data.replace('\n', '')
        self.data = self.data.replace('\t', '')

        version_match = re.search(r'<td>(?:version).*?<td.*?>(\w.*?)</td>', self.data, flags=re.IGNORECASE)
        if version_match:
            self.__result.set_version(version_match.groups()[0])

        compiler_match = re.search(r'<td>(?:compiler).*?<td.*?>(\w.*?)</td>', self.data, flags=re.IGNORECASE)
        if compiler_match:
            self.__result.set_compiler(compiler_match.groups()[0])

        cpu_name_match = re.search(r'<td>(?:cpu name|cpu|processor).*?<td.*?>(\w.*?)</td>', self.data, flags=re.IGNORECASE)
        if cpu_name_match:
            self.__result.set_cpu_name(cpu_name_match.groups()[0])

        cpu_core_count_match = re.search(r'<td>(?:logical cores|threads|cores|core count).*?<td.*?>(\w.*?)</td>', self.data, flags=re.IGNORECASE)
        if cpu_core_count_match:
            self.__result.set_cpu_core_count(int(cpu_core_count_match.groups()[0]))

        date_match = re.search(r'<td>(?:date|timestamp).*?<td.*?>(\w.*?)</td>', self.data, flags=re.IGNORECASE)
        if date_match:
            self.__result.set_date(date_match.groups()[0])

        comment_match = re.search(r'<td>(?:comment).*?<td.*?>(.*?)</td>', self.data, flags=re.IGNORECASE)
        if comment_match:
            self.__result.set_comment(comment_match.groups()[0])

        raw_data = []
        table_rows = re.findall(r'<tr>.*?</tr>', self.data)

        for table_column_count in [4, 3, 2, 1]:
            for table_row in table_rows:
                if re.search(f'<tr><td>[\\d\\.]+</td>.*?(<td>[\\d\\.]+</td>){{{table_column_count}}}</tr>', table_row):
                    raw_data.append(re.findall(f'((?:<td>[\\d\\.]+</td>){{{table_column_count}}})</tr>', table_row))
            if raw_data:
                break
                
        if not raw_data:
             raise ValueError("Could not extract a valid table matrix from the HTML data.")

        df = self.__to_dataframe(raw_data, remove_first_column=False)
        self.__result.set_result_dataframe(df)

        return self.__result.get()

    def parse(self, filetype):
        if filetype == FileType.TEXT:
            return self.parse_text()
        elif filetype == FileType.HTML:
            return self.parse_html()

class FileType(Enum):
    TEXT = 1
    HTML = 2

def parse_arguments():
    parser = argparse.ArgumentParser(description="Evolution benchmark result parser")
    parser.add_argument('file', type=str, help='Path to the input file')
    parser.add_argument('-d', '--debug', action='count', default=0, help='Increase verbosity (use -d, -dd, -ddd)')
    return parser.parse_args()

def read_file(filepath):
    encodings = ["utf-8", "cp1252", "iso-8859-1"]
    for enc in encodings:
        try:
            with open(filepath, "r", encoding=enc, errors="replace") as file:
                return file.read()
        except UnicodeDecodeError:
            continue
    raise ValueError("File encoding could not be determined.")

def main():
    args = parse_arguments()
    logger.debug(f"Arguments: {args}")

    filename = args.file

    if filename.endswith('.html') or filename.endswith('.htm'):
        filetype = FileType.HTML
    elif filename.endswith('.txt'):
        filetype = FileType.TEXT
    else:
        logger.error(f"File {filename} does not have a supported file extension (.txt or .html).")
        sys.exit(1)
        
    logger.info(f"Detected file extension is {filetype}")

    try:
        data = read_file(filename)
    except Exception as e:
        logger.error(f"Failed to read file {filename}. Error: {e}")
        sys.exit(1)

    parser = ResultParser(data)
    
    try:
        result = parser.parse(filetype)
    except Exception as e:
        logger.error(f"Failed to parse the benchmark results. Error: {e}")
        logger.debug("Ensure the file contains valid Waverian Evolution benchmark data tables.")
        sys.exit(1)

    # Output naming logic
    base_name = filename.rsplit('.', 1)[0]
    out_html = f"{base_name}.html"
    out_txt = f"{base_name}_updated.txt"

    # HTML Generation
    try:
        with open(out_html, 'w', encoding='utf-8') as file:
            py_print_html(file, result)
        logger.info(f"Successfully generated HTML report: {out_html}")
    except Exception as e:
        logger.error(f"Failed to write HTML file {out_html}: {e}")

    # Version Logic for TXT Generation
    version_str = result.get("system_info", {}).get("version_info", "0.0.0")
    is_current = False
    try:
        v_match = re.search(r'(\d+)\.(\d+)', version_str)
        if v_match:
            major, minor = int(v_match.group(1)), int(v_match.group(2))
            if major > 3 or (major == 3 and minor >= 3):
                is_current = True
    except Exception:
        pass

    if is_current and filetype == FileType.TEXT:
        logger.info(f"Detected current version ({version_str} >= 3.3.0). Skipping TXT generation.")
    else:
        try:
            with open(out_txt, 'w', encoding='utf-8') as file:
                py_print(file, result)
            logger.info(f"Successfully generated TXT report: {out_txt}")
        except Exception as e:
            logger.error(f"Failed to write TXT file {out_txt}: {e}")

if __name__ == "__main__":
    main()