#!/usr/bin/python3 '''Report the datastructures from best performers to worst for a given area of comparison''' import re #mport sys import glob #mport pprint import functools #mport collections def return_javascript_code(): ''' Output the required javascript for drilling down to greater levels of detail. Derived from http://www.gnostice.com/nl_article.asp?id=208&t=Interactive_Tree_Control_Using_An_HTML_List_and_JavaScript ''' # BTW, this dislikes it when you close your <li>'s with </li>'s # This xml tag makes chrome and perhaps others have problems #<?xml version="1.0" encoding="UTF-8"?> string = ''' <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8" /> <title>Python Dictionary Results Table</title> <style type="text/css"> ul.LinkedList { display: block; } /* ul.LinkedList ul { display: none; } */ .HandCursorStyle { cursor: pointer; cursor: hand; } /* For IE */ </style> <script type="text/JavaScript"> // Add this to the onload event of the BODY element function addEvents() { activateTree(document.getElementById("LinkedList1")); } // This function traverses the list and add links // to nested list items function activateTree(oList) { // Collapse the tree for (var i=0; i < oList.getElementsByTagName("ul").length; i++) { oList.getElementsByTagName("ul")[i].style.display="none"; } // Add the click-event handler to the list items if (oList.addEventListener) { oList.addEventListener("click", toggleBranch, false); } else if (oList.attachEvent) { // For IE oList.attachEvent("onclick", toggleBranch); } // Make the nested items look like links addLinksToBranches(oList); } // This is the click-event handler function toggleBranch(event) { var oBranch, cSubBranches; if (event.target) { oBranch = event.target; } else if (event.srcElement) { // For IE oBranch = event.srcElement; } cSubBranches = oBranch.getElementsByTagName("ul"); if (cSubBranches.length > 0) { if (cSubBranches[0].style.display == "block") { cSubBranches[0].style.display = "none"; } else { cSubBranches[0].style.display = "block"; } } } // This function makes nested list items look like links function addLinksToBranches(oList) { var cBranches = oList.getElementsByTagName("li"); var i, n, cSubBranches; if (cBranches.length > 0) { for (i=0, n = cBranches.length; i < n; i++) { cSubBranches = cBranches[i].getElementsByTagName("ul"); if (cSubBranches.length > 0) { addLinksToBranches(cSubBranches[0]); cBranches[i].className = "HandCursorStyle"; cBranches[i].style.color = "blue"; cSubBranches[0].style.color = "black"; cSubBranches[0].style.cursor = "auto"; } } } } </script> </head> <body onload="addEvents();"> ''' return string def get_last_line(file_): '''Get the last line of file_''' last = None for line in file_: last = line return last @functools.total_ordering class Number_Pattern_Key(object): '''Store a number pattern key''' # pylint: disable=too-few-public-methods def __init__(self, number_pattern): self.number_pattern = number_pattern def __hash__(self): return hash(self.number_pattern) def __str__(self): return self.number_pattern __repr__ = __str__ def cmp(self, other): '''Compare two number pattern keys''' if self.number_pattern < other.number_pattern: return -1 elif self.number_pattern > other.number_pattern: return 1 else: return 0 def __lt__(self, other): if self.cmp(other) == -1: return True else: return False def __eq__(self, other): if self.cmp(other) == 0: return True else: return False @functools.total_ordering class Size_Key(object): '''Store a size key''' # pylint: disable=too-few-public-methods def __init__(self, size): self.size = size def __hash__(self): return hash(self.size) def __str__(self): return str(self.size) __repr__ = __str__ def cmp(self, other): '''Compare two size keys in order''' if self.size < other.size: return -1 elif self.size > other.size: return 1 else: return 0 def __lt__(self, other): if self.cmp(other) == -1: return True else: return False def __eq__(self, other): if self.cmp(other) == 0: return True else: return False @functools.total_ordering class Interpreter_Key(object): '''A class to hold an interpreter key''' # pylint: disable=too-few-public-methods def __init__(self, interpreter): self.interpreter = interpreter def __hash__(self): return hash(self.interpreter) def __str__(self): return str(self.interpreter) __repr__ = __str__ def cmp(self, other): '''Compare two interpreter names''' if self.interpreter < other.interpreter: return -1 elif self.interpreter > other.interpreter: return 1 else: return 0 def __lt__(self, other): if self.cmp(other) == -1: return True else: return False def __eq__(self, other): if self.cmp(other) == 0: return True else: return False @functools.total_ordering class Tree_Type_Key(object): '''Store a tree type key''' # pylint: disable=too-few-public-methods def __init__(self, tree_type): self.tree_type = tree_type def __hash__(self): return hash(self.tree_type) def __str__(self): return str(self.tree_type) __repr__ = __str__ def cmp(self, other): '''Compare two Tree_Type_keys''' if self.tree_type < other.tree_type: return -1 elif self.tree_type > other.tree_type: return 1 else: return 0 def __lt__(self, other): if self.cmp(other) == -1: return True else: return False def __eq__(self, other): if self.cmp(other) == 0: return True else: return False @functools.total_ordering class Performer(object): '''Class for holding a description of a top performing datastructure''' # pylint: disable=too-few-public-methods,too-many-return-statements def __init__(self, size, tree_type, duration, standard_deviation): self.size = int(size.size) self.tree_type = tree_type self.duration = float(duration) self.standard_deviation = float(standard_deviation) def cmp(self, other): '''Compare two Top_Performer's''' # Compare size in reverse if self.size < other.size: return 1 elif self.size > other.size: return -1 else: # Compare duration forward if self.duration < other.duration: return -1 elif self.duration > other.duration: return 1 #else: # if self.tree_type < other.tree_type: # return -1 # elif self.tree_type > other.tree_type: # return 1 # else: # return 0 else: return 0 def __lt__(self, other): if self.cmp(other) == -1: return True else: return False def __eq__(self, other): if self.cmp(other) == 0: return True else: return False def gen_performers(dict_): '''Generate the top performers''' for size_key in dict_: tree_type_dict = dict_[size_key] for tree_type_key in tree_type_dict: mean = tree_type_dict[tree_type_key][0] standard_deviation = tree_type_dict[tree_type_key][1] yield Performer(size_key, tree_type_key, mean, standard_deviation) def main(): # pylint: disable=too-many-locals '''Main function''' dict_ = {} for filename in glob.glob('*.dat'): fixed_red_black = filename.replace('red-black', 'red_black') fields = fixed_red_black.split('-') interpreter = '-'.join(fields[:2]) number_pattern = fields[2] # tree_type = re.sub(r'\.dat$', '', '-'.join(fields[5:7])) tree_type = re.sub(r'\.dat$', '', fields[3]) with open(filename, 'r') as file_: last_line = get_last_line(file_) size, duration, standard_deviation = last_line.split() size = int(size) duration = round(float(duration), 1) standard_deviation = round(float(standard_deviation), 3) number_pattern_key = Number_Pattern_Key(number_pattern) size_key = Size_Key(size) interpreter_key = Interpreter_Key(interpreter) tree_type_key = Tree_Type_Key(tree_type) if number_pattern_key not in dict_: dict_[number_pattern_key] = {} if interpreter_key not in dict_[number_pattern_key]: dict_[number_pattern_key][interpreter_key] = {} if size_key not in dict_[number_pattern_key][interpreter_key]: dict_[number_pattern_key][interpreter_key][size_key] = {} if tree_type_key not in dict_[number_pattern_key][interpreter_key][size_key]: dict_[number_pattern_key][interpreter_key][size_key][tree_type_key] = (duration, standard_deviation) num_top_performers = 20 print(return_javascript_code()) print('<ul id="LinkedList1" class="LinkedList">') for number_pattern in sorted(dict_): print('<li>{}'.format(number_pattern)) print('\t<ul>') for interpreter_key in sorted(dict_[number_pattern]): print('\t<li>{}'.format(interpreter_key)) print('\t\t<ul>') performers_it = gen_performers(dict_[number_pattern][interpreter_key]) performers = list(performers_it) performers.sort() for performer in performers[:num_top_performers]: # print('1:', type(performer.size)) # print('2:', type(performer.duration)) # print('3:', type(performer.tree_type)) string = '\t\t\t<li>Ops: {:>7}, duration: {:>5} +/- {}, ops/sec: {:>11.2f}, dictionary type: {}'.format( performer.size, performer.duration, performer.standard_deviation, float(str(performer.size)) / float(str(performer.duration)), performer.tree_type, ) print(string.replace(' ', ' ')) print('\t\t</ul>') print('\t</ul>') print('</ul>') print('</body>') print('</html>') main()