1import math
2from datetime import date
3
4NORMALFORMAT=0
5BYCFORMAT=1
6BYDFORMAT=2
7
8def joinit(iterable, delimiter):
9    it = iter(iterable)
10    yield next(it)
11    for x in it:
12        yield delimiter
13        yield x
14
15# To format, in HTML, the cores in the right order.
16# First we order the categories
17# Then we order the cores in each category
18# The final ORDEREDCORES is what is used
19# to order tjhe values
20# Since some cores may be missing, each atble display
21# is computing a rstricted ordered core list with only the available cores.
22CORTEXCATEGORIES=["Cortex-M","Cortex-R","Cortex-A"]
23CORECATEGORIES={"Cortex-M":["m0","m4", "m7", "m33" , "m55 scalar", "m55 mve","m55 autovec"],
24"Cortex-R":["r8","r52"],
25"Cortex-A":["a32"]
26}
27ORDEREDCORES=[]
28for cat in CORTEXCATEGORIES:
29  cores=[]
30  if cat in CORECATEGORIES:
31     for core in CORECATEGORIES[cat]:
32       cores.append(core)
33  else:
34    print("Error core %s not found" % cat)
35    quit()
36  ORDEREDCORES += cores
37
38ORDEREDTYPES=["q7","q15","q31","u32","f16","f32","f64"]
39
40class Markdown:
41  def __init__(self,output):
42    self._id=0
43    self._output = output
44
45  def visitBarChart(self,data):
46      pass
47
48  def visitHistory(self,data):
49      pass
50
51  def visitText(self,text):
52      self._output.write(text)
53
54    # Write columns in markdown format
55  def writeColumns(self,cols):
56        colStr = "".join(joinit(cols,"|"))
57        self._output.write("|")
58        self._output.write(colStr)
59        self._output.write("|\n")
60        sepStr="".join(joinit([":-:" for x in cols],"|"))
61        self._output.write("|")
62        self._output.write(sepStr)
63        self._output.write("|\n")
64
65    # Write row in markdown format
66  def writeRow(self,row):
67        row=[str(x) for x in row]
68        rowStr = "".join(joinit(row,"|"))
69        self._output.write("|")
70        self._output.write(rowStr)
71        self._output.write("|\n")
72
73  def visitTable(self,table):
74      self.writeColumns(table.columns)
75      for row in table.rows:
76        self.writeRow(row)
77
78  def visitSection(self,section):
79     self._id = self._id + 1
80     header = "".join(["#" for i in range(self._id)])
81     self._output.write("%s %s\n" % (header,section.name))
82
83  def leaveSection(self,section):
84     self._id = self._id - 1
85
86  def visitDocument(self,document):
87      if document.runidHeader:
88         self._output.write("Document generated for run ids : %s\n" % document.runidHeader)
89
90  def leaveDocument(self,document):
91      pass
92
93styleSheet="""
94<style type='text/css'>
95
96#TOC {
97  position: fixed;
98  left: 0;
99  top: 0;
100  width: 290px;
101  height: 100%;
102  overflow:auto;
103  margin-top:5px;
104  margin-bottom:10px;
105}
106
107html {
108  font-size: 16px;
109}
110
111html, body {
112  background-color: #E5ECEB;
113  font-family: "Lato";
114  font-style: normal; font-variant: normal;
115  color: #002B49;
116  line-height: 1.5em;
117}
118
119body {
120  margin: auto;
121  margin-top:0px;
122  margin-left:290px;
123
124}
125
126.NA {
127  color: #999999;
128}
129
130.testname {
131  color: #0091BD;
132  font-size: 1.125em;
133}
134
135h1,
136h2,
137h3,
138h4,
139h5,
140h6 {
141  font-weight: bold;
142}
143h1 {
144  font-size: 1.875em;
145  margin-top:5px;
146}
147h2 {
148  font-size: 1.3125em;
149}
150h3 {
151  font-size: 1.3125em;
152  margin-left:1em;
153}
154h4 {
155  font-size: 1.125em;
156  margin-left:1em;
157}
158h5,
159h6 {
160  font-size: 1em;
161  margin-left:1em;
162}
163
164#TOC h1 {
165  margin-top:0em;
166  margin-left:0.5em;
167}
168
169table {
170  margin-bottom: 1.5em;
171  font-size: 1em;
172  width: 100%;
173  border-collapse: collapse;
174  border-spacing: 0;
175  width: 100%;
176  margin-left:1em;
177}
178thead th,
179tfoot th {
180  padding: .25em .25em .25em .4em;
181  text-transform: uppercase;
182}
183th {
184  text-align: left;
185}
186td {
187  vertical-align: top;
188  padding: .25em .25em .25em .4em;
189}
190
191.ty-table-edit {
192  background-color: transparent;
193}
194thead {
195  background-color: #979ea3;
196}
197tr:nth-child(even) {
198  background: #d7dadc;
199}
200
201ul, #myUL {
202  list-style-type: none;
203  padding-inline-start:10px;
204}
205
206
207
208/* Remove margins and padding from the parent ul */
209#myUL {
210  margin: 0;
211  padding: 0;
212}
213
214/* Style the caret/arrow */
215.caret {
216  cursor: pointer;
217  user-select: none; /* Prevent text selection */
218}
219
220/* Create the caret/arrow with a unicode, and style it */
221.caret::before {
222  content: "\\25B6";
223  color: black;
224  display: inline-block;
225  margin-right: 6px;
226}
227
228/* Rotate the caret/arrow icon when clicked on (using JavaScript) */
229.caret-down::before {
230  transform: rotate(90deg);
231}
232
233/* Hide the nested list */
234.nested {
235  display: none;
236}
237
238/* Show the nested list when the user clicks on the caret/arrow (with JavaScript) */
239.active {
240  display: block;
241}
242
243.firstcore {
244  border-left-color: black;
245  border-left-style: solid;
246  border-left-width: 2px;
247}
248
249
250</style>
251"""
252
253script="""<script type="text/javascript">
254var toggler = document.getElementsByClassName("caret");
255var i;
256for (i = 0; i < toggler.length; i++) {
257  toggler[i].addEventListener("click", function() {
258    this.parentElement.querySelector(".nested").classList.toggle("active");
259    this.classList.toggle("caret-down");
260  });
261}</script>"""
262
263barscript="""    <script src="https://d3js.org/d3.v5.js"></script>
264
265
266
267<script type="text/javascript">
268
269histwidth=400;
270histheight=200;
271histmargin={left:40,right:100,bottom:40,top:10};
272
273function legend(color,svg)
274{
275    const g = svg
276      .attr("transform", `translate(${histwidth},0)`)
277      .attr("text-anchor", "end")
278      .attr("font-family", "sans-serif")
279      .attr("font-size", 9)
280    .selectAll("g")
281    .data(color.domain().slice().reverse())
282    .join("g")
283      .attr("transform", (d, i) => `translate(0,${i * 20})`);
284
285  g.append("rect")
286      .attr("x", -19)
287      .attr("width", 19)
288      .attr("height", 19)
289      .attr("fill", color);
290
291  g.append("text")
292      .attr("x", -24)
293      .attr("y", 9.5)
294      .attr("dy", "0.35em")
295      .text(d => d);
296
297}
298
299function myhist(data,theid)
300{
301    var x,y,xAxis,yAxis,svg,color;
302
303
304
305color = d3.scaleOrdinal()
306    .domain(data.series.map(d => d['name']))
307    .range(["#FF6B00",
308"#FFC700",
309"#95D600",
310"#00C1DE",
311"#0091BD",
312"#002B49",
313"#333E48",
314"#7D868C",
315"#E5ECEB"]);
316
317    svg = d3.select(theid).insert("svg")
318      .attr("viewBox", [0, 0, histwidth, histheight]);
319
320
321sx = d3.scaleLinear()
322    .domain(d3.extent(data.dates))
323    .range([histmargin.left,histwidth - histmargin.right]);
324
325sy = d3.scaleLinear()
326    .domain([0, d3.max(data.series, d => d3.max(d.values,q => q[1]))]).nice()
327    .range([histheight - histmargin.bottom, histmargin.top]);
328
329xAxis = g => g
330    .attr("transform", `translate(0,${histheight - histmargin.bottom})`)
331    .call(d3.axisBottom(sx).tickValues(data.dates).ticks(histwidth / 80,"d").
332        tickSizeOuter(0));
333
334svg.append("text")
335    .attr("class", "x label")
336    .attr("text-anchor", "end")
337    .attr("x", histwidth/2.0)
338    .attr("y", histheight - 6)
339    .text("RUN ID");
340
341yAxis = g => g
342    .attr("transform", `translate(${histmargin.left},0)`)
343    .call(d3.axisLeft(sy))
344    .call(g => g.select(".domain").remove());
345
346
347
348line = d3.line()
349    .x(d => sx(data.dates[d[0]])
350    )
351    .y(d => sy(d[1]));
352
353svg.append("g")
354      .call(xAxis);
355
356  svg.append("g")
357      .call(yAxis);
358
359  const path = svg.append("g")
360      .attr("fill", "none")
361      .attr("stroke", "steelblue")
362      .attr("stroke-width", 1.5)
363      .attr("stroke-linejoin", "round")
364      .attr("stroke-linecap", "round")
365    .selectAll("path")
366    .data(data.series)
367    .join("path")
368      .style("mix-blend-mode", "multiply")
369      .attr("stroke", d => color(d.name))
370      .attr("d", d => line(d.values));
371
372// Legend
373
374
375
376svg.append("g")
377      .call(d => legend(color,d));
378  //svg.call(hover, path);
379
380
381}
382
383function mybar(data,theid)
384{
385    var width,height,margin,x,y,xAxis,yAxis,svg,color;
386
387    width=400;
388    height=100;
389    margin={left:40,right:10,bottom:40,top:10};
390
391
392    svg = d3.select(theid).insert("svg")
393      .attr("viewBox", [0, 0, width, height]);;
394
395x = d3.scaleBand()
396    .domain(d3.range(data.length))
397    .range([margin.left, width - margin.right])
398    .padding(0.1);
399
400y = d3.scaleLinear()
401    .domain([0, d3.max(data, d => d.value)]).nice()
402    .range([height - margin.bottom, margin.top]);
403
404xAxis = g => g
405    .attr("transform", `translate(0,${height - margin.bottom})`)
406    .call(d3.axisBottom(x).tickFormat(i => data[i].name).tickSizeOuter(0));
407
408yAxis = g => g
409    .attr("transform", `translate(${margin.left},0)`)
410    .call(d3.axisLeft(y).ticks(4, data.format))
411    .call(g => g.select(".domain").remove())
412    .call(g => g.append("text")
413        .attr("x", -margin.left)
414        .attr("y", 10)
415        .attr("fill", "currentColor")
416        .attr("text-anchor", "start")
417        .text(data.y));
418
419color = "steelblue"
420
421  svg.append("g")
422      .attr("fill", color)
423    .selectAll("rect")
424    .data(data)
425    .join("rect")
426      .attr("x", (d, i) => x(i))
427      .attr("y", d => y(d.value))
428      .attr("height", d => y(0) - y(d.value))
429      .attr("width", x.bandwidth());
430
431  svg.append("g")
432      .call(xAxis);
433
434  svg.append("g")
435      .call(yAxis);
436
437}
438</script>"""
439
440
441class HTMLToc:
442  def __init__(self,output):
443    self._id=0
444    self._sectionID = 0
445    self._output = output
446
447
448
449  def visitTable(self,table):
450      pass
451
452  def visitBarChart(self,data):
453      pass
454
455  def visitHistory(self,data):
456      pass
457
458  def visitText(self,text):
459      pass
460
461  def visitSection(self,section):
462     self._id = self._id + 1
463     self._sectionID = self._sectionID + 1
464     if section.hasChildren:
465        self._output.write("<li><span class=\"caret\"><a href=\"#section%d\">%s</a></span>\n" % (self._sectionID,section.name))
466        self._output.write("<ul class=\"nested\">\n")
467     else:
468        self._output.write("<li><span><a href=\"#section%d\">%s</a></span>\n" % (self._sectionID,section.name))
469
470  def leaveSection(self,section):
471    if section.hasChildren:
472       self._output.write("</ul></li>\n")
473
474    self._id = self._id - 1
475
476  def visitDocument(self,document):
477      self._output.write("<div id=\"TOC\"><h1>Table of content</h1><ul id=\"myUL\">\n")
478
479
480  def leaveDocument(self,document):
481      self._output.write("</ul></div>%s\n" % script)
482
483def permutation(ordered,unordered,mode):
484    result=[]
485    restricted=[]
486    order = ORDEREDCORES
487    if mode == BYDFORMAT:
488      order = ORDEREDTYPES
489    for c in order:
490      if c in unordered:
491         restricted.append(c)
492
493    for c in unordered:
494      result.append(restricted.index(c))
495
496    return(result,restricted)
497
498def reorder(p,v):
499    result=[0 for x in v]
500    for val,i in zip(v,p):
501        result[i]=val
502
503    return(result)
504
505class HTML:
506  def __init__(self,output,regMode,ratio,reorder):
507    self._id=0
508    self._sectionID = 0
509    self._barID = 0
510    self._histID = 0
511    self._output = output
512    self._regMode = regMode
513    self._reorder = reorder
514    self._ratioMode = ratio and regMode
515
516  def visitBarChart(self,bar):
517      data=bar.data
518      datastr = "".join(joinit(["{name:'%s',value:%s}" % x for x in data],","))
519      #print(datastr)
520      self._output.write("<p id=\"g%d\"></p>\n" % self._barID)
521      self._output.write("""<script type="text/javascript">
522thedata%d=[%s];
523mybar(thedata%d,"#g%d");
524</script>""" % (self._barID,datastr,self._barID,self._barID))
525
526      self._barID = self._barID + 1
527
528  def _getIndex(self,runids,data):
529    return([[runids.index(x[0]),x[1]] for x in data])
530
531  def visitHistory(self,hist):
532      data=hist.data
533      runidstr = "".join(joinit([str(x) for x in hist.runids],","))
534      serieelems=[]
535      for core in data:
536        serieelems.append("{name: '%s',values: %s}" % (core,self._getIndex(hist.runids,data[core])))
537
538      seriestr = "".join(joinit(serieelems,","))
539      datastr="""{
540series: [%s],
541 dates: [%s]
542};""" %(seriestr,runidstr);
543      #print(datastr)
544      self._output.write("<p id=\"hi%d\"></p>\n" % self._histID)
545      self._output.write("""<script type="text/javascript">
546thehdata%d=%s
547myhist(thehdata%d,"#hi%d");
548</script>""" % (self._histID,datastr,self._histID,self._histID))
549
550      self._histID = self._histID + 1
551
552  def visitText(self,text):
553      self._output.write("<p>\n")
554      self._output.write(text.text)
555      self._output.write("</p>\n")
556
557  def visitTable(self,table):
558      self._output.write("<table>\n")
559      self._output.write("<thead>\n")
560      self._output.write("<tr>\n")
561      firstCore = False
562      for col in table.params:
563        firstCore = True
564        self._output.write("<th class=\"param\">")
565        self._output.write(str(col))
566        self._output.write("</th>\n")
567
568      if self._reorder == NORMALFORMAT:
569         perm,restricted=permutation(ORDEREDCORES,table.cores,self._reorder)
570      elif self._reorder == BYDFORMAT:
571         perm,restricted=permutation(ORDEREDTYPES,table.cores,self._reorder)
572      else:
573         restricted = table.cores
574
575      for col in restricted:
576        if firstCore:
577           self._output.write("<th class=\"firstcore\">")
578        else:
579           self._output.write("<th class=\"core\">")
580        self._output.write(str(col))
581        self._output.write("</th>\n")
582        firstCore = False
583      self._output.write("</tr>\n")
584      self._output.write("</thead>\n")
585
586      nbParams = len(table.params)
587      for row in table.rows:
588        self._output.write("<tr>\n")
589        i = 0
590
591        row=list(row)
592
593        #print(row)
594
595        params=row[0:nbParams]
596        values=row[nbParams:]
597
598        if self._reorder == NORMALFORMAT:
599          row = params + reorder(perm,values)
600        elif self._reorder == BYDFORMAT:
601          row = params + reorder(perm,values)
602        else:
603          row = params + values
604
605        for elem in row:
606            txt=str(elem)
607            if txt == 'NA':
608               txt = "<span class=\"NA\">" + txt + "</span>"
609            if i < nbParams:
610               self._output.write("<td class=\"param\">")
611               self._output.write(txt)
612               self._output.write("</td>\n")
613            elif i == nbParams and nbParams != 0:
614               self._output.write("<td class=\"firstcore\">")
615               self._output.write(txt)
616               self._output.write("</td>\n")
617            else:
618               self._output.write("<td class=\"core\">")
619               self._output.write(txt)
620               self._output.write("</td>\n")
621            i = i + 1
622        self._output.write("</tr>\n")
623      self._output.write("</table>\n")
624
625
626  def visitSection(self,section):
627     self._id = self._id + 1
628     self._sectionID = self._sectionID + 1
629     name = section.name
630     if section.isTest:
631        name = "<span class=\"testname\">" + name + "</span>"
632     self._output.write("<h%d id=\"section%d\">%s</h%d>\n" % (self._id,self._sectionID,name,self._id))
633
634  def leaveSection(self,section):
635     self._id = self._id - 1
636
637  def visitDocument(self,document):
638      self._output.write("""<!doctype html>
639<html>
640<head>
641<meta charset='UTF-8'><meta name='viewport' content='width=device-width initial-scale=1'>
642<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato">
643<title>Benchmarks</title>%s</head><body>\n""" % styleSheet)
644      if self._regMode and not self._ratioMode:
645         self._output.write("<h1>ECPS Benchmark Regressions</h1>\n")
646      elif self._ratioMode:
647         self._output.write("<h1>ECPS Benchmark Ratios</h1>\n")
648      else:
649         self._output.write("<h1>ECPS Benchmark Summary</h1>\n")
650
651      if document.runidHeader:
652         self._output.write("<p>Document generated for run ids : %s</p>\n" % document.runidHeader)
653      today = date.today()
654      d2 = today.strftime("%B %d, %Y")
655      self._output.write("<p>Document generated on  %s</p>\n" % d2)
656
657      self._output.write(barscript)
658
659  def leaveDocument(self,document):
660    document.accept(HTMLToc(self._output))
661
662    self._output.write("</body></html>\n")
663
664
665