
import re
import math

import numpy

from gmisclib import die
from gmisclib import cache as CC
from gmisclib import gpkmisc
from gmisclib import g_encode
from gmisclib import erb_scale
from gmisclib import Numeric_gpk as NG
from gpk_voicing import voice_misc
from gpk_voicing import cached_ps as PSC
from gpk_voicing import percep_spec as PS
try:
	from gpk_voicing import pseudo_dur as PD
except ImportError:
	PD = None

pylab = None
# import pylab

DB = math.sqrt(0.5)
NSV = 0.75
# Dt = 0.005
C = 10.0


class SillyWidthException(ValueError):
	def __init__(self, *s):
		ValueError.__init__(self, *s)


# def edgepair_win(n):
	# e = Num.zeros((2*n,), Num.Float)
	# e[:n] = -1.0
	# e[n:] = 1.0
	# s = Num.ones((2*n), Num.Float)
	# return (e, s)


def edgepair_win(n):
	if not n > 0:
		raise SillyWidthException, n
	if isinstance(n, int):
		tmp0 = voice_misc.window(2*n, 0)
		tmp1 = voice_misc.window(2*n, 1)
	else:
		tmp0 = voice_misc.cont_kernel(2+2*n, 0)
		tmp1 = voice_misc.cont_kernel(2+2*n, 1)
	norm = gpkmisc.N_maximum(tmp0)
	return (tmp1/norm, tmp0/norm)


# def win(n):
	# tmp = Num.ones((n,), Num.Float)
	# return tmp

def win(n):
	if not n > 0:
		raise SillyWidthException, n
	if isinstance(n, int):
		tmp = voice_misc.window(n, 0)
	else:
		tmp = voice_misc.cont_kernel(n, 0)
	return tmp/gpkmisc.N_maximum(tmp)


def burst_win(n, i):
	if not n > 0:
		raise SillyWidthException, n
	tmp = numpy.zeros((n,), numpy.float)
	tmp[i] = 1.0
	neg = win(n)
	nneg = neg/neg.sum()
	return (tmp - nneg, nneg)


def entropy(w):
	assert numpy.greater(w, 0.0).all()
	p = w/numpy.sum(w)
	return -numpy.sum(p*numpy.log(p))

def convolve(signal, kernel):
	"""Returns something the length of the signal, by zero padding."""
	if signal.shape[0] > kernel.shape[0]:
		return numpy.convolve(signal, kernel, 1)
	m = 1+kernel.shape[0]-signal.shape[0]
	tmp = numpy.concatenate((signal, numpy.zeros((m,), numpy.float)))
	die.info('Narrow signal: padding from %d to %d' % (signal.shape[0], m))
	return numpy.convolve(tmp, kernel, 1)[:signal.shape[0]]


def vowel(width, ectrs, neural, neural_now, Nsv,
		formant_low=None, formant_high=None):
	VFAC = 0.75
	cs = win(width)
	css = cs.sum()

	nns = convolve(neural_now, cs/css)
	norm = numpy.hypot(nns, Nsv * numpy.square(nns).sum()/numpy.sum(nns))
	# The second term within the hypot() should always be Nsv*one.

	# pylab.plot(norm)
	# pylab.figure()
	o = []
	descr = []
	for (i, e) in enumerate(ectrs):
		if e['type']=='band' and formant_low < e['erb'] < formant_high:
			tmp = (VFAC/css)*convolve(neural[i], cs)/norm
			# pylab.plot(tmp)
			# print 'RMS', math.sqrt(Num.square(tmp).sum()/tmp.shape[0])
			dtmp = e.copy()
			dtmp['type'] = 'vowel'
			dtmp['width'] = width
			dtmp['Kentropy'] = entropy(cs)
			dtmp['Fentropy'] = 0.0
			dtmp['t_symmetry'] = 1
			dtmp['a_scaling'] = 1
			# pylab.title('vowel %s' % dtmp['id'])
			# pylab.figure()
			o.append(tmp)
			dtmp['id'] = '%s:%d:%.1f' % (dtmp['type'], width, e['erb'])
			descr.append( dtmp )
	# pylab.show()
	return (o, descr)


def spectral_entropy(width, ectrs, neural, neural_now, Nsv):
	"""This is inspired (but only loosely) by
	"Robust Entropy-based Endpoint Detection for Speech Recognition
	in Noisy Environments." by Jia-lin Shen and Jeih-weih Hung
	and Lin-shan Lee, http://www.ee.columbia.edu/~dpwe/papers/ShenHL98-endpoint.pdf
	International Conference on Spoken Language Processing, 1998.
	"""
	assert Nsv > 0.0
	SEF = 1.0
	EPS = 1e-6
	cs = win(width)
	assert numpy.greater(cs, 0.0).all()

	nnt = []
	nns = numpy.zeros(neural.shape[1], numpy.float)
	for (i, e) in enumerate(ectrs):
		if e['type']=='band':
			assert numpy.greater_equal(neural[i], 0.0).all()
			tmp = convolve(neural[i], cs)
			nnt.append(tmp)
			numpy.add(nns, tmp, nns)

	np = len(nnt)
	ent_sum = numpy.zeros(neural.shape[1], numpy.float) + math.log(4.0)
	for tmp in nnt:
		numpy.divide(tmp, nns, tmp)
		numpy.add(tmp, EPS*nns, tmp)
		etmp = tmp*numpy.log(tmp)
		numpy.add(ent_sum, etmp, ent_sum)
	numpy.multiply(ent_sum, -SEF, ent_sum)
	# pylab.title('spectral_entropy')
	# pylab.show()
	assert np > 0
	dtmp = {'type': 'spectral_entropy',
		'width': width,
		'Kentropy': entropy(cs),
		'Fentropy': math.log(np),
		'id': 'Sentropy:%d' % width,
		't_symmetry': 1,
		'a_scaling': 0
		}
	return ([ent_sum], [dtmp])


def space_time_entropy(width, ectrs, neural, neural_now, Nsv):
	assert Nsv > 0.0
	SEF = 1.0
	EPS = 1e-6
	cs = win(width)

	nnt = []
	nns = numpy.zeros(neural.shape[1], numpy.float)
	for (i, e) in enumerate(ectrs):
		if e['type']=='band':
			assert numpy.greater_equal(neural[i], 0.0).all()
			nnt.append(neural[i])
			numpy.add(nns, neural[i], nns)

	np = len(nnt)
	nns = convolve(nns, cs)
	ent_sum = numpy.zeros(neural.shape[1], numpy.float) + math.log(3.0*np)
	for tmp in nnt:
		numpy.divide(tmp, nns, tmp)
		numpy.add(tmp, EPS*nns, tmp)
		etmp = convolve(tmp*numpy.log(tmp), cs)
		numpy.add(ent_sum, etmp, ent_sum)
	numpy.multiply(ent_sum, -SEF, ent_sum)
	# pylab.title('spectral_entropy')
	# pylab.show()
	assert np > 0
	dtmp = {'type': 'space_time_entropy',
		'width': width,
		'Kentropy': entropy(cs),
		'Fentropy': math.log(np),
		'id': 'STentropy:%d' % width,
		't_symmetry': 1,
		'a_scaling': 0
		}
	return ([ent_sum], [dtmp])



def haspitch(width, ectrs, neural, neural_now, Nsv):
	HFAC = {'high': 20.0, 'all': 10.0, 1: 10.0}
	cs = win(width)
	css = cs.sum()
	nns = convolve(neural_now, cs)/cs.sum()
	norm = numpy.hypot(nns, Nsv * numpy.square(nns).sum()/nns.sum())
	o = []
	descr = []
	for (i, e) in enumerate(ectrs):
		if e['type'].startswith('haspitch'):
			tmp = (HFAC[e['variant']]/css)*convolve(neural[i], cs)/norm
			# pylab.figure()
			# pylab.title('e=%s' % e['id'])
			# pylab.plot(neural[i])
			# pylab.plot(tmp)
			# pylab.plot(norm)
			o.append(tmp)
			# print 'RMS', math.sqrt(Num.square(tmp).sum()/tmp.shape[0])
			dtmp = e.copy()
			dtmp['id'] = '%s:%d' % (dtmp['id'], width)
			dtmp['Kentropy'] = entropy(cs)
			assert 'Fentropy' in dtmp
			dtmp['width'] = width
			dtmp['t_symmetry'] = 1
			dtmp['a_scaling'] = 1
			descr.append( dtmp )
	# pylab.show()
	return (o, descr)



def peakiness(width, ectrs, neural, neural_now, Nsv):
	HFAC = 10
	cs = win(width)
	css = cs.sum()
	nns = convolve(neural_now, cs)/css
	norm = numpy.hypot(nns, Nsv * numpy.square(nns).sum()/numpy.sum(nns))
	o = []
	descr = []
	for (i, e) in enumerate(ectrs):
		if e['type'].startswith('peakalign'):
			tmp = (HFAC/css)*convolve(neural[i], cs)/norm
			# pylab.figure()
			# pylab.title('e=%s' % e['id'])
			# pylab.plot(neural[i])
			# pylab.plot(tmp)
			# pylab.plot(norm)
			o.append(tmp)
			# print 'RMS', math.sqrt(Num.square(tmp).sum()/tmp.shape[0])
			dtmp = e.copy()
			dtmp['id'] = '%s:%d' % (dtmp['id'], width)
			dtmp['Kentropy'] = entropy(cs)
			assert 'Fentropy' in dtmp
			dtmp['width'] = width
			dtmp['t_symmetry'] = 1
			dtmp['a_scaling'] = 1
			descr.append( dtmp )
	# pylab.show()
	return (o, descr)


# This is dubious, unless the calibration (C) is set for the
# particular code that produces neural.    Note that the "official"
# pseudoduration estimator uses its own set of feature vectors.
def pdur(ectrs, neural, neural_now, Dt, Cfac=1.0):
	if PD is None:	# If pseuduration is not available.
		return ([], [])
	TYPICAL = 0.100
	c = C * Cfac
	norm2 = (numpy.square(neural_now).sum()/numpy.sum(neural_now))**2
	no = neural_now.shape[0]
	out = numpy.zeros((no,), numpy.float)
	for t in range(no):
		plm, pcm = PD.pdur_guts(neural.transpose(), t, -1, Dt, c/norm2)
		plp, pcp = PD.pdur_guts(neural.transpose(), t, 1, Dt, c/norm2)
		out[t] = plm + plp
	if pylab:
		pylab.figure()
		pylab.title('pdur')
		pylab.plot(out)
		pylab.show()
	nbands = 0
	for e in ectrs:
		if e['type']=='band':
			nbands += 1
	assert nbands > 0
	typical = NG.N_median(out)
	descr = [ {'id': 'pseudoduration:%f' % c,
		'Kentropy': NG.N_median(numpy.log(out)),
		'Fentropy': math.log(nbands),
		'width': typical,
		't_symmetry': 1, 'a_scaling': 0
		} ]
	return ([numpy.log(out/TYPICAL)], descr)


def roughness(width, ectrs, neural, neural_now, Nsv):
	HFAC = {1: 4.0}
	cs = win(width)
	css = cs.sum()
	nns = convolve(neural_now, cs)/css
	norm = numpy.hypot(nns, Nsv * numpy.square(nns).sum()/nns.sum())
	o = []
	descr = []
	for (i, e) in enumerate(ectrs):
		if e['type'].startswith('roughness'):
			tmp = (HFAC[e['variant']]/css)*convolve(neural[i], cs)/norm
			# pylab.figure()
			# pylab.title('e=%s' % e['id'])
			# pylab.plot(neural[i])
			# pylab.plot(tmp)
			# pylab.plot(norm)
			o.append(tmp)
			# print 'RMS', math.sqrt(Num.square(tmp).sum()/tmp.shape[0])
			dtmp = e.copy()
			dtmp['id'] = '%s:%d' % (dtmp['id'], width)
			dtmp['Kentropy'] = entropy(cs)
			assert 'Fentropy' in dtmp
			dtmp['width'] = width
			dtmp['t_symmetry'] = 1
			dtmp['a_scaling'] = 1
			descr.append( dtmp )
	# pylab.show()
	return (o, descr)

dissonance = roughness	# obsolete



def vowel_edge(width, ectrs, neural, neural_now, Nsv, do_abs=False,
		formant_low=None, formant_high=None):
	VEfac = 0.7
	ce, cs = edgepair_win(width)
	css = cs.sum()
	nns = convolve(neural_now, cs)/css
	norm = numpy.hypot(nns, Nsv * numpy.square(nns).sum()/nns.sum())
	# pylab.plot(norm)
	# pylab.figure()
	o = []
	descr = []
	for (i, e) in enumerate(ectrs):
		if e['type']=='band' and formant_low < e['erb'] < formant_high:
			tmp = VEfac * convolve(neural[i], ce)/norm
			# pylab.plot(tmp)
			# print 'RMS', math.sqrt(Num.square(tmp).sum()/tmp.shape[0])
			dtmp = e.copy()
			dtmp['width'] = 2*width
			dtmp['Kentropy'] = entropy(cs)
			dtmp['Fentropy'] = 0.0
			dtmp['t_symmetry'] = -1
			dtmp['a_scaling'] = 1
			if do_abs:
				o.append( numpy.absolute(tmp) )
				dtmp['type'] = 'vowel |edge|'
			else:
				o.append( tmp )
				dtmp['type'] = 'vowel edge'
			dtmp['id'] = '%s:%d:%.1f' % (dtmp['type'], width, e['erb'])
			descr.append( dtmp )
	# pylab.show()
	return (o, descr)


def fricative(width, ectrs, neural, neural_now, Nsv):
	CSSE = 0.7
	N = neural[0].shape[0]
	cs = win(width)
	css = cs.sum()
	nns = convolve(neural_now, cs)/css
	norm = numpy.hypot(nns, Nsv * numpy.square(nns).sum()/nns.sum())
	# pylab.plot(norm)
	# pylab.figure()
	o = []
	descr = []
	for (rs, re, fac) in [(70.0, 700.0, 0.6), (700.0, 1500.0, 0.6),
				(1500.0, 2500.0, 0.9), (2500.0, 4000.0, 1.1),
				(4000.0, 6000.0, 1.3)
				]:
		tsum = numpy.zeros((N,), numpy.float)
		elow = erb_scale.f_to_erb(rs)
		ehigh = erb_scale.f_to_erb(re)
		included = []
		nq = 0
		for (i, e) in enumerate(ectrs):
			if e['type']=='band' and elow < e['erb'] < ehigh:
				numpy.add(tsum, neural[i], tsum)
				included.append(e)
				nq += 1
		tmp = (fac/css**CSSE) * convolve(tsum, cs)/(norm*nq)
		# print 'RMS', math.sqrt(Num.square(tmp).sum()/tmp.shape[0])

		included.sort(key=lambda x:x['erb'])
		dtmp = {'width': width,
				'type': 'fricative', 't_symmetry': 1,
				'erbs': [e['erb'] for e in included],
				'fcs' : [e['fc'] for e in included],
				'Kentropy': entropy(cs),
				'Fentropy': -math.log(len(included)),
				'a_scaling': 1
				}
		o.append(tmp)
		dtmp['id'] = '%s:%d:%.1f-%.1f' % (dtmp['type'], width, included[0]['erb'], included[-1]['erb'])
		descr.append( dtmp )
	# pylab.show()
	return (o, descr)


def prominence(ectrs, neural, neural_now, Nsv):
	WIDTH = 20
	N = neural[0].shape[0]
	ce, cs = edgepair_win(WIDTH)
	css = cs.sum()
	nns = convolve(neural_now, cs)
	norm = numpy.hypot(nns, Nsv * numpy.square(nns).sum()/nns.sum())
	# pylab.plot(norm)
	# pylab.figure()
	o = []
	descr = []
	included = []
	for (rs, re, fac) in [ (70.0, 600.0, 0.22), (600.0, 2000.0, 0.25),
				(2000.0, 3000.0, 0.32), (3000.0, 6000.0, 0.35) ]:
		tsum = numpy.zeros((N,), numpy.float)
		elow = erb_scale.f_to_erb(rs)
		ehigh = erb_scale.f_to_erb(re)
		for (i, e) in enumerate(ectrs):
			if e['type']=='band' and elow < e['erb'] < ehigh:
				numpy.add(tsum, neural[i], tsum)
				included.append(e)
		tmp = (fac/css) * convolve(tsum, ce)/norm
		# pylab.plot(tmp + len(o))
		# print 'RMS', math.sqrt(Num.square(tmp).sum()/tmp.shape[0])
		included.sort(key=lambda x:x['erb'])
		dtmp = {'type': 'prominence', 'width': WIDTH,
				'erbs': [e['erb'] for e in included],
				'fcs' : [e['fc'] for e in included],
				'Kentropy': entropy(cs),
				'Fentropy': -math.log(len(included)),
				'id': 'prominence:%d:%.1f-%.1f' % (WIDTH, included[0]['erb'], included[-1]['erb']),
				't_symmetry': 1, 'a_scaling': 1
				}
		o.append( tmp )
		descr.append( dtmp )
	# pylab.show()
	return (o, descr)



def fricative_edge(width, ectrs, neural, neural_now, Nsv, do_abs=False, version=1):
	CSSE = 1.6
	N = neural[0].shape[0]
	ce, cs = edgepair_win(width)
	css = cs.sum()
	nns = convolve(neural_now, cs)/css
	norm = numpy.hypot(nns, Nsv * numpy.square(nns).sum()/nns.sum())
	# pylab.plot(norm)
	# pylab.figure()
	o = []
	descr = []
	if version==1:
		flist = [(70.0, 700.0, 7), (700.0, 1500.0, 7),
				(1500.0, 2500.0, 7), (2500.0, 4000.0, 7),
				(4000.0, 6000.0, 7)]
	elif version==2:
		flist = [(100.0, 300.0, 7), (300.0, 550.0, 7), (550.0, 900.0, 7),
				(900.0, 1400.0, 7),
				(1400.0, 2800.0, 7), (2800.0, 5000.0, 7)]
	for (rs, re, fac) in flist:
		tsum = numpy.zeros((N,), numpy.float)
		elow = erb_scale.f_to_erb(rs)
		ehigh = erb_scale.f_to_erb(re)
		included = []
		nq = 0
		for (i, e) in enumerate(ectrs):
			if e['type']=='band' and elow < e['erb'] < ehigh:
				numpy.add(tsum, neural[i], tsum)
				included.append(e)
				nq += 1
		tmp = (fac/css**CSSE) * convolve(tsum, ce)/(norm*nq)
		# print 'RMS', math.sqrt(Num.square(tmp).sum()/tmp.shape[0])

		included.sort(key=lambda x:x['erb'])
		dtmp = {'width': width,
				'erbs': [e['erb'] for e in included],
				'fcs' : [e['fc'] for e in included],
				'Kentropy': entropy(cs),
				'Fentropy': -math.log(len(included)),
				'a_scaling': 1
				}
		if do_abs:
			o.append( numpy.absolute(tmp) )
			dtmp['type'] = 'fricative |edge|'
			dtmp['t_symmetry'] = 1
		else:
			o.append( tmp )
			dtmp['type'] = 'fricative edge'
			dtmp['t_symmetry'] = -1
		dtmp['id'] = '%s:%d:%.1f-%.1f' % (dtmp['type'], width, included[0]['erb'], included[-1]['erb'])
		descr.append( dtmp )
	# pylab.show()
	return (o, descr)


def burst(ectrs, neural, neural_now, Nsv):
	BKGWIDTH = 8
	WIDTH = 1
	N = neural[0].shape[0]
	ce,cs = burst_win(BKGWIDTH, 2)
	nns = convolve(neural_now, cs)/cs.sum()
	norm = numpy.hypot(nns, Nsv * numpy.square(nns).sum()/nns.sum())
	# pylab.plot(norm)
	# pylab.figure()
	o = []
	descr = []
	included = []
	# Burst bandwidth needs to be large enough so that the
	# burst can fit into a 10ms slot.
	for (rs, re, fac) in [ (150.0, 500.0, 1.0), (500.0, 1200.0, 1.0),
				(1200.0, 2800.0, 1.0), (2800.0, 6000.0, 1.0) ]:
		tsum = numpy.zeros((N,), numpy.float)
		elow = erb_scale.f_to_erb(rs)
		ehigh = erb_scale.f_to_erb(re)
		for (i, e) in enumerate(ectrs):
			if e['type']=='band' and elow < e['erb'] < ehigh:
				numpy.add(tsum, neural[i], tsum)
				included.append( e )
		tss = fac * convolve(tsum, ce)/norm
		# pylab.plot(tmp + len(o))
		# print 'RMS', math.sqrt(Num.square(tmp).sum()/tmp.shape[0])
		included.sort(key=lambda x:x['erb'])
		o.append( tss )
		descr.append( {'type': 'burst', 'width': WIDTH,
				'bkgwidth': BKGWIDTH,
				'erbs': [e['erb'] for e in included],
				'fcs' : [e['fc'] for e in included],
				'Kentropy': entropy(cs),
				'Fentropy': -math.log(len(included)),
				'id': 'burst:%d:%.1f-%.1f'
					% (WIDTH, included[0]['erb'], included[-1]['erb']),
				't_symmetry': 1, 'a_scaling': 1
				}
				)
	# pylab.show()
	return (o, descr)



class scale_xform(object):
	def __init__(self, opt_string, name='???', prefix='scale'):
		self.prefix = prefix
		self._name = name
		self.sc = []
		for tmp in opt_string.split('\n'):
			if tmp=='' or tmp.startswith('#'):
				continue
			v, k = tmp.split()
			self.sc.append( (re.compile(k), float(v)) )
		self._e = g_encode.encoder(notallowed=' %,')

	def get(self, k):
		for (pat,v) in self.sc:
			if pat.match(k):
				return v
		raise KeyError, "scale_xform.get(%s): no matching pattern." % k

	def get_many(self, *k):
		for q in k:
			yield self.get(q)

	def name(self):
		return self._name

	def operate(self, o, descr):
		assert o.shape[1] == len(descr)
		for (i,d) in enumerate(descr):
			k = '%s,%s' % (self.prefix, self._e.fwd(d['id']))
			scalefac = math.exp(self.get(k))
			numpy.multiply(o[:,i], scalefac, o[:,i])
			d['id'] = d['id'] + '(scaled)'
			d['scalefac'] = scalefac

	def describe_xform(self):
		pass


def normalize_neural(data, dt, Dt, bmin=None, bmax=None, db=DB, do_mod=0, do_dissonance=False,
			PlompBouman=False, cache_info=None,
			norm_kernel=None, Nsv=None, t_kernel=None, f_kernel=None
			):
	if cache_info is not None:
		assert isinstance(cache_info, CC.cache_info)
		all_ectrs, all_ps, t0 = PSC.perceptual_spec(data, dt, Dt,
							bmin=bmin, bmax=bmax, db=db,
							do_mod=do_mod,
							do_dissonance=do_dissonance,
							PlompBouman=PlompBouman,
							cache_info=cache_info
							)
	else:
		all_ectrs, all_ps, t0 = PS.block_percep_spec(data, dt, Dt,
							bmin=bmin, bmax=bmax, db=db,
							do_mod=do_mod,
							do_dissonance=do_dissonance,
							PlompBouman=PlompBouman
							)

	lng = all_ps.shape[1]
	def ctr_freq(x):
		return x[1]['erb']
	ecbsorted = sorted([(i,ec) for (i,ec) in enumerate(all_ectrs) if ec['type']=='band'],
				key=ctr_freq)
	band_indices = [i for (i,ec) in ecbsorted]
	ectrs = [ec for (i,ec) in ecbsorted]
	neural = all_ps.take(band_indices, axis=0)

	assert neural.shape[1]==lng
	assert len(data.shape) == 1
	assert abs(lng*Dt-data.shape[0]*dt) < 0.1*data.shape[0]*dt

	neural_now = numpy.average(neural, axis=0)
	assert neural_now.shape[0] == lng
	neural_avg = numpy.square(neural_now).sum()/neural_now.sum()
	numpy.divide(neural, neural_avg, neural)
	numpy.divide(neural_now, neural_avg, neural_now)
	if t_kernel is not None:
		for f in range(neural.shape[0]):
			neural[f,:] = numpy.convolve(neural[f,:], t_kernel, mode="same")
	if f_kernel is not None:
		for t in range(lng):
			neural[:,t] = numpy.convolve(neural[:,t], f_kernel, mode="same")

	nband_indices = [i for (i,ec) in enumerate(all_ectrs) if ec['type']!='band']
	nneural = all_ps.take(nband_indices, axis=0)
	nectrs = [ec for ec in all_ectrs if ec['type']!='band']

	nns = convolve(neural_now, norm_kernel)
	norm = numpy.hypot(nns, Nsv * numpy.square(nns).sum()/nns.sum())

	numpy.divide(neural, norm[numpy.newaxis,:], neural)

	assert neural.shape[0]+nneural.shape[0] == all_ps.shape[0]
	assert nneural.shape[1]==neural.shape[1]
	assert nneural.shape[0] < nneural.shape[1]
	assert len(nectrs) == nneural.shape[0]
	for (i,e) in enumerate(nectrs):
		assert e['type'] == 'band'

	return (t0, neural, ectrs, nneural, nectrs)
