# include "stdlib.pxi"
# include "math.pxi"
# cython: cdivision=True

cdef extern from "math.h":
	double fabs(double x) nogil
	double pow(double y, double x) nogil
	double hypot(double y, double x) nogil

cdef extern from "stdlib.h":
	long int labs(long int a) nogil

cdef extern from "gpklib.h":
	float **float2alloc(int a, int b) nogil
	void float2free(float **x) nogil

import cython
# import numpy
from gmisclib import die
import numpy
cimport numpy
cimport libc.stdlib
ctypedef numpy.float_t FTYPE
ctypedef numpy.int32_t ITYPE

S = '. '

cdef struct node
ctypedef node node_t

cdef struct node:
	int i, j
	double d
	double gdist
	node_t *best


cdef class slice_c:
	cdef int i, j, ie, je, istep, jstep
	cdef double xc, yc, cc
	cdef node *n

	def __init__(self, int nx, int ny, double slx1, double sly1, double slx2, double sly2):
		self.i = <int>(slx1+0.5)
		self.j = <int>(sly1+0.5)
		self.ie = <int>(slx2+0.5)
		self.je = <int>(sly2+0.5)
		self.xc = sly2-sly1
		self.yc = slx1-slx2
		self.cc = -self.xc*slx1 - self.yc*sly1
		if self.i >= self.ie:
			self.istep = -1
		else:
			self.istep = 1
		if self.j >= self.je:
			self.jstep = -1
		else:
			self.jstep = 1
		assert self.istep!=0 and self.jstep!=0
		assert self.dist(slx1, sly1) <= 0.01*hypot(self.xc, self.yc)
		assert self.dist(slx2, sly2) <= 0.01*hypot(self.xc, self.yc)
		assert self.dist(self.i, self.j) <= hypot(self.xc, self.yc)
		assert self.dist(self.ie, self.je) <= hypot(self.xc, self.yc)
		# print 'slice set up: ij=%d,%d e=%d,%d step=%d,%d' % (self.i, self.j, self.ie, self.je, self.istep, self.jstep)

	cdef int ok(self):
		cdef double EPS = 1e-8
		return (self.i-self.ie)*self.istep <= EPS and (self.j-self.je)*self.jstep <= EPS

	cdef int next(self):
		if self.istep == 0:
			self.j += self.jstep
		elif self.jstep == 0:
			self.i += self.istep
		else:
			if self.dist(self.i-1, self.j) < self.dist(self.i, self.j+1):
				self.i += self.istep
			else:
				self.j += self.jstep
		return 1


	cdef double dist(self, double i, double j) nogil:
		return fabs(i*self.xc + j*self.yc + self.cc)



cdef double _slmf(int nx, int ny, double r):
	return r * (nx*nx+ny*ny) / <double>ny


cdef slice_c slicemaker(int nx, int ny, int depth, int nsteps):
	assert 0 <= depth < nsteps

	if nsteps==1 or depth >= nsteps-1:
		# print 'Slicemaker: nsteps=%d depth=%d' % (nsteps, depth)
		return slice_c(nx, ny, 0.0, 0.0, 0.0, 0.0)
	cdef double r = <double>(nsteps-depth-1) / <double>nsteps

	slx1 = _slmf(ny, nx, r)
	sly1 = 0.0
	if slx1 > nx:
		slx1 = nx
		sly1 = ny - _slmf(nx, ny, 1-r)
	sly2 = _slmf(nx, ny, r)
	slx2 = 0.0
	if sly2 > ny:
		sly2 = ny
		slx2 = nx - _slmf(ny, nx, 1-r)
	# assert 0 <= slx1 <= nx, "slx1=%.3f vs nx=%d r=%.3f depth=%d nsteps=%d" % (slx1, nx, r, depth, nsteps)
	# assert 0 <= slx2 <= nx, "slx2=%.3f vs nx=%d r=%.3f depth=%d nsteps=%d" % (slx2, nx, r, depth, nsteps)
	# assert 0 <= sly1 <= ny, "sly1=%.3f vs ny=%d r=%.3f depth=%d nsteps=%d" % (sly1, ny, r, depth, nsteps)
	# assert 0 <= sly2 <= ny, "sly2=%.3f vs ny=%d r=%.3f depth=%d nsteps=%d" % (sly2, ny, r, depth, nsteps)

	# print 'Slicemaker: nsteps=%d depth=%d: slice_c(%.1f,%.1f,%.1f,%.1f)' % (nsteps, depth, slx1, sly1, slx2, sly2)
	return slice_c(nx, ny, slx1, sly1, slx2, sly2)




cdef int inbeam(node *tgt, node *n, int nx, int ny):
	cdef double denom = hypot(nx, ny)
	cdef double along = ((n.i-tgt.i)*nx + (n.j-tgt.j)*ny) / denom
	cdef double npi = n.i - along*nx/denom
	cdef double npj = n.j - along*ny/denom
	return hypot(tgt.i-npi, tgt.j-npj) < 1.42*fabs(along)


cdef class state_c:
	cdef node *nodes
	cdef int nx, ny, nparams
	cdef double alpha, beta
	cdef double BIG
	cdef float **x, **y

	def __cinit__(self, numpy.ndarray[FTYPE, ndim=2] x, numpy.ndarray[FTYPE, ndim=2] y,
				double alpha, double beta):
		self.nx = x.shape[0]
		self.ny = y.shape[0]
		self.nodes = <node_t *>libc.stdlib.malloc(cython.sizeof(node)*self.nx*self.ny)
		self.x = float2alloc(x.shape[1], x.shape[0])
		self.y = float2alloc(y.shape[1], y.shape[0])

	def __dealloc__(self):
		libc.stdlib.free(self.nodes)
		float2free(self.x)
		float2free(self.y)

	cdef node_t * gn(self, int i, int j) nogil:
		return & self.nodes[ i + self.nx*j ]

	def __init__(self, numpy.ndarray[FTYPE, ndim=2] x, numpy.ndarray[FTYPE, ndim=2] y,
				double alpha, double beta):
		assert x.shape[1]==y.shape[1]
		self.nparams = x.shape[1]
		cdef int i, j
		for i from 0 <= i < self.nparams:
			for j from 0 <= j < self.nx:
				self.x[i][j] = x[j,i]
			for j from 0 <= j < self.ny:
				self.y[i][j] = y[j,i]
		cdef node_t *n
		with nogil:
			for i from 0 <= i < self.nx*self.ny:
				n = & self.nodes[i]
				n.i = i % self.nx
				n.j = i // self.nx
				n.best = <node_t *>0
				n.d = -1.0
				n.gdist = -1.0
		# self.gn(0, 0).gdist = 0.0
		self.alpha = alpha
		self.beta = beta
		self.BIG = 1e30
		

	cdef double get(self, node_t *n) nogil:
		cdef float **x, **y
		cdef double s
		cdef int i, j, k
		if n.d < 0.0:
			s = 0.0
			x = self.x
			y = self.y
			i = n.i
			j = n.j
			for k from 0 <= k < self.nparams:
				s += pow(fabs(x[k][i]-y[k][j]), self.alpha)
			n.d = pow(s/self.nparams, self.beta)
		return n.d


	cdef double search(self, node_t *n) nogil:
		if n.gdist >= 0:
			return n.gdist
		elif n.i==0 and n.j==0:
			n.best = <node_t *>0
			n.gdist = 0.0
			return n.gdist

		cdef double gi, gj, gij
		cdef node_t *nij, *ni, *nj
		if n.i > 0:
			ni = self.gn(n.i-1, n.j)
			gi = self.search(ni) + self.get(ni)
		else:
			gi = self.BIG
			ni = <node_t *> 0

		if n.j > 0:
			nj = self.gn(n.i, n.j-1)
			gj = self.search(nj) + self.get(nj)
		else:
			gj = self.BIG
			nj = <node_t *> 0

		if n.i>0 and n.j>0:
			nij = self.gn(n.i-1, n.j-1)
			gij = self.search(nij) + self.get(nij)
		else:
			gij = self.BIG
			nij = <node_t *> 0

		cdef double gbest = self.BIG
		if gij <= gi and gij <= gj:
			n.best = nij
			n.gdist = gij
			# print 'n=', n.i, n.j, '->', n.best.i, n.best.j, 'cost=', n.gdist
		elif gi <= gij and gi <= gj:
			# print "%d,%d -> %d,%d cost %.1f" % (n.i. n.j, n.i-1, n.j, gi)
			n.best = ni
			n.gdist = gi
			# print 'n=', n.i, n.j, '->', n.best.i, n.best.j, 'cost=', n.gdist
		else:
			# gj <= gij and gj <= gi:
			# print "%d,%d -> %d,%d cost %.1f" % (n.i. n.j, n.i, n.j-1, gj)
			n.best = nj
			n.gdist = gj
			# print 'n=', n.i, n.j, '->', n.best.i, n.best.j, 'cost=', n.gdist
		return n.gdist


	cdef double searchjump(self, node_t *n, int nsteps, int depth) except -2.0:
		# print '%s--sjump node[%d,%d] --' % (S*depth, n.i, n.j)
		if n.best:
			return n.gdist
		elif n.i==0 and n.j==0:
			n.best = <node_t *>0
			n.gdist = 0.0
			return n.gdist

		cdef node_t *tmp, *tgt
		cdef double g
		cdef double gbest = self.BIG
		cdef int ki, kj, r, phase, found = 0
		n.gdist = -1
		n.best = <node_t *>0

		cdef slice_c slice = slicemaker(self.nx-1, self.ny-1, depth, nsteps)
		# print '%s slice=@[%d,%d] to (%.1f,%.1f)' % (S*depth, slice.i, slice.j, slice.ie, slice.je)
		while slice.ok():
				# print 'slice.ij=', slice.i, slice.j
				tgt = self.gn(slice.i, slice.j)
				if not inbeam(tgt, n, self.nx-1, self.ny-1):
					slice.next()
					continue
				g = self.searchjump(tgt, nsteps, depth+1)
				if g < 0.0:
					# print '%s searchjump=%g < 0 bailout for (%d,%d) -> (%d,%d)' % (S*depth, g, n.i, n.j, tgt.i, tgt.j)
					slice.next()
					continue
				if g > gbest:
					# print '%s searchjump=%g > gbest=%g bailout for (%d,%d) -> (%d,%d)' % (S*depth, g, gbest, n.i, n.j, tgt.i, tgt.j)
					slice.next()
					continue
				found = 1
				# print '%s -- partial g=%g search [%d,%d] -> [%d,%d]' % (S*depth, g, n.i, n.j, tgt.i, tgt.j)
				tmp = n
				while True:
					tmp = self.stepnode(tmp, tgt, n)
					if not tmp:
						break
					g += self.get(tmp)
					# print '%s      partial g=%g get [%d,%d] -> [%d,%d] @ [%d,%d]' % (S*depth, g, n.i, n.j, tgt.i, tgt.j, tmp.i, tmp.j)
					if g > gbest:
						# print '%s g=%g > gbest=%g get bailout for [%d,%d]->[%d,%d] at [%d,%d]' % (S*depth, g, gbest, n.i, n.j, tgt.i, tgt.j, tmp.i, tmp.j)
						break
				if g < gbest:
					# print '%s Setting n.best=[%d,%d] g=%g from [%d,%d]' % (S*depth, n.i, n.j, g, tgt.i, tgt.j)
					gbest = g
					n.gdist = g
					n.best = tgt
				else:
					# print '%s Not the best g=%g' % (S*depth, g)
					pass
				slice.next()
		if found and not n.best:
			n.gdist = -1.0	# Trouble!
			# print '%strouble!' % (S*depth,)
			# raise RuntimeError, "node[%d,%d]" % (n.i, n.j)
		elif not found:
			n.gdist = self.BIG
		# print '%s---done' % (S*depth,)
		return n.gdist


	cdef node_t *stepnode(self, node_t *n, node_t *tgt, node_t *start):
		"""Pick the next node, starting from n towards tgt that is closest to the line
		between start and n.
		"""
		if n.i <= tgt.i and n.j <= tgt.j:
			return <node_t *> 0
		elif n.i <= tgt.i:
			return self.gn(n.i, n.j-1)
		elif n.j <= tgt.j:
			return self.gn(n.i-1, n.j)
		cdef int a = start.j - tgt.j
		cdef int b = tgt.i - start.i

		cdef long di = labs((n.i-start.i-1)*a + (n.j-start.j)*b)
		cdef long dj = labs((n.i-start.i)*a + (n.j-start.j-1)*b)
		cdef long dij = labs((n.i-start.i-1)*a + (n.j-start.j-1)*b)

		if di < dj and di < dij:
			return self.gn(n.i-1, n.j)
		elif dj < dij:
			return self.gn(n.i, n.j-1)
		return self.gn(n.i-1, n.j-1)


	cdef numpy.ndarray path(self, node_t *n):
		cdef node_t *tgt, *tmp, *nn
		cdef int ns = 0
		# with nogil:
		if True:
			nn = n
			while nn.best:
				tgt = nn.best
				tmp = nn
				while True:
					tmp = self.stepnode(tmp, tgt, nn)
					if not tmp:
						break
					ns += 1
				nn = tgt
		cdef numpy.ndarray[ITYPE, ndim=2, mode="c"] o = numpy.zeros((ns+1, 2), numpy.int32)
		o[ns,0] = n.i
		o[ns,1] = n.j
		ns -= 1
		while n.best:
			tgt = n.best
			tmp = n
			# print 'here=(%d,%d) tgt=(%d,%d)' % (n.i, n.j, tgt.i, tgt.j)
			while True:
				tmp = self.stepnode(tmp, tgt, n)
				if not tmp:
					break
				# print 'step to (%d,%d) ns=%d' % (tmp.i, tmp.j, ns)
				o[ns,0] = tmp.i
				o[ns,1] = tmp.j
				ns -= 1
			n = tgt
		assert ns == -1
		return o


def dtw(numpy.ndarray[FTYPE, ndim=2] x, numpy.ndarray[FTYPE, ndim=2] y, alpha, beta):
	"""A standard Dynamic Time Warp match of two sequences of feature vectors.
	The allowed time increments are (0,1), (1,0), and (1,1).
	"""
	if x.shape[1] != y.shape[1]:
		raise ValueError, "Feature vectors different lengths: %dx%d and %dx%d" % (x.shape[0], x.shape[1], y.shape[0], y.shape[1])
	cdef state_c s = state_c(x, y, alpha, beta)
	cdef node_t *n = s.gn(s.nx-1, s.ny-1)
	with nogil:
		s.search(n)
	return s.path(n)


def dtwjump(numpy.ndarray[FTYPE, ndim=2] x, numpy.ndarray[FTYPE, ndim=2] y, mm, alpha, beta):
	"""A constrained Dynamic Time Warp match of two sequences of feature vectors.
	The allowed time increments are range from (0,m) and (m,0) to (0,2m), (2m,0), (m,m).
	"""
	cdef int m = int(round(mm))
	assert m > 0
	if x.shape[1] != y.shape[1]:
		raise ValueError, "Feature vectors different lengths: %dx%d and %dx%d" % (x.shape[0], x.shape[1], y.shape[0], y.shape[1])
	cdef state_c s = state_c(x, y, alpha, beta)
	cdef node_t *n = s.gn(s.nx-1, s.ny-1)
	cdef double rv = 0.0
	cdef int nsteps = max(s.nx,s.ny)//m
	# print 's.n=(%d,%d), nsteps=%d' % (s.nx, s.ny, nsteps)
	# with nogil:
	if True:
		rv = s.searchjump(n, nsteps, 0)
	if rv < 0.0:
		raise RuntimeError, "searchjump: !found, nsteps=%d" % nsteps
	if n.best == <node_t *>0:
		n.best = s.gn(0, 0)	# Default, for when m is bigger than the data length.
	# else:
		# die.info("gdist=%g" % n.gdist)
	return s.path(n)


def map2index(numpy.ndarray[FTYPE, ndim=2] x, numpy.ndarray[ITYPE, ndim=1] tmap):
	cdef int j, i, n=tmap.shape[0], np=x.shape[1]
	cdef numpy.ndarray[FTYPE, ndim=2, mode="c"] o = numpy.zeros((np, n))
	assert tmap[0] >= 0, "np=%d n=%d, tmap=%d ... %d" % (np, n, tmap[0], tmap[-1])
	assert tmap[-1] <= x.shape[0]-1, "np=%d n=%d, tmap=%d ... %d" % (np, n, tmap[0], tmap[-1])
	for i from 0 <= i < n:
		k = tmap[i]
		for j from 0 <= j < np:
			o[j,i] = x[j,k]
	return o


def map2shared(numpy.ndarray[FTYPE, ndim=2] x, numpy.ndarray[FTYPE, ndim=2] y,
		numpy.ndarray[ITYPE, ndim=2] tmap):
	if x.shape[1] != y.shape[1]:
		raise ValueError, "Feature vectors different lengths: %dx%d and %dx%d" % (x.shape[0], x.shape[1], y.shape[0], y.shape[1])
	assert tmap.shape[1]==2
	cdef int i, n=tmap.shape[0], np=x.shape[1]
	cdef numpy.ndarray[FTYPE, ndim=1, mode="c"] t = numpy.zeros((n,))
	for i from 0 <= i < n:
		t[i] = 0.5*(tmap[i,0] + tmap[i,1])
	return (t, map2index(x, tmap[:,0]), map2index(x, tmap[:,1]))


def map2dist(numpy.ndarray[FTYPE, ndim=2] x, numpy.ndarray[FTYPE, ndim=2] y,
		numpy.ndarray[ITYPE, ndim=2] tmap, double alpha, double beta):
	if x.shape[1] != y.shape[1]:
		raise ValueError, "Feature vectors different lengths: %dx%d and %dx%d" % (x.shape[0], x.shape[1], y.shape[0], y.shape[1])
	cdef int kx, ky, j, i, n=tmap.shape[0], np=x.shape[1]
	assert tmap.shape[1]==2
	assert tmap[0,0] >= 0, "np=%d n=%d, tmap=%d ... %d" % (np, n, tmap[0,0], tmap[-1,0])
	assert tmap[0,1] >= 0, "np=%d n=%d, tmap=%d ... %d" % (np, n, tmap[0,1], tmap[-1,1])
	assert tmap[-1,0] <= x.shape[0]-1, "np=%d n=%d, tmap=%d ... %d" % (np, n, tmap[0,0], tmap[-1,0])
	assert tmap[-1,1] <= y.shape[0]-1, "np=%d n=%d, tmap=%d ... %d" % (np, n, tmap[0,1], tmap[-1,1])
	cdef numpy.ndarray[FTYPE, ndim=1, mode="c"] t = numpy.zeros((n,))
	cdef numpy.ndarray[FTYPE, ndim=1, mode="c"] d = numpy.zeros((n,))
	cdef double tmp
	for i from 0 <= i < n:
		t[i] = 0.5*(tmap[i,0] + tmap[i,1])
		kx = tmap[i,0]
		ky = tmap[i,1]
		tmp = 0.0
		for j from 0 <= j < np:
			tmp += pow(fabs(x[kx,j] - y[ky,j]), alpha)
		d[i] = pow(tmp/np, beta)
	return (t, d)


def scale_tmap(numpy.ndarray[ITYPE, ndim=2] tmap, double dt0, double s0, double dt1, double s1):
	assert tmap.shape[1] == 2
	cdef int n = tmap.shape[0]
	cdef numpy.ndarray[FTYPE, ndim=2, mode="c"] rv = numpy.zeros((n,2))
	cdef int i
	for i from 0 <= i < n:
		rv[i,0] = tmap[i,0]*dt0 + s0
		rv[i,1] = tmap[i,1]*dt1 + s1
	return  rv


def test_same():
	LEN = 10
	FVL = 12
	x = numpy.random.normal(size=(LEN,FVL))
	q = dtw(x, x, 2, 1)
	# print 'q=', q
	assert q.shape[1]==2
	assert numpy.equal(q[:,0], q[:,1]).all()
	assert numpy.equal(q[:,0], numpy.arange(LEN)).all()
	for step in range(1,LEN):
		# print '--------step=%d---------' % step
		try:
			qj = dtwjump(x, x, step, 2, 1)
		except RuntimeError, x:
			die.die("Fails with step=%d: %s" % (step, x))
		# print 'q(%d)=%s', (step, qj)
		assert q.shape[1]==2
		assert LEN <= q.shape[0] <= 2*LEN, "q.shape=%s" % str(q.shape)
		assert numpy.equal(qj[:,0], qj[:,1]).all(), "fail(%d): %s %s" % (step, qj[:,0], qj[:,1])
		assert numpy.equal(qj[:,0], numpy.arange(LEN)).all()
		# print


def test_partial(m):
	print '# test_partial', m
	LEN = 11
	FVL = 20
	x = numpy.random.normal(size=(LEN,FVL))
	y = numpy.array(x, copy=True)
	y[0:6,:] = 0
	if m<=0:
		q = dtw(x, y, 2, 1)
	else:
		q = dtwjump(x, y, m, 2, 1)
	assert numpy.equal(q[:,0], q[:,1]).all()
	assert numpy.equal(q[:,0], numpy.arange(LEN)).all()

def test_scaled(m):
	print '# test_scaled', m
	LEN = 20
	FVL = 30
	x = numpy.random.normal(size=(LEN,FVL))
	y = x*0.5
	if m<=0:
		q = dtw(x, y, 2, 1)
	else:
		q = dtwjump(x, y, m, 2, 1)
	assert numpy.equal(q[:,0], q[:,1]).all()
	assert numpy.equal(q[:,0], numpy.arange(LEN)).all()

def test_stretched(m):
	print '# test_stretched', m
	LEN = 12
	FVL = 30
	x = numpy.random.normal(size=(LEN,FVL))
	y = numpy.zeros((2*LEN,FVL))
	for i in range(x.shape[0]):
		y[2*i,:] = x[i,:]
		y[2*i+1,:] = x[i,:]
	if m<=0:
		q = dtw(x, y, 2, 1)
	else:
		q = dtwjump(x, y, m, 2, 1)
	for i in range(x.shape[0]):
		assert q[i,1]==2*q[i,0] or q[i,1]==2*q[i,0]+1


def test():
	test_same()
	for m in [0, 1, 2, 3, 5, 7, 10]:
		test_partial(m)
		test_scaled(m)
		test_stretched(m)

# test()

import gpkimgclass
from gmisclib import edit_distance

def namelist(x):
	i = 0
	rv = []
	while 'TTYPE%d'%i in x.hdr:
		rv.append(x.hdr['TTYPE%d' % i])
		i += 1
	return rv

def check_names(x, y, approx_name):
	xn = namelist(x)
	yn = namelist(y)
	if approx_name:
		xs = ';'.join(xn)
		ys = ';'.join(yn)
		return edit_distance.distf(xs, ys, edit_distance.def_cost) < 0.1*0.5*(len(xs)+len(ys))
	return xn == yn


def dtw_ti(x, y, alpha, beta, x0=None, xe=None, y0=None, ye=None, approx_name=False):
	"""
	@type x: gpkimgclass.gpk_img
	@type y: gpkimgclass.gpk_img
	"""
	if x.d.shape[0] != y.d.shape[0]:
		raise ValueError, "Feature vectors different lengths: %d and %d" % (x.d.shape[0], y.d.shape[0])
	if not check_names(x, y, approx_name):
		raise ValueError, "Feature vectors are too different."
	if x0 is None:
		i0 = 0
	else:
		i0 = int(round(x.t_index(x0)))
	if xe is None:
		ie = x.d.shape[1]
	else:
		ie = int(round(x.t_index(xe)))
	if y0 is None:
		j0 = 0
	else:
		j0 = int(round(y.t_index(y0)))
	if ye is None:
		je = y.d.shape[1]
	else:
		je = int(round(y.t_index(ye)))
	imap = dtw(x.d[:,i0:ie+1], y.d[:,j0,je+1], alpha, beta)
	rv = numpy.array([x.time(imap[:,0]), y.time(imap[:,1])], axis=1)
	assert rv.shape[1] == 2
	return rv

def parse_args(args):
	x = {'loose': False, 'limit_edges': False, 'alpha': 2.0, 'beta': 0.5, 'step': 0.0,
		'x0': None, 'xe': None, 'y0': None, 'ye': None, 'o': '-'
		}
	x0, xe, y0, ye = None, None, None, None
	while args and args[0].startswith('-'):
		arg = args.pop(0)
		if arg == '-loose':
			x['loose'] = True
		elif arg == '-limit_range':
			x['limit_edges'] = True
		elif arg == '-o':
			x['o'] = args.pop(0)
		elif arg == '-step':
			x['step'] = float((args.pop(0)))
		elif arg == '-xr':
			x['x0'] = float(args.pop(0))
			x['xe'] = float(args.pop(0))
		elif arg == '-yr':
			x['y0'] = float(args.pop(0))
			x['ye'] = float(args.pop(0))
		elif arg == '-d':
			x['alpha'] = float(args.pop(0))
			x['beta'] = float(args.pop(0))/x['alpha']
		else:
			die.die("Unrecognized flag: %s: %s" % (arg, args))
	if len(args) != 2:
		die.die("Should be two args: %s" % str(args))
	fA = args.pop(0)
	fB = args.pop(0)
	return (fA, fB, x)
