import common
from math import *
import array
import sys
import random
import elastfe # for arrow

import Numeric as N
import numarray.linear_algebra as la

def eps_prologue(x0, y0, x1, y1, draw_box = False):
    print '%!PS-Adobe-3.0 EPSF'
    print '%%BoundingBox:', x0, y0, x1, y1
    print '%%EndComments'
    print '%%EndProlog'
    print '%%Page: 1 1'
    if draw_box:
        print x0, y0, 'moveto', x0, y1, 'lineto', x1, y1, 'lineto', x1, y0, 'lineto closepath stroke'

def eps_trailer():
    print '%%EOF'

# One step of 4th-order Runge-Kutta numerical integration - update y in place
def rk4(y, dydx, x, h, derivs):
    hh = h * .5
    h6 = h * (1./6)
    xh = x + hh
    yt = []
    for i in range(len(y)):
	yt.append(y[i] + hh * dydx[i])
    dyt = derivs(xh, yt)
    for i in range(len(y)):
	yt[i] = y[i] + hh * dyt[i]
    dym = derivs(xh, yt)
    for i in range(len(y)):
	yt[i] = y[i] + h * dym[i]
	dym[i] += dyt[i]
    dyt = derivs(x + h, yt)
    for i in range(len(y)):
	y[i] += h6 * (dydx[i] + dyt[i] + 2 * dym[i])

def run_elastica_half(sp, k0, lam1, lam2, n):
    def mec_derivs(x, ys):
	th, k = ys[2], ys[3]
	dx, dy = cos(th), sin(th)
	return [dx, dy, k, lam1 * dx + lam2 * dy, k * k]
    ys = [0, 0, 0, k0, 0]
    xyk = [(ys[0], ys[1], ys[3])]
    n = max(1, int(sp * n))
    h = float(sp) / n
    s = 0
    for i in range(n):
	dydx = mec_derivs(s, ys)
	rk4(ys, dydx, s, h, mec_derivs)
	xyk.append((ys[0], ys[1], ys[3]))
    return xyk, ys[2], ys[4]

def run_elastica(sm, sp, k0, lam1, lam2, n = 100):
    xykm, thm, costm = run_elastica_half(-sm, k0, -lam1, lam2, n)
    xykp, thp, costp = run_elastica_half(sp, k0, lam1, lam2, n)
    xyk = []
    for i in range(1, len(xykm)):
	x, y, k = xykm[i]
	xyk.append((-x, y, k))
    xyk.reverse()
    xyk.extend(xykp)
    x = xyk[-1][0] - xyk[0][0]
    y = xyk[-1][1] - xyk[0][1]
    th = thm + thp
    sth, cth = sin(thm), cos(thm)
    x, y = x * cth - y * sth, x * sth + y * cth
    result = []
    maxk = 0
    for xt, yt, kt in xyk:
        maxk = max(maxk, kt)
        result.append((xt, yt, kt))
    cost = costm + costp
    return result, cost, x, y, th

def run_mec_cos_pos(k, lam1, lam2, n = 1000):
    cost = 0
    th = 0
    x = 0
    y = 0
    dt = 1.0 / n
    result = [(0, 0)]
    for i in range(n):
        k1 = lam1 * cos(th) + lam2 * sin(th)

        cost += dt * k * k
        x += dt * cos(th)
        y += dt * sin(th)
        th += dt * k

        k += dt * k1
        result.append((x, y))
    return result, cost, x, y, th

def run_mec_cos(k, lam1, lam2, n = 1000):
    resp, costp, xp, yp, thp = run_mec_cos_pos(k*.5, lam1*.25, lam2*.25, n)
    resm, costm, xm, ym, thm = run_mec_cos_pos(k*.5, lam1*-.25, lam2*.25, n)
    cost = (costp + costm) * .5
    x, y = xp + xm, yp - ym
    th = thp + thm
    sth, cth = .5 * sin(thm), .5 * cos(thm)
    x, y = x * cth - y * sth, x * sth + y * cth
    result = []
    for i in range(1, n):
        xt, yt = resm[n - i - 1]
        result.append((-xt * cth - yt * sth, -xt * sth + yt * cth))
    for i in range(n):
        xt, yt = resp[i]
        result.append((xt * cth - yt * sth, xt * sth + yt * cth))
    return result, cost, x, y, th

def cross_prod(a, b):
    return [a[1] * b[2] - a[2] * b[1],
            a[2] * b[0] - a[0] * b[2],
            a[0] * b[1] - a[1] * b[0]]

def solve_mec(constraint_fnl):
    delta = 1e-3
    params = [pi, 0, 0]
    for i in range(20):
        k, lam1, lam2 = params
        xys, cost, x, y, th = run_elastica(-.5, .5, k, lam1, lam2)
        #print i * .05, 'setgray'
        #plot(xys)
        c1c, c2c, costc = constraint_fnl(cost, x, y, th)
        print '% constraint_fnl =', c1c, c2c, 'cost =', costc

        dc1s = []
        dc2s = []
        for j in range(len(params)):
            params1 = N.array(params)
            params1[j] += delta
            k, lam1, lam2 = params1
            xys, cost, x, y, th = run_elastica(-.5, .5, k, lam1, lam2)
            c1p, c2p, costp = constraint_fnl(cost, x, y, th)
            params1 = N.array(params)
            params1[j] -= delta
            k, lam1, lam2 = params1
            xys, cost, x, y, th = run_elastica(-.5, .5, k, lam1, lam2)
            c1m, c2m, costm = constraint_fnl(cost, x, y, th)
            dc1s.append((c1p - c1m) / (2 * delta))
            dc2s.append((c2p - c2m) / (2 * delta))
        xp = cross_prod(dc1s, dc2s)
        xp = N.divide(xp, sqrt(N.dot(xp, xp))) # Normalize to unit length

        print '% dc1s =', dc1s
        print '% dc2s =', dc2s
        print '% xp =', xp
        
        # Compute second derivative wrt orthogonal vec
        params1 = N.array(params)
        for j in range(len(params)):
            params1[j] += delta * xp[j]
        k, lam1, lam2 = params1
        xys, cost, x, y, th = run_elastica(-.5, .5, k, lam1, lam2)
        c1p, c2p, costp = constraint_fnl(cost, x, y, th)
        print '% constraint_fnl+ =', c1p, c2p, 'cost =', costp
        params1 = N.array(params)
        for j in range(len(params)):
            params1[j] -= delta * xp[j]
        k, lam1, lam2 = params1
        xys, cost, x, y, th = run_elastica(-.5, .5, k, lam1, lam2)
        c1m, c2m, costm = constraint_fnl(cost, x, y, th)
        print '% constraint_fnl- =', c1m, c2m, 'cost =', costm
        d2cost = (costp + costm - 2 * costc) / (delta * delta)
        dcost = (costp - costm) / (2 * delta)

        print '% dcost =', dcost, 'd2cost =', d2cost
        if d2cost < 0: d2cost = .1
        # Make Jacobian matrix to invert
        jm = N.array([dc1s, dc2s, [x * d2cost for x in xp]])
        #print jm
        ji = la.inverse(jm)
        #print ji

        dp = N.dot(ji, [c1c, c2c, dcost])
        print '% dp =', dp
        print '% [right]=', [c1c, c2c, dcost]
        params -= dp * .1
        print '%', params
        sys.stdout.flush()
    return params

def solve_mec_3constr(constraint_fnl, n = 30, initparams = None):
    delta = 1e-3
    if initparams:
        params = N.array(initparams)
    else:
        params = [3.14, 0, 0]
    for i in range(n):
        k, lam1, lam2 = params
        xys, cost, x, y, th = run_elastica(-.5, .5, k, lam1, lam2)
        c1c, c2c, c3c = constraint_fnl(cost, x, y, th)
        print '% constraint_fnl =', c1c, c2c, c3c

        dc1s = []
        dc2s = []
        dc3s = []
        for j in range(len(params)):
            params1 = N.array(params)
            params1[j] += delta
            k, lam1, lam2 = params1
            xys, cost, x, y, th = run_elastica(-.5, .5, k, lam1, lam2)
            c1p, c2p, c3p = constraint_fnl(cost, x, y, th)
            params1 = N.array(params)
            params1[j] -= delta
            k, lam1, lam2 = params1
            xys, cost, x, y, th = run_elastica(-.5, .5, k, lam1, lam2)
            c1m, c2m, c3m = constraint_fnl(cost, x, y, th)
            dc1s.append((c1p - c1m) / (2 * delta))
            dc2s.append((c2p - c2m) / (2 * delta))
            dc3s.append((c3p - c3m) / (2 * delta))

        # Make Jacobian matrix to invert
        jm = N.array([dc1s, dc2s, dc3s])
        #print jm
        ji = la.inverse(jm)

        dp = N.dot(ji, [c1c, c2c, c3c])
        if i < n/2: scale = .25
        else: scale = 1
        params -= scale * dp
        print '%', params
    return params

def mk_ths_fnl(th0, th1):
    def fnl(cost, x, y, th):
        actual_th0 = atan2(y, x)
        actual_th1 = th - actual_th0
        print '% x =', x, 'y =', y, 'hypot =', hypot(x, y)
        return th0 - actual_th0, th1 - actual_th1, cost
    return fnl

def mk_y_fnl(th0, th1, ytarg):
    def fnl(cost, x, y, th):
        actual_th0 = atan2(y, x)
        actual_th1 = th - actual_th0
        return th0 - actual_th0, th1 - actual_th1, ytarg - hypot(x, y)
    return fnl

def solve_mec_nested_inner(th0, th1, y, fnl):
    innerfnl = mk_y_fnl(th0, th1, y)
    params = [th0 + th1, 1e-6, 1e-6]
    params = solve_mec_3constr(innerfnl, 10, params)
    k, lam1, lam2 = params
    xys, cost, x, y, th = run_elastica(-.5, .5, k, lam1, lam2, 100)
    c1, c2, c3 = fnl(cost, x, y, th)
    return c3, params

# Solve (th0, th1) mec to minimize fnl; use solve_mec_3constr as inner loop
# Use golden section search
def solve_mec_nested(th0, th1, fnl):
    R = .61803399
    C = 1 - R
    ax, cx = .6, .85
    bx = .5 * (ax + cx)

    x0, x3 = ax, cx
    if abs(cx - bx) > abs(bx - ax):
        x1, x2 = bx, bx + C * (cx - bx)
    else:
        x1, x2 = bx - C * (bx - ax), bx
    f1, p = solve_mec_nested_inner(th0, th1, x1, fnl)
    f2, p = solve_mec_nested_inner(th0, th1, x2, fnl)
    for i in range(10):
        print '%', x0, x1, x2, x3, ':', f1, f2
        if f2 < f1:
            x0, x1, x2 = x1, x2, R * x2 + C * x3
            f1 = f2
            f2, p = solve_mec_nested_inner(th0, th1, x2, fnl)
        else:
            x1, x2, x3 = R * x1 + C * x0, x1, x2
            f2 = f1
            f1, p = solve_mec_nested_inner(th0, th1, x1, fnl)
    if f1 < f2:
        x = x1
    else:
        x = x2
    cc, p = solve_mec_nested_inner(th0, th1, x, fnl)
    return p

def plot(xys):
    cmd = 'moveto'
    for xy in xys:
        print 306 + 200 * xy[0], 396 - 200 * xy[1], cmd
        cmd = 'lineto'
    print 'stroke'

def mec_test():
    th0, th1 = 0, pi / 4
    fnl = mk_ths_fnl(th0, th1)
    params = solve_mec_nested(th0, th1, fnl)
    k, lam1, lam2 = params
    xys, cost, x, y, th = run_mec_cos(k, lam1, lam2, 1000)
    plot(xys)
    print '% run_mec_cos:', cost, x, y, th
    print '1 0 0 setrgbcolor'
    xys, cost, x, y, th = run_elastica(-.5, .5, k, lam1, lam2)
    print '%', xys
    plot(xys)
    print '% run_elastica:', cost, x, y, th
    print 'showpage'
    print '%', params

def lenfig():
    print '306 720 translate'
    th0, th1 = pi / 2, pi / 2
    for i in range(1, 10):
	y = .1 * i + .003
	fnl = mk_y_fnl(th0, th1, y)
	params = solve_mec_3constr(fnl)
	k, lam1, lam2 = params
	print 'gsave 0.5 dup scale -306 -396 translate'
	xys, cost, x, y, th = run_elastica(-2, 2, k, lam1, lam2, 100)
        print 'gsave [2] 0 setdash'
	plot(xys)
        print 'grestore'
	xys, cost, x, y, th = run_elastica(-.5, .5, k, lam1, lam2, 100)
	plot(xys)
	print 'grestore'
        print '% y =', y, 'params =', params
        if lam2 < 0:
            mymaxk = k
        else:
            mymaxk = sqrt(k * k + 4 * lam2)
        lam = abs(lam2) / (mymaxk * mymaxk)
        print '-200 0 moveto /Symbol 12 selectfont (l) show'
        print '/Times-Roman 12 selectfont ( = %.4g) show' % lam
	print '0 -70 translate'
    print 'showpage'

def lenplot(figno, th0, th1):
    result = []
    if figno in (1, 2):
	imax = 181
    elif figno == 3:
	imax = 191
    for i in range(1, imax):
        yy = .005 * i
        if figno in (1, 2) and i == 127:
            yy = 2 / pi
	if figno == 3 and i == 165:
	    yy = .8270
	fnl = mk_y_fnl(th0, th1, yy)
	params = solve_mec_3constr(fnl)
	k, lam1, lam2 = params
        xys, cost, x0, y0, th = run_elastica(-.5, .5, k, lam1, lam2, 100)
        if lam2 < 0:
            mymaxk = k
        else:
            mymaxk = sqrt(k * k + 4 * lam2)
        lam = abs(lam2) / (mymaxk * mymaxk)
        result.append((yy, lam, cost))
    return result

def lengraph(figno):
    if figno in (1, 2):
        eps_prologue(15, 45, 585, 543)
	th0, th1 = pi / 2, pi / 2
	yscale = 900
	ytic = .05
	ymax = 10
    elif figno == 3:
        eps_prologue(15, 45, 585, 592)
	th0, th1 = pi / 3, pi / 3
	yscale = 500
	ytic = .1
	ymax = 10
    x0 = 42
    xscale = 7.5 * 72
    y0 = 72
    print '/Times-Roman 12 selectfont'
    print '/ss 1.5 def'
    print '/circle { ss 0 moveto currentpoint exch ss sub exch ss 0 360 arc } bind def'
    print '.5 setlinewidth'
    print x0, y0, 'moveto', xscale, '0 rlineto 0', yscale * ytic * ymax, 'rlineto', -xscale, '0 rlineto closepath stroke'
    for i in range(0, 11):
        print x0 + .1 * i * xscale, y0, 'moveto 0 6 rlineto stroke'
        print x0 + .1 * i * xscale, y0 + ytic * ymax * yscale, 'moveto 0 -6 rlineto stroke'
        print x0 + .1 * i * xscale, y0 - 12, 'moveto'
        print '(%.1g) dup stringwidth exch -.5 mul exch rmoveto show' % (.1 * i)
    for i in range(0, 11):
        print x0, y0 + ytic * i * yscale, 'moveto 6 0 rlineto stroke'
        print x0 + xscale, y0 + ytic * i * yscale, 'moveto -6 0 rlineto stroke'
        print x0 - 3, y0 + ytic * i * yscale - 3.5, 'moveto'
        print '(%.2g) dup stringwidth exch neg exch rmoveto show' % (ytic * i)
    print x0 + .25 * xscale, y0 - 24, 'moveto (chord length / arc length) show'
    print x0 - 14, y0 + ytic * ymax * yscale + 10, 'moveto /Symbol 12 selectfont (l) show'
    if figno in (1, 2):
	print x0 + 2 / pi * xscale, y0 - 18, 'moveto'
	print '(2/p) dup stringwidth exch -.5 mul exch rmoveto show'
    print 'gsave [3] 0 setdash'
    print x0, y0 + .25 * yscale, 'moveto', xscale, '0 rlineto stroke'
    if figno == 3:
	print x0, y0 + .5 * yscale, 'moveto', xscale, '0 rlineto stroke'
	xinterest = .81153
	print x0 + xinterest * xscale, y0, 'moveto 0', yscale * .5, 'rlineto stroke'
    print 'grestore'
    print '.75 setlinewidth'
    if 1:
	if figno in (1, 2):
	    costscale = .01 * yscale
	elif figno == 3:
	    costscale = .0187 * yscale
        lenresult = lenplot(figno, th0, th1)
        cmd = 'moveto'
        for x, y, cost in lenresult:
            print x0 + xscale * x, y0 + yscale * y, cmd
            cmd = 'lineto'
        print 'stroke'
	if figno in (2, 3):
            cmd = 'moveto'
            for x, y, cost in lenresult:
                print x0 + xscale * x, y0 + costscale * cost, cmd
                cmd = 'lineto'
            print 'stroke'
            cmd = 'moveto'
            for x, y, cost in lenresult:
                print x0 + xscale * x, y0 + costscale * x * cost, cmd
                cmd = 'lineto'
            print 'stroke'
            print '/Times-Roman 12 selectfont'
	    if figno == 2:
		xlm, ylm = .75, 7
		xls, yls = .42, 15
		xll, yll = .58, 40
	    elif figno == 3:
		xlm, ylm = .6, 3
		xls, yls = .34, 15
		xll, yll = .8, 45
            print '/Symbol 12 selectfont'
            print x0 + xscale * xll, y0 + costscale * yll, 'moveto'
            print '(l) show'
            print x0 + xscale * xlm, y0 + costscale * ylm, 'moveto'
            print '/Times-Roman 12 selectfont'
            print '(MEC energy) show'
            print x0 + xscale * xls, y0 + costscale * yls, 'moveto'
            print '(SIMEC energy) show'
    if figno == 1:
        minis = [(.05, 5, -5),
             (.26, -40, 10),
             (.4569466, -5, -10),
             (.55, 35, 12),
             
             (.6026, -60, 45),
             (.6046, -60, 15),
             (.6066, -60, -15),
             
             (.6213, -22, 10),
             (.6366198, 15, 22),
             (.66, 20, 10),
             (.9, 0, -10)]
    elif figno == 2:
        minis = [(.4569466, -5, -10),
             (.6366198, 15, 22)]
    elif figno == 3:
	minis = [(.741, 55, -10),
		 (.81153, -30, 20),
		 (.8270, 20, 30)]

    for yy, xo, yo in minis:
	fnl = mk_y_fnl(th0, th1, yy)
	params = solve_mec_3constr(fnl)
	k, lam1, lam2 = params
        if lam2 < 0:
            mymaxk = k
        else:
            mymaxk = sqrt(k * k + 4 * lam2)
        lam = abs(lam2) / (mymaxk * mymaxk)
        x = x0 + xscale * yy
        y = y0 + yscale * lam
	print 'gsave %g %g translate circle fill' % (x, y)
        print '%g %g translate 0.15 dup scale' % (xo, yo)
        print '-306 -396 translate'
        print '2 setlinewidth'
        if yy < .6 or yy > .61:
            s = 2
        elif yy == .6046:
            s = 2.8
        else:
            s = 5
	xys, cost, x, y, th = run_elastica(-s, s, k, lam1, lam2, 100)
        print 'gsave [10] 0 setdash'
	plot(xys)
        print 'grestore 6 setlinewidth'
	xys, cost, x, y, th = run_elastica(-.5, .5, k, lam1, lam2, 100)
	plot(xys)
	print 'grestore'
        
    print 'showpage'
    eps_trailer()

def draw_axes(x0, y0, xscale, yscale, xmax, ymax, nx, ny):
    print '.5 setlinewidth'
    print '/Times-Roman 12 selectfont'
    print x0, y0, 'moveto', xscale * xmax, '0 rlineto 0', yscale * ymax, 'rlineto', -xscale * xmax, '0 rlineto closepath stroke'
    xinc = (xmax * xscale * 1.) / nx
    yinc = (ymax * yscale * 1.) / ny
    for i in range(0, nx + 1):
        if i > 0 and i < nx + 1:
            print x0 + xinc * i, y0, 'moveto 0 6 rlineto stroke'
            print x0 + xinc * i, y0 + ymax * yscale, 'moveto 0 -6 rlineto stroke'
        print x0 + xinc * i, y0 - 12, 'moveto'
        print '(%.2g) dup stringwidth exch -.5 mul exch rmoveto show' % ((i * xmax * 1.) / nx)
    for i in range(0, ny + 1):
        if i > 0 and i < ny + 1:
            print x0, y0 + yinc * i, 'moveto 6 0 rlineto stroke'
            print x0 + xmax * xscale, y0 + yinc * i, 'moveto -6 0 rlineto stroke'
        print x0 - 3, y0 + yinc * i - 3.5, 'moveto'
        print '(%.2g) dup stringwidth exch neg exch rmoveto show' % ((i * ymax * 1.) / ny)
    

def mecchord():
    print '%!PS-Adobe-3.0 EPSF'
    x0 = 72
    y0 = 72
    thscale = 150
    chscale = 400
    print '.5 setlinewidth'
    print '/ss 1.5 def'
    print '/circle { ss 0 moveto currentpoint exch ss sub exch ss 0 360 arc } bind def'
    draw_axes(x0, y0, thscale, chscale, 3.2, 1, 16, 10)
    print x0 + 1 * thscale, y0 - 28, 'moveto (angle) show'
    print 'gsave', x0 - 32, y0 + .25 * chscale, 'translate'
    print '90 rotate 0 0 moveto (chordlen / arclen) show'
    print 'grestore'

    cmd = 'moveto'
    for i in range(0, 67):
        s = i * .01
        xys, cost, x, y, th = run_elastica(-s, s, 4, 0, -8, 100)
        #plot(xys)
        if s > 0:
            ch = hypot(x, y) / (2 * s)
        else:
            ch = 1
        print '%', s, x, y, th, ch
        print x0 + thscale * th / 2, y0 + ch * chscale, cmd
        cmd = 'lineto'
    print 'stroke'

    print 'gsave %g %g translate circle fill' % (x0 + thscale * th / 2, y0 + ch * chscale)
    print '-30 15 translate 0.25 dup scale'
    print '-306 -396 translate'
    print '2 setlinewidth'
    plot(xys)
    print 'grestore'

    print 'gsave [2] 0 setdash'
    cmd = 'moveto'
    for i in range(0, 151):
        th = pi * i / 150.
        if th > 0:
            ch = sin(th) / th
        else:
            ch = 1
        print x0 + thscale * th, y0 + ch * chscale, cmd
        cmd = 'lineto'
    print 'stroke'
    print 'grestore'

    k0 = 4 * .4536 / (2 / pi)
    s = pi / (2 * k0)
    xys, cost, x, y, th = run_elastica(-s, s, k0, 0, 0, 100)
    th = pi
    ch = 2 / pi
    print 'gsave %g %g translate circle fill' % (x0 + thscale * th / 2, y0 + ch * chscale)
    print '30 15 translate 0.25 dup scale'
    print '-306 -396 translate'
    print '2 setlinewidth'
    plot(xys)
    print 'grestore'

    print x0 + 1.25 * thscale, y0 + .55 * chscale, 'moveto (MEC) show'
    print x0 + 2.3 * thscale, y0 + .35 * chscale, 'moveto (SIMEC) show'

    print 'showpage'

def trymec(sm, sp):
    xys, thm, cost = run_elastica_half(abs(sm), 0, 1, 0, 10)
    xm, ym, km = xys[-1]
    if sm < 0:
        xm = -xm
        ym = -ym
        thm = -thm
    xys, thp, cost = run_elastica_half(abs(sp), 0, 1, 0, 10)
    xp, yp, kp = xys[-1]
    if sp < 0:
        xp = -xp
        yp = -yp
        thp = -thp
    chth = atan2(yp - ym, xp - xm)
    #print xm, ym, xp, yp, chth, thm, thp
    actual_th0 = chth - thm
    actual_th1 = thp - chth
    return actual_th0, actual_th1

def findmec_old(th0, th1):
    guess_ds = sqrt(6 * (th1 - th0))
    guess_savg = 2 * (th1 + th0) / guess_ds
    sm = .5 * (guess_savg - guess_ds)
    sp = .5 * (guess_savg + guess_ds)
    #sm, sp = th0, th1
    for i in range(1):
        actual_th0, actual_th1 = trymec(sm, sp)
        guess_dth = 1./6 * (sp - sm) ** 2
        guess_avth = .5 * (sp + sm) * (sp - sm)
        guess_th0 = .5 * (guess_avth - guess_dth)
        guess_th1 = .5 * (guess_avth + guess_dth)
        print sm, sp, actual_th0, actual_th1, guess_th0, guess_th1

def mecplots():
    print '2 dup scale'
    print '-153 -296 translate'
    scale = 45
    print '0.25 setlinewidth'
    print 306 - scale * pi/2, 396, 'moveto', 306 + scale * pi/2, 396, 'lineto stroke'
    print 306, 396 - scale * pi/2, 'moveto', 306, 396 + scale * pi/2, 'lineto stroke'
    print '/ss .5 def'
    print '/circle { ss 0 moveto currentpoint exch ss sub exch ss 0 360 arc } bind def'

    tic = .1
    maj = 5
    r0, r1 = 0, 81

    for i in range(r0, r1, maj):
        sm = i * tic
        cmd = 'moveto'
        for j in range(i, r1):
            sp = j * tic + 1e-3
            th0, th1 = trymec(sm, sp)
            print '%', sm, sp, th0, th1
            print 306 + scale * th0, 396 + scale * th1, cmd
            cmd = 'lineto'
        print 'stroke'
    for j in range(r0, r1, maj):
        sp = j * tic + 1e-3
        cmd = 'moveto'
        for i in range(0, j + 1):
            sm = i * tic
            th0, th1 = trymec(sm, sp)
            print '%', sm, sp, th0, th1
            print 306 + scale * th0, 396 + scale * th1, cmd
            cmd = 'lineto'
        print 'stroke'

    for i in range(r0, r1, maj):
        sm = i * tic
        for j in range(i, r1, maj):
            sp = j * tic + 1e-3
            th0, th1 = trymec(sm, sp)
            print 'gsave'
            print 306 + scale * th0, 596 + scale * th1, 'translate'
            print 'circle fill'
            uscale = 3
            xys, thm, cost = run_elastica_half(abs(sm), 0, 1, 0, 100)
            x0, y0 = xys[-1][0], xys[-1][1]
            if sm < 0:
                x0, y0 = -x0, -y0
            xys, thm, cost = run_elastica_half(abs(sp), 0, 1, 0, 100)
            x1, y1 = xys[-1][0], xys[-1][1]
            if sp < 0:
                x1, y1 = -x1, -y1
            cmd = 'moveto'
            for xy in xys:
                print xy[0] * uscale, xy[1] * uscale, cmd
                cmd = 'lineto'
            print 'stroke'
            print '1 0 0 setrgbcolor'
            print x0 * uscale, y0 * uscale, 'moveto'
            print x1 * uscale, y1 * uscale, 'lineto stroke'
            print 'grestore'

    print 'showpage'

def findmec(th0, th1):
    if th0 < 0 and th0 - th1 < .1:
        sm = 5.
        sp = 6.
    else:
        sm = 3.
        sp = 5.
    return findmec_init(th0, th1, sm, sp)

def findmec_init(th0, th1, sm, sp):
    delta = 1e-3
    params = [sm, sp]
    lasterr = 1e6
    for i in range(25):
        sm, sp = params
        ath0, ath1 = trymec(sm, sp)
        c1c, c2c = th0 - ath0, th1 - ath1

        err = c1c * c1c + c2c * c2c
        if 0:
            print '%findmec', sm, sp, ath0, ath1, err
            sys.stdout.flush()

        if err < 1e-9:
            return params
        if err > lasterr:
            return None
        lasterr = err


        dc1s = []
        dc2s = []
        for j in range(len(params)):
            params1 = N.array(params)
            params1[j] += delta
            sm, sp = params1
            ath0, ath1 = trymec(sm, sp)
            c1p, c2p = th0 - ath0, th1 - ath1

            params1 = N.array(params)
            params1[j] -= delta
            sm, sp = params1
            ath0, ath1 = trymec(sm, sp)
            c1m, c2m = th0 - ath0, th1 - ath1

            dc1s.append((c1p - c1m) / (2 * delta))
            dc2s.append((c2p - c2m) / (2 * delta))

        jm = N.array([dc1s, dc2s])
        ji = la.inverse(jm)
        dp = N.dot(ji, [c1c, c2c])

        if i < 4:
            scale = .5
        else:
            scale = 1
        params -= scale * dp
        if params[0] < 0: params[0] = 0.
    return params

def mecrange(figtype):
    scale = 130
    eps_prologue(50, 110, 570, 630)
    print -50, 0, 'translate'
    print '0.5 setlinewidth'
    thlmin, thlmax = -pi/2, 2.4
    thrmin, thrmax = -2.2, pi / 2 + .2
    print 306 + scale * thlmin, 396, 'moveto', 306 + scale * thlmax, 396, 'lineto stroke'
    print 306, 396 + scale * thrmin, 'moveto', 306, 396 + scale * thrmax, 'lineto stroke'

    print 'gsave [2] 0 setdash'
    print 306, 396 + scale * pi / 2, 'moveto'
    print 306 + scale * thlmax, 396 + scale * pi / 2, 'lineto stroke'
    print 306 + scale * thlmin, 396 - scale * pi / 2, 'moveto'
    print 306 + scale * thlmax, 396 - scale * pi / 2, 'lineto stroke'
    print 306 + scale * pi / 2, 396 + scale * thrmin, 'moveto'
    print 306 + scale * pi / 2, 396 + scale * thrmax, 'lineto stroke'
    print 'grestore'

    print 306 + 3, 396 + scale * thrmax - 10, 'moveto'
    print '/Symbol 12 selectfont (q) show'
    print 0, -2, 'rmoveto'
    print '/Times-Italic 9 selectfont (right) show'

    print 306 - 18, 396 + scale * pi / 2 - 4, 'moveto'
    print '/Symbol 12 selectfont (p/2) show'
    print 306 + scale * 2.2, 396 - scale * pi / 2 + 2, 'moveto'
    print '/Symbol 12 selectfont (-p/2) show'

    print 306 + scale * pi/2 + 2, 396 + scale * thrmax - 10, 'moveto'
    print '/Symbol 12 selectfont (p/2) show'

    print 306 + scale * 2.2, 396 + 6, 'moveto'
    print '/Symbol 12 selectfont (q) show'
    print 0, -2, 'rmoveto'
    print '/Times-Italic 9 selectfont (left) show'

    print '/ss 0.8 def'
    print '/circle { ss 0 moveto currentpoint exch ss sub exch ss 0 360 arc } bind def'
    cmd = 'moveto'
    for i in range(0, 201):
        th = (i * .005 - .75 )* pi
        rmin = 1.5
        rmax = 2.5
        for j in range(20):
            r = (rmin + rmax) * .5
            th0 = r * cos(th)
            th1 = r * sin(th)
            if findmec(th0, th1) == None:
                rmax = r
            else:
                rmin = r
        r = (rmin + rmax) * .5
        th0 = r * cos(th)
        th1 = r * sin(th)
        print '%', r, th, th0, th1
        print 306 + scale * th0, 396 + scale * th1, cmd
        cmd = 'lineto'
        sys.stdout.flush()
    print 'stroke'
    sys.stdout.flush()
        
    for i in range(-11, 12):
        for j in range(-11, i + 1):
            th0, th1 = i * .196, j * .196
            print '%', th0, th1
            params = findmec(th0, th1)
            if params != None:
                sm, sp = params
                print 'gsave'
                print 306 + scale * th0, 396 + scale * th1, 'translate'
                uscale = 22
                k0, lam1, lam2 = justify_mec(sm, sp)
                xys, cost, x, y, th = run_elastica(-.5, .5, k0, lam1, lam2)
                cmdm = 'moveto'
                dx = xys[-1][0] - xys[0][0]
                dy = xys[-1][1] - xys[0][1]
                ch = hypot(dx, dy)
                chth = atan2(dy, dx)
                if figtype == 'mecrange':
                    print 'circle fill'
                    s = uscale * sin(chth) / ch
                    c = uscale * cos(chth) / ch
                    h = -xys[0][0] * s + xys[0][1] * c
                    for xy in xys:
                        print xy[0] * c + xy[1] * s, h + xy[0] * s - xy[1] * c, cmdm
                        cmdm = 'lineto'
                elif figtype == 'mecrangek':
                    ds = 1. / (len(xys) - 1)
                    sscale = 13. / ch
                    kscale = 3 * ch
                    print 'gsave .25 setlinewidth'
                    print sscale * -.5, 0, 'moveto', sscale, 0, 'rlineto stroke'
                    print 'grestore'
                    for l in range(len(xys)):
                        print sscale * (ds * l - 0.5), kscale * xys[l][2], cmdm
                        cmdm = 'lineto'
                print 'stroke'
                print 'grestore'
            sys.stdout.flush()
    print 'showpage'
    eps_trailer()

# given sm, sp in [0, 1, 0] mec, return params for -0.5..0.5 segment
def justify_mec(sm, sp):
    sc = (sm + sp) * 0.5
    xys, thc, cost = run_elastica_half(sc, 0, 1, 0, 100)
    #print '% sc =', sc, 'thc =', thc
    k0, lam1, lam2 = xys[-1][2], cos(thc), -sin(thc)
    ds = sp - sm
    k0 *= ds
    lam1 *= ds * ds
    lam2 *= ds * ds
    return [k0, lam1, lam2]

def make_k_lut(step = 1):
    show_ctrs = False
    #print '%!PS'
    #print '/ss 1.5 def'
    #print '/circle { ss 0 moveto currentpoint exch ss sub exch ss 0 360 arc } bind def'
    kss = []
    xss = []
    yss = []
    thss = []
    for j in range(-32, 33, step):
	ks = []
	xs = []
	ys = []
	ths = []
	for i in range(-32, 33, step):
	    th0 = j * .1
	    th1 = i * .1
	    oth0, oth1 = th0, th1
	    if th1 > th0:
		thmul = 1
		ix = 0
		th0, th1 = th1, th0
	    else:
		thmul = -1
		ix = -1
	    params = findmec(th0, th1)
	    if params != None:
                sm, sp = params
                k0, lam1, lam2 = justify_mec(sm, sp)
                xys, cost, x, y, th = run_elastica(-.5, .5, k0, lam1, lam2)
                dx = xys[-1][0] - xys[0][0]
                dy = xys[-1][1] - xys[0][1]
                ch = hypot(dx, dy)
                chth = atan2(dy, dx)
		k = xys[ix][2] * ch
		ks.append(k)
		ix = len(xys) / 2
		s = sin(chth) / ch
		c = cos(chth) / ch
		# Note: these are always 0 due to construction. We cheat a bit
		# and assume that theta = 0 at the midpoint as well, rather
		# than sampling it; if we used a point other than the exact
		# midpoint, we'd need to improve that.
		x0 = xys[0][0] * c + xys[0][1] * s
		y0 = xys[0][0] * s - xys[0][1] * c
		x, y = xys[ix][0], xys[ix][1]
		x, y = x * c + y * s, x * s - y * c
		xs.append(x - x0)
		ys.append(y - y0)
		ths.append(chth * thmul)
		#print '%', x, y
		if show_ctrs:
		    xo, yo = 306 + 15 * i, 396 + 15 * j
		    print xo, yo, 'moveto', 12, 0, 'rlineto stroke'
		    print 'gsave', xo + 12 * (x - x0), yo + 12 * (y - y0), 'translate circle fill'
		    print chth * thmul * 180 / pi, 'rotate', -4, 0, 'moveto', 8, 0, 'rlineto stroke'
		    print 'grestore'
	    else:
		ks.append(None)
		xs.append(None)
		ys.append(None)
		ths.append(None)
	#print '%', ks
	sys.stdout.flush()
	kss.append(ks)
	xss.append(xs)
	yss.append(ys)
	thss.append(ths)
    #plot_lut(thss, .5)
    #print 'showpage'
    print 'import lut_eval'
    print 'mec = lut_eval.Lut()'
    print 'mec.scale = 10.0'
    print 'mec.offset = 32.0'
    print 'mec.kss = ', kss
    print 'mec.xss = ', xss
    print 'mec.yss = ', yss
    print 'mec.thss = ', thss

def plot_lut(lut, scale = .1):
    ny = len(lut)
    nx = len(lut[0])
    dx = 7.5 * 72 / nx
    dy = 7.5 * 72 / ny
    for j in range(ny):
	for i in range(nx):
	    v = lut[j][i]
	    if not v is None:
		if v > 0:
		    print .5, min(1.0, .5 + scale * v), .5, 'setrgbcolor'
		else:
		    print .5, max(0.0, .5 + scale * v), 'dup setrgbcolor'
		print 36 + dx * i, 36 + dy * j, 'moveto'
		print dx, 0, 'rlineto', 0, dy, 'rlineto', -dx, 0, 'rlineto fill'

def quadrant():
    eps_prologue(306 - 260, 396 - 260, 306 + 260, 396 + 260)
    print '/circle { ss 0 moveto currentpoint exch ss sub exch ss 0 360 arc } bind def'
    common.setlinewidth()
    print 'gsave 306 396 translate'
    n = 3
    th0 = pi / n
    th1 = pi / n
    scale = 530
    params = findmec(th0, th1)
    sm, sp = params
    k0, lam1, lam2 = justify_mec(sm, sp)
    xys, cost, xh, yh, thh = run_elastica(-0, .5, k0, lam1, lam2)
    xys, cost, xf, yf, th = run_elastica(-.5, .5, k0, lam1, lam2)
    print xh, yh, thh
    print xf, yf, th
    r_inner = xh / sin(pi / n)
    r_outer = xh / tan(pi / n) + yh
    print '% r_outer / r_inner =', r_outer / r_inner
    x0 = 0
    y0 = -r_outer
    cmd = 'moveto'
    for qdrnt in range(n - 1, -1, -1):
        rot = (1 + 2 * qdrnt) * (pi / n)
        cth, sth = scale * cos(rot), scale * sin(rot)
        for xy in xys:
            print '%', xy
            x, y = xy[0] + x0, xy[1] + y0
            xt = cth * x + sth * y
            yt = sth * x - cth * y
            print xt, yt, cmd
            cmd = 'lineto'
    print 'closepath stroke'
    print '/ss', 2 * common.linescale, 'def'
    for qdrnt in range(n - 1, -1, -1):
        rot = (1 + 2 * qdrnt) * (pi / n)
        cth, sth = scale * cos(rot), scale * sin(rot)
        x, y = xys[0][0] + x0, xys[0][1] + y0
        xt = cth * x + sth * y
        yt = sth * x - cth * y
        print 'gsave', xt, yt, 'translate circle fill grestore'
    common.setlinewidth(.5)
    print '/ss', scale * r_inner, 'def circle stroke'
    #print '/ss', scale * r_outer, 'def circle stroke'
    print 'grestore'

def runaway():
    eps_prologue(229, 394, 383, 617, False)
    common.setlinewidth()
    for yy in (0.5, 0.3, 0.151):
	fnl = mk_y_fnl(0.6 * pi, 0.6 * pi, yy)
	params = solve_mec_3constr(fnl)
	k, lam1, lam2 = params
	xys, cost, x, y, th = run_elastica(-0.5, 0.5, k, lam1, lam2, 100)
	scale = -0.2 / xys[0][0]
	print '% xy', x, y, xys[0], xys[-1]
	newxys = [(scale * xy[0], scale * (xy[1] - xys[0][1])) for xy in xys]
	plot(newxys)
    elastfe.arrow(306, 545, 50, 90, 5 * common.linescale, 6 * common.linescale)
    print 'showpage'
    eps_trailer()

def rect_elast():
    print '%!PS-Adobe-3.0 EPSF'
    common.setlinewidth(2)
    xys, cost, x, y, th = run_elastica(-3.4, 3.4, 0, 5, 0, 100)
    xys = [(-xy[1], xy[0]) for xy in xys]
    plot(xys)
    print 'showpage'
    eps_trailer()

def multi_mec():
    print '%!PS-Adobe-3.0 EPSF'
    print '/Times-Roman 12 selectfont'
    th0, th1 = pi / 4, pi / 4
    uscale = 0.5
    for i in range(9):
	print 'gsave'
	print -180 + 140 * (i % 3), 280 - 130 * (i / 3), 'translate'
	sm = 3.
	sp = sm + i * 1.0
	#sp = (5., 9., 11., 17., 19., 23.)[i]
	if 1:
	 sm, sp = ((3., 5.),
		  (5., 13.),
		  (3., 9.),
		  (5., 15.),
		  (3., 11.),
		  (5., 19.),
		  (3., 17.),
		  (3., 21.),
		  (5., 23.),
		  (3., 17.))[i]
	osm, osp = sm, sp
	result = findmec_init(th0, th1, sm, sp)
	if result:
	    sm, sp = result
	    k0, lam1, lam2 = justify_mec(sm, sp)
	    xys, cost, x, y, th = run_elastica(-.5, .5, k0, lam1, lam2)
	    dx = xys[-1][0] - xys[0][0]
	    dy = xys[-1][1] - xys[0][1]
	    ch = hypot(dx, dy)
	    chth = atan2(dy, dx) + pi
	    s = uscale * sin(chth) / ch
	    c = uscale * cos(chth) / ch
	    xo = -xys[0][0] * c - xys[0][1] * s
	    yo = -xys[0][0] * s + xys[0][1] * c
	    newxy = [(xo + xy[0] * c + xy[1] * s, 
		      yo + xy[0] * s - xy[1] * c) for xy in xys]
	    plot(newxy)
	    print '.75 setlinewidth 0 0 .5 setrgbcolor'
	    print 306 - 100 - 3, 396 - 3, 'moveto -8 -8 rlineto stroke'
	    print 306 + 3, 396 - 3, 'moveto 8 -8 rlineto stroke'
	    #print '310 400 moveto (%5.2f, %5.2f) show' % (osm, osp)
	print 'grestore'
    print 'showpage'
    eps_trailer()

import sys
figname, args, flags = common.parse_cmdline()
if figname in ('lengraph1', 'lengraph2', 'lengraph3'):
    lengraph(int(figname[-1]))
    #mec_test()
    #lenfig()
elif figname == 'mecchord':
    mecchord()
elif figname in ('mecrange', 'mecrangek'):
    mecrange(figname)
elif figname == 'mecplots':
    mecplots()
elif figname == 'findmec':
    findmec(-.1, -.1)
    findmec(-.2, -.2)
elif figname == 'quadrant':
    quadrant()
elif figname == 'make_k_lut':
    make_k_lut(1)
elif figname == 'runaway':
    runaway()
elif figname == 'rect_elast':
    rect_elast()
elif figname == 'multi_mec':
    multi_mec()
