import re, math
def fmt(string): ## string.format(**vars()) using tags {expression!format} by CMG Lee
def f(tag): i_sep = tag.rfind('!'); return (re.sub('\.0$', '', str(eval(tag[1:-1])))
if (i_sep < 0) else ('{:%s}' % tag[i_sep + 1:-1]).format(eval(tag[1:i_sep])))
return (re.sub(r'(?<!{){[^{}]+}', lambda m:f(m.group()), string)
.replace('{{', '{').replace('}}', '}'))
def append(obj, string): return obj.append(fmt(string))
def tabbify(cellss, separator='|'):
cellpadss = [list(rows) + [''] * (len(max(cellss, key=len)) - len(rows)) for rows in cellss]
fmts = ['%%%ds' % (max([len(str(cell)) for cell in cols])) for cols in zip(*cellpadss)]
return '\n'.join([separator.join(fmts) % tuple(rows) for rows in cellpadss])
def format_sign(x): return ('%+d' % (x)).replace('+', '+ ').replace('-','− ')
def scale_x(x): return 50 * x
def scale_y(y): return -y
n_search = 20
cubic_bez_dx = 15
quad_bez_dx = 15
linear_dx = 15
fmt_out = '{id}\
|{root0}|{root1}|{root2}|{max_x}|{max_y}|{min_x}|{min_y}|{inf_x}|{inf_y}|{inf_c}|{inf_m}\
|{cubic_b}|{cubic_c}|{cubic_d}|{quad_b}|{quad_c}\
'
double_dash = '-' * 2
outss = []
outs = []
for i_pass in range(2): ## 0: to find best values, 1: output SVG
id_best = None
if (i_pass == 1):
outss = sorted(outss, key=lambda outs:[max(abs(int(outs[5])), abs(int(outs[7]))),
max(abs(int(outs[1])), abs(int(outs[3])))])
id_best = int(outss[0][0])
print(tabbify([fmt_out.replace('{','').replace('}','').split('|')] + outss))
id = 0
for k2 in range(-n_search, n_search + 1):
for k1 in range(k2 + 1, n_search + 1):
for k0 in range(k1 + 1, n_search + 1):
(root0, root1, root2) = (-k0, -k1, -k2)
if (root0 == 0 or root0 == root1 or
root1 == 0 or root1 == root2 or
root2 == 0 or root2 == root0): continue
cubic_a = 1
cubic_b = k0 + k1 + k2
cubic_c = k0 * k1 + k1 * k2 + k2 * k0
cubic_d = k0 * k1 * k2
quad_a = cubic_a * 3
quad_b = cubic_b * 2
quad_c = cubic_c
linear_a = quad_a * 2
linear_b = quad_b
if (cubic_a == 0 or quad_a == 0 or linear_a == 0 or
cubic_b == 0 or quad_b == 0 or linear_b == 0 or
cubic_c == 0 or quad_c == 0 or
cubic_d == 0): continue
sqrt_disc = (4 * (k0 ** 2 + k1 ** 2 + k2 ** 2 - quad_c)) ** 0.5
if ((quad_b + sqrt_disc) % linear_a != 0 or
(quad_b - sqrt_disc) % linear_a != 0): continue
(max_x, min_x) = [(-quad_b + sign * sqrt_disc) / linear_a for sign in (-1,1)]
quad_bez_x1 = inf_x = (max_x + min_x) / 2
linear_x0 = inf_x - linear_dx
linear_x1 = inf_x + linear_dx
cubic_bez_x0 = inf_x - cubic_bez_dx
cubic_bez_x3 = inf_x + cubic_bez_dx
(inf_y, max_y, min_y, cubic_bez_y0, cubic_bez_y3) = [
cubic_a * x ** 3 + cubic_b * x ** 2 + cubic_c * x + cubic_d
for x in (inf_x, max_x, min_x, cubic_bez_x0, cubic_bez_x3)]
quad_bez_x0 = inf_x - quad_bez_dx
quad_bez_x2 = inf_x + quad_bez_dx
(inf_m, quad_bez_y0, quad_bez_y2, cubic_bez_m0, cubic_bez_m3) = [
quad_a * x ** 2 + quad_b * x + quad_c for x in
(inf_x, quad_bez_x0, quad_bez_x2, cubic_bez_x0, cubic_bez_x3)]
inf_c = inf_y - inf_m * inf_x
quad_bez_y1 = ((linear_a * quad_bez_x0 + quad_b) * (quad_bez_x1 - quad_bez_x0) + quad_bez_y0)
cubic_bez_x1 = (3 * inf_x - cubic_bez_dx) // 3 ## not sure how to get this
cubic_bez_x2 = inf_x + (inf_x - cubic_bez_x1)
cubic_bez_y1 = cubic_bez_m0 * (cubic_bez_x1 - cubic_bez_x0) + cubic_bez_y0
cubic_bez_y2 = cubic_bez_m3 * (cubic_bez_x2 - cubic_bez_x3) + cubic_bez_y3
if (id == id_best):
path_cubic = fmt('''M {scale_x(cubic_bez_x0)},{scale_y(cubic_bez_y0)} C\
{scale_x(cubic_bez_x1)},{scale_y(cubic_bez_y1)}\
{scale_x(cubic_bez_x2)},{scale_y(cubic_bez_y2)}\
{scale_x(cubic_bez_x3)},{scale_y(cubic_bez_y3)}''')
path_quad = fmt('''M {scale_x(quad_bez_x0)},{scale_y(quad_bez_y0)} Q\
{scale_x(quad_bez_x1)},{scale_y(quad_bez_y1)}\
{scale_x(quad_bez_x2)},{scale_y(quad_bez_y2)}''')
path_linear = fmt('''M {scale_x(linear_x0)},{scale_y(linear_x0 * linear_a + linear_b)} L\
{scale_x(linear_x1)},{scale_y(linear_x1 * linear_a + linear_b)}''')
path_tangent = fmt('''M {scale_x(float(min_y - inf_c) / inf_m)!.0f},{scale_y(min_y)} L\
{scale_x(float(max_y - inf_c) / inf_m)!.0f},{scale_y(max_y)}''')
append(outs,'''\
<use xlink:href="#axes"/>
<g stroke-width="4">
<g mask="url(#mask_line)">
<path class="line_cubic" d="{path_cubic}"/>
<path class="line_quad" d="{path_quad}" stroke-dasharray="20,5"/>
<path class="line_linear" d="{path_linear}" stroke-dasharray="6,4" stroke-width="6"/>
<path class="line_tangent" d="{path_tangent}" stroke-dasharray="25,5,5,5,5,5"/>
</g>
<g class="line_concav">
<path d="M {scale_x(max_x)},{scale_y(max_y)} V 0
M {scale_x(min_x)},{scale_y(min_y)} V 0
M {scale_x(inf_x)},{scale_y(inf_y)} V 500" stroke-dasharray="20,5,5,5"/>
<path d="M -630 460 Q -630 470 -620 470 H 35 Q 45 470 45 460 M 55 460 Q 55 470 65 470 H 620 Q 630 470 630 460"/>
</g>
</g>
<g stroke-width="8">
<g class="label_cubic">
<text class="equation" x="-70" y="-630"><tspan class="var">f</tspan><tspan dx="0.2ex">(</tspan><tspan class="var">x</tspan><tspan>) = </tspan><tspan class="var">x</tspan><tspan>³ {format_sign(cubic_b)}</tspan><tspan class="var">x</tspan><tspan>² {format_sign(cubic_c)}</tspan><tspan class="var">x</tspan><tspan> {format_sign(cubic_d)}</tspan></text>
<g transform="translate({scale_x(root0)}, 0)"><use xlink:href="#root"/><text x="0.5ex" y="2ex">root ({root0})</text></g>
<g transform="translate({scale_x(root1)}, 0)"><use xlink:href="#root"/><text x="0.5ex" y="2ex">root ({root1})</text></g>
<g transform="translate({scale_x(root2)}, 0)"><use xlink:href="#root"/><text x="-0.5ex" y="2ex" class="end">root ({root2})</text></g>
<g transform="translate({scale_x(max_x)},{scale_y(max_y)})"><use xlink:href="#tp" /><text x="-9ex" y="-0.8ex">turning point, stationary point & local maximum ({max_x}, {max_y})</text></g>
<g transform="translate({scale_x(min_x)},{scale_y(min_y)})"><use xlink:href="#tp" /><text x="5ex" y="2ex" class="end">turning point, stationary point & local minimum ({min_x}, {min_y})</text></g>
<g transform="translate({scale_x(inf_x)},{scale_y(inf_y)})"><use xlink:href="#ip" /><text y="-1ex">falling inflection point ({inf_x}, {inf_y})</text></g>
<!-{double_dash}
<g transform="translate(0 ,{scale_y(cubic_d)})"><use xlink:href="#yi" /><text x="0.5ex">y-intercept ({cubic_d})</text></g>
{double_dash}>
</g>
<g class="label_quad">
<text class="equation" x="-530" y="-260"><tspan class="var">f</tspan><tspan dx="0.2ex" class="var">'</tspan><tspan dx="0.2ex">(</tspan><tspan class="var">x</tspan><tspan>) = {quad_a}</tspan><tspan class="var">x</tspan><tspan>² {format_sign(quad_b)}</tspan><tspan class="var">x</tspan><tspan> {format_sign(quad_c)}</tspan></text>
<g transform="translate({scale_x(max_x)}, 0)"><use xlink:href="#root"/><text x="0.5ex" y="-0.2ex">root ({max_x})</text></g>
<g transform="translate({scale_x(min_x)}, 0)"><use xlink:href="#root"/><text x="-0.3ex" y="-0.2ex" class="end">root ({min_x})</text></g>
<g transform="translate({scale_x(inf_x)},{scale_y(inf_m)})"><use xlink:href="#tp" /><text x="-1ex" y="2ex" class="end"><tspan>turning point, stationary point</tspan><tspan x="-1ex" dy="2ex">& local maximum ({inf_x}, {inf_m})</tspan></text></g>
<use xlink:href="#tp" transform="translate({scale_x(inf_x)},{scale_y(inf_m)})"/>
</g>
<g class="label_linear">
<text class="equation" x="-560" y="120"><tspan class="var">f</tspan><tspan dx="0.2ex" class="var">''</tspan><tspan dx="0.2ex">(</tspan><tspan class="var">x</tspan><tspan>) = {linear_a}</tspan><tspan class="var">x</tspan><tspan> {format_sign(linear_b)}</tspan></text>
<g transform="translate({scale_x(inf_x)}, 0)"><use xlink:href="#root"/><text x="-0.5ex" y="-0.2ex" class="end">root ({inf_x})</text></g>
</g>
<g class="label_concav">
<text x="-295" y="505"><tspan class="var">f</tspan><tspan dx="0.2ex">(</tspan><tspan class="var">x</tspan><tspan>) curve concave (downwards)</tspan></text>
<text x="345" y="505"><tspan class="var">f</tspan><tspan dx="0.2ex">(</tspan><tspan class="var">x</tspan><tspan>) convex (downwards)</tspan></text>
</g>
<g class="label_tangent">
<g transform="translate(-140,-860)"><text><tspan>tangent at inflection point:</tspan><tspan x="15" dy="2ex" class="var">y</tspan><tspan> = -147</tspan><tspan class="var">x</tspan><tspan> + 433</tspan></text></g>
</g>
</g>
''')
outss.append(fmt(fmt_out).split('|'))
id += 1
out_p = fmt('width="100%" height="100%" viewBox="-640 -1024 1280 1536"')
## Compile everything into an .svg file
myself = open(__file__, 'r').read() ## the contents of this very file
file_out = open(__file__[:__file__.rfind('.')] + '.svg', 'w') ## *.* -> *.svg
try: ## use try/finally so that file is closed even if write fails
file_out.write('''<?xml version="1.0" encoding="utf-8"?><!%s
%s%s%s\n%s%s''' % ('-' + '-', ## because SVG comments cannot have 2 consecutive '-'s
myself[ : myself.find('width',myself.find('<svg'))], ## assume width specified before height/viewBox
out_p, ## replace SVG width/height/viewBox with {out_p} & dynamic SVG block with {outs} contents
myself[myself.find('>',myself.find('<svg')) : myself.find('\n',myself.find('BEGIN_'+'DYNAMIC_SVG'))],
'\n'.join(outs), myself[myself.rfind('\n',0,myself.find('END_'+'DYNAMIC_SVG')) : ]))
finally:
file_out.close()
## SVG-Python near-polyglot framework version 2 by CMG Lee (Feb 2016) -->