aboutsummaryrefslogtreecommitdiff
path: root/measurements/tolatex.py
blob: 845866e8fb36080a861caf966a3d1d8dd0c6ea0a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# Generates benchmark graphs in LaTex (following format from the pgfplots
# package)
# 
# This ignores duplicate entries, including different primes with the same
# number of bits.
import sys, math

USAGE = "USAGE: python tolatex.py [input file]"

SETUPS = {
        "fiat_montgomery32": "color=blue,mark=triangle*", 
        "fiat_montgomery64": "color=blue,mark=square*", 
        "fiat_solinas32": "color=blue,mark=triangle", 
        "fiat_solinas64": "color=blue,mark=square", 
        "gmpvar": "color=red,mark=*", 
        "gmpxx": "color=red,mark=o", 
        "gmpsec" : "color=red,mark=x"
        }

LEGEND = {
        "fiat_montgomery32": "ours, Montgomery reduction", 
        "fiat_montgomery64": "ours, Montgomery reduction", 
        "fiat_solinas32": "ours, Solinas reduction", 
        "fiat_solinas64": "ours, Solinas reduction", 
        "gmpvar": "GMP mpn_ API", 
        "gmpxx": "GMP C++ API", 
        "gmpsec" : "GMP mpn_sec API"
        }

EXCLUDE = [
        "fiat_montgomery32",
        "fiat_solinas32"
        ]

class ParseException(Exception): pass

def parse_line(line):
    data = line.strip().split("\t")
    if len(data) != 3 or (data[1] not in SETUPS) or ("2e" not in data[0]) :
        raise ParseException("Could not parse line %s" %line)
    return { 
            "prime" : data[0],
            "setup" : data[1],
            "time" : data[2] }

# given a string representing one term or "tap" in a prime, returns a pair of
# integers representing the weight and coefficient of that tap
#    "2 ^ y" -> [1, y]
#    "x * 2 ^ y" -> [x, y]
#    "x * y" -> [x*y,0]
#    "x" -> [x,0]
def parse_term(t) :
    if "*" not in t and "^" not in t:
        return [int(t),0]

    if "*" in t:
        if len(t.split("*")) > 2: # this occurs when e.g. [w - x * y] has been turned into [w + -1 * x * y]
            a1,a2,b = t.split("*")
            a = int(a1) * int(a2)
        else:
            a,b = t.split("*")
        if "^" not in b:
            return [int(a) * int(b),0]
    else:
        a,b = (1,t)

    b,e = b.split("^")
    if int(b) != 2:
        raise NonBase2Exception("Could not parse term, power with base other than 2: %s" %t)
    return [int(a),int(e)]

# expects prime to be a string and expressed as sum/difference of products of
# two with small coefficients (e.g. '2^448 - 2^224 - 1', '2^255 - 19')
def parse_prime(prime):
    prime = prime.replace("-", "+ -").replace(' ', '').replace('+-2^', '+-1*2^')
    terms = prime.split("+")
    return list(map(parse_term, terms))

# remove duplicates, reorganize, and parse primes
def clean_data(parsed_lines):
    out = {s:{} for s in SETUPS}
    for ln in parsed_lines:
        prime2 = ln["prime"].replace("e", "^").replace("m", "-").replace("p","+").replace("x","*")
        p = sum([(x * (2**e)) for x,e in parse_prime(prime2)])
        n = math.log2(p)
        # if some measurement is duplicated, ignore the repeats
        if n not in out[ln["setup"]]:
            out[ln["setup"]][n] = ln["time"]
    return out

def makeplot(data):
    out = """
 \\begin{figure*}
 \\begin{tikzpicture}
 \t\\begin{axis}[
 \t\theight=9cm,
 \t\twidth=\\textwidth,
 \t\tlegend pos= north west,
 \t\txtick distance=100,
 \t\textra x ticks={127,256,448,480},
 \t\textra x tick style={grid=major, tick label style={rotate=45,anchor=east}},
 \t\tymin=0,
 \t\txlabel=log2(prime),
 \t\tylabel=Time (seconds)]"""
    for s in SETUPS:
        if s in EXCLUDE:
            continue
        out +="\t\t\\addplot[%s] coordinates {\n" %SETUPS[s]
        for n in data[s]:
            out += "\t\t\t(%s, %s) \n" %(n, data[s][n])
        out += "\t\t};\n"
        out += "\t\t\\addlegendentry{%s}\n\n" %LEGEND[s].replace("_", "\_")
    out += "\t\end{axis}\n\\end{tikzpicture}\n\\end{figure*}"
    return out

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print(USAGE)
        sys.exit()
    f = open(sys.argv[1])
    parsed_lines = []
    for line in f:
        try:
            parsed_lines.append(parse_line(line))
        except ParseException:
            print("WARNING: Could not parse line %s, skipping" %line)
    f.close()
    print(makeplot(clean_data(parsed_lines)))