3 import sys, os, re, fractions
4 from fractions import Fraction
8 header_re = re.compile(r'^(\d+) into (\d+).*:.* ([\d/]+)')
9 dissect_re = re.compile(r'\s+(\d+)\s+x\s+\((([\d/]+ \+ )*[\d/]+)\)')
13 def find_min_frag(dissection):
15 for side in dissection:
16 for count, pieces in side:
19 if ret is None or ret > piece:
21 assert ret is not None
24 def verify(n, m, dissection):
25 collections = [{}, {}]
27 for count, pieces in dissection[0]:
28 assert sum(pieces) == n
31 collections[0].setdefault(piece, 0)
32 collections[0][piece] += count
35 for count, pieces in dissection[1]:
36 assert sum(pieces) == m
39 collections[1].setdefault(piece, 0)
40 collections[1][piece] += count
42 assert collections[0] == collections[1]
44 def gcd_dissector(n,m):
45 gcd = fractions.gcd(n,m)
46 return [[(m, (gcd,) * (n/gcd))],
47 [(n, (gcd,) * (m/gcd))]]
49 def invent_dissection(n,m):
50 # Try to come up with a dissection by assorted simple methods.
52 for dissector, this_reason in [
53 (gcd_dissector, "trivial gcd solution"),
55 dissection = dissector(n, m)
56 if dissection is None: continue
57 verify(n, m, dissection)
58 this_best = find_min_frag(dissection)
60 best, best_reason = this_best, this_reason
62 return dissection, this_reason
67 # Look for appropriate file(s).
68 for filename, nature in [("data/main.%d.%d" % (n,m), "e"),
69 ("data/partition.%d.%d" % (n,m), "p"),
70 ("data/manual.%d.%d" % (n,m), "m")]:
71 if os.path.exists(filename):
72 with open(filename) as f:
74 match = header_re.match(header)
75 assert match is not None
76 assert int(match.group(1)) == n
77 assert int(match.group(2)) == m
78 this_best = Fraction(match.group(3))
80 # Now read the actual dissection.
82 for line in f.readlines():
83 match = dissect_re.match(line)
85 count = int(match.group(1))
86 pieces = map(Fraction, match.group(2).split(" + "))
89 dissection[0].append((count, pieces))
91 dissection[1].append((count, pieces))
93 assert False, "wrong length"
94 verify(n, m, dissection)
95 assert find_min_frag(dissection) == this_best
96 dissections[(n,m)] = dissection
103 # We don't have a stored solution at all, so make up something
104 # plausible if we can.
105 dissection, best_nature = invent_dissection(n, m)
106 best = find_min_frag(dissection)
107 dissections[(n,m)] = dissection
109 bound, bound_type = bounds.upper_bound(n, m)
114 elif best_nature == 'e':
117 elif best_nature == 'p':
122 show_bound = " – %s"
123 sys.stdout.write("<td class=\"%s\">" % tdclass)
124 sys.stdout.write(str(best))
126 sys.stdout.write(show_bound % str(bound))
127 sys.stdout.write("</td>\n")
130 limit = 24 # FIXME: configurable
131 sys.stdout.write("""\
134 <title>Known bounds for stick-dissecting problem</title>
135 <style type="text/css">
137 border: 1px solid black;
138 border-collapse: collapse;
141 background-color: #00ff00;
144 background-color: #aaff00;
147 background-color: #ffff00;
150 background-color: #ff8888;
157 sys.stdout.write("<tr>\n")
158 sys.stdout.write("<th>n \\ m</th>\n")
159 for m in range(2,limit-1):
160 sys.stdout.write("<th>%d</th>\n" % m)
161 sys.stdout.write("</tr>\n")
162 for n in range(2,limit):
163 sys.stdout.write("<tr>\n")
164 sys.stdout.write("<th>%d</th>\n" % n)
167 sys.stdout.write("</tr>\n")
168 sys.stdout.write("</table>\n")
169 sys.stdout.write("</body></html>\n")
171 if __name__ == "__main__":