//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "Spectrogram.hpp"
#include "makeImage.hpp"
#include <Accelerate/Accelerate.h>
#include <math.h>
#include <stdio.h>
#include <string.h>

static void makeColorTable(unsigned char* table);

static double bessi0(double x)
{
	//returns the modified Bessel function I_0(x) for any real x
	//from numerical recipes
	double ax, ans;
	double y;

	if((ax=fabs(x))<3.75){
		y=x/3.75;
		y *= y;
		ans =1.0+y*(3.5156229+y*(3.0899424+y*(1.2067492
			+y*(0.2659732+y*(0.360768e-1+y*0.45813e-2)))));
	}
	else{
		y=3.75/ax;
		ans = (exp(ax)/sqrt(ax))*(0.39894228+y*(0.1328592e-1
			+y*(0.225319e-2+y*(-0.157565e-2+y*(0.916281e-2
			+y*(-0.2057706e-1+y*(0.2635537e-1+y*(-0.1647633e-1
			+y*0.392377e-2))))))));
	}

	return ans;
}

static double i0(double x)
{
	const double epsilon = 1e-18;
	int n = 1;
	double S = 1., D = 1., T;

	while (D > epsilon * S) {
		T = x / (2 * n++);
		D *= T * T;
		S += D;
	}
	return S;
}

static void calcKaiserWindowD(size_t size, double* window, double stopBandAttenuation)
{
	size_t M = size - 1;
	size_t N = M-1;
#if VERBOSE
	printf("FillKaiser %d %g\n", M, stopBandAttenuation);
#endif

	double alpha = 0.;
	if (stopBandAttenuation <= -50.)
		alpha = 0.1102 * (-stopBandAttenuation - 8.7);
    else if (stopBandAttenuation < -21.)
		alpha = 0.5842 * pow(-stopBandAttenuation - 21., 0.4) + 0.07886 * (-stopBandAttenuation - 21.);

	double p = N / 2;
	double kk = 1.0 / i0(alpha);

	for(unsigned int k = 0; k < M; k++ )
	{
		double x = (k-p) / p;

		// Kaiser window
		window[k+1] *= kk * bessi0(alpha * sqrt(1.0 - x*x) );
	}
	window[0] = 0.;
	window[size-1] = 0.;
#if VERBOSE
	printf("done\n");
#endif
}

const int border = 8;

void spectrogram(int size, double* data, int width, int log2bins, const char* path, double dBfloor)
{
	int numRealFreqs = 1 << log2bins;
	int log2n = log2bins + 1;
	int n = 1 << log2n;
	int nOver2 = n / 2;
	double scale = 1./nOver2;
	int64_t paddedSize = size + n;
	double* paddedData = (double*)calloc(paddedSize, sizeof(double));
	memcpy(paddedData + nOver2, data, size * sizeof(double));
	double* dBMags = (double*)calloc(numRealFreqs + 1, sizeof(double));
	double hopSize = size <= n ? 0 : (double)(size - n) / (double)(width - 1);
	double* window = (double*)calloc(n, sizeof(double));
	for (int i = 0; i < n; ++i) window[i] = 1.;
	calcKaiserWindowD(n, window, -180.);
	unsigned char table[1028];
	makeColorTable(table);
	int heightOfAmplitudeView = 128;
	int heightOfFFT = numRealFreqs+1;
	int totalHeight = heightOfAmplitudeView+heightOfFFT+3*border;
	int topOfSpectrum = heightOfAmplitudeView + 2*border;
	int totalWidth = width+2*border;
	Bitmap* b = createBitmap(totalWidth, totalHeight);
	fillRect(b, 0, 0, totalWidth, totalHeight, 160, 160, 160, 255);
	fillRect(b, border, border, width, heightOfAmplitudeView, 0, 0, 0, 255);
	FFTSetupD fftSetup = vDSP_create_fftsetupD(log2n, kFFTRadix2);
	double* windowedData = (double*)calloc(n, sizeof(double));
	double* interleavedData = (double*)calloc(n, sizeof(double));
	double* resultData = (double*)calloc(n, sizeof(double));
	DSPDoubleSplitComplex interleaved;
	interleaved.realp = interleavedData;
	interleaved.imagp = interleavedData + nOver2;
	DSPDoubleSplitComplex result;
	result.realp = resultData;
	result.imagp = resultData + nOver2;
	double maxmag = 0.;
	double hpos = nOver2;
	for (int i = 0; i < width; ++i) {
		size_t ihpos = (size_t)hpos;
		double peak = 1e-20;
		for (int w = 0; w < n; ++w) {
			double x = paddedData[w+ihpos];
			x = fabs(x);
			if (x > peak) peak = x;
		}
		for (int64_t w = 0; w < n; ++w) windowedData[w] = window[w] * paddedData[w+ihpos];
		vDSP_ctozD((DSPDoubleComplex*)windowedData, 2, &interleaved, 1, nOver2);
		vDSP_fft_zropD(fftSetup, &interleaved, 1, &result, 1, log2n, kFFTDirection_Forward);
		dBMags[0] = result.realp[0] * scale;
		dBMags[numRealFreqs] = result.imagp[0] * scale;
		if (dBMags[0] > maxmag) maxmag = dBMags[0];
		if (dBMags[numRealFreqs] > maxmag) maxmag = dBMags[numRealFreqs];
		for (int64_t j = 1; j < numRealFreqs-1; ++j) {
			double x = result.realp[j] * scale;
			double y = result.imagp[j] * scale;
			dBMags[j] = sqrt(x*x + y*y);
			if (dBMags[j] > maxmag) maxmag = dBMags[j];
		}
		double invmag = 1.;
		dBMags[0] = 20.*log2(dBMags[0]*invmag);
		dBMags[numRealFreqs] = 20.*log10(dBMags[numRealFreqs]*invmag);
		for (int64_t j = 0; j <= numRealFreqs-1; ++j) {
			dBMags[j] = 20.*log10(dBMags[j]*invmag);
		}
		{
			double peakdB =  20.*log10(peak);
			int peakColorIndex = 256. - peakdB * (256. / dBfloor);
			int peakIndex = heightOfAmplitudeView - peakdB * (heightOfAmplitudeView / dBfloor);
			if (peakIndex < 0) peakIndex = 0;
			if (peakIndex > heightOfAmplitudeView) peakIndex = heightOfAmplitudeView;
			if (peakColorIndex < 0) peakColorIndex = 0;
			if (peakColorIndex > 255) peakColorIndex = 255;
			unsigned char* t = table + 4*peakColorIndex;
			fillRect(b, i+border, border+128-peakIndex, 1, peakIndex, t[0], t[1], t[2], t[3]);
		}
		for (int j = 0; j < numRealFreqs; ++j) {
			int colorIndex = 256. - dBMags[j] * (256. / dBfloor);
			if (colorIndex < 0) colorIndex = 0;
			if (colorIndex > 255) colorIndex = 255;
			unsigned char* t = table + 4*colorIndex;
			setPixel(b, i+border, numRealFreqs-j+topOfSpectrum, t[0], t[1], t[2], t[3]);
		}
		hpos += hopSize;
	}
	vDSP_destroy_fftsetupD(fftSetup);
	writeBitmap(b, path);
	freeBitmap(b);
	free(dBMags);
	free(paddedData);
	free(window);
	free(windowedData);
	free(interleavedData);
	free(resultData);
}

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "VM.hpp"
#include "clz.hpp"
#include "elapsedTime.hpp"
#include <cmath>
#include <memory>
#include <float.h>
#include <vector>
#include <algorithm>
#include "MultichannelExpansion.hpp"
#include "UGen.hpp"
#include "dsp.hpp"
#include "SoundFiles.hpp"

const Z kOneThird = 1. / 3.;

// list ops
#pragma mark LIST OPS

static void finite_(Thread& th, Prim* prim)
{
	V v = th.pop();
	th.pushBool(v.isFinite());
}

static void size_(Thread& th, Prim* prim)
{
	V v = th.pop();
	if (v.isList() && !v.isFinite())
		th.push(INFINITY);
	else
		th.push(v.length(th));
}

static void rank_(Thread& th, Prim* prim)
{
	V a = th.pop();

	int rank = 0;
	while (a.isVList()) {
		++rank;
		VIn in(a);
		if (in.one(th, a)) break;
	}

	th.push(rank);
}

static void shape_(Thread& th, Prim* prim)
{
	P<Array> shape = new Array(itemTypeZ, 4);

	V a = th.pop();

	while (a.isVList()) {
		Z len;
		if (a.isFinite()) {
			len = a.length(th);
		} else {
			len = INFINITY;
		}
		shape->addz(len);
		VIn in(a);
		if (in.one(th, a)) break;
	}
	th.push(new List(shape));
}

static void bub_(Thread& th, Prim* prim)
{
	V a = th.pop();
	P<List> seq = new List(itemTypeV, 1);
	seq->add(a);
	th.push(seq);
}

static void nbub_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("nbub : n");
	V a = th.pop();

	for (int64_t i = 0; i < n; ++i) {
		P<List> seq = new List(itemTypeV, 1);
		seq->add(a);
		a = seq;
	}
	th.push(a);
}

template <int N>
static void tupleN_(Thread& th, Prim* prim)
{
	P<List> seq = new List(itemTypeV, N);
	P<Array> arr = seq->mArray;
	arr->setSize(N);
	for (int i = 0; i < N; ++i) {
		V v = th.pop();
		arr->put(N-1-i, v);
	}
	th.push(seq);
}

template <int N>
static void untupleN_(Thread& th, Prim* prim)
{
	P<List> s = th.popList("unN : s");

	BothIn in(s);
	for (int i = 0; i < N; ++i) {
		V v;
		if (in.one(th, v)) {
			post("too few items in list for un%d", N);
			throw errFailed;
		}
		th.push(v);
	}
}

static void reverse_(Thread& th, Prim* prim)
{
	P<List> s = th.popList("reverse : s");

	if (!s->isFinite())
		indefiniteOp("reverse", "");

	s = s->pack(th);
	if (s->isEnd()) {
		th.push(s);
		return;
	}

	P<Array> const& a = s->mArray;
	P<List> s2 = new List(a->elemType, a->size());
	P<Array> const& a2 = s2->mArray;
	int64_t n = a->size();
	int64_t n1 = n-1;
	a2->setSize(n);
	if (a->isV()) {
		V* p = a2->v();
		V* q = a->v();
		for (int i = 0; i < n; ++i) {
			p[i] = q[n1-i];
		}
	} else {
		Z* p = a2->z();
		Z* q = a->z();
		for (int i = 0; i < n; ++i) {
			p[i] = q[n1-i];
		}
	}
	th.push(s2);
}

template <typename T>
void copy(T* dst, T* src, int64_t n)
{
	for (int64_t i = 0; i <  n; ++i) dst[i] = src[i];
}

template <typename T>
void reverse_copy(T* dst, T* src, int64_t n)
{
	for (int64_t i = 0; i <  n; ++i) dst[i] = src[-i];
}

static List* makeMirror(P<Array> const& a, int64_t n, int64_t nr, int64_t roff)
{
	int type = a->elemType;
	int64_t size = n + nr;
	List* s = new List(type, size);
	Array* b = s->mArray();

	b->setSize(size);
	if (type == itemTypeV) {
		V* p = b->v();
		V* q = a->v();
		copy(p, q, n);
		reverse_copy(p+n, q+roff, nr);
	} else {
		Z* p = b->z();
		Z* q = a->z();
		copy(p, q, n);
		reverse_copy(p+n, q+roff, nr);
	}
	return s;
}

static void mirror(Thread& th, int w, P<List> s)
{
	if (!s->isFinite())
		indefiniteOp("mirror", "");

	s = s->pack(th);

	P<Array> const& a = s->mArray;
	int64_t n = a->size();
	int64_t n1 = n-1;
	int64_t n2 = n-2;

	switch (w) {
		case 0 : {
			if (n < 3) {
				th.push(s);
				return;
			}
			th.push(makeMirror(a, n, n2, n2));
		} break;
		case 1 : {
			if (n < 2) {
				th.push(s);
				return;
			}
			th.push(makeMirror(a, n, n1, n2));
		} break;
		case 2 : {
			if (n == 0) {
				th.push(s);
				return;
			}
			th.push(makeMirror(a, n, n, n1));
		} break;
	}
}

static void mirror0_(Thread& th, Prim* prim)
{
	P<List> s = th.popList("mirror0 : s");
	mirror(th, 0, s);
}

static void mirror1_(Thread& th, Prim* prim)
{
	P<List> s = th.popList("mirror1 : s");
	mirror(th, 1, s);
}

static void mirror2_(Thread& th, Prim* prim)
{
	P<List> s = th.popList("mirror2 : s");
	mirror(th, 2, s);
}

static void rot_(Thread& th, Prim* prim)
{
	int64_t r = th.popInt("rot : r");
	P<List> s = th.popList("rot : s");

	if (r == 0) {
		th.push(s);
		return;
	}

	if (!s->isFinite())
		indefiniteOp("rot", "");

	s = s->pack(th);
	if (s->isEnd()) {
		th.push(s);
		return;
	}

	P<Array> const& a = s->mArray;
	int64_t n = a->size();
	int type = a->elemType;

	P<List> s2 = new List(type, n);
	P<Array> const& b = s2->mArray;
	if (type == itemTypeV) {
		for (int i = 0; i < n; ++i) b->add(a->wrapAt(i-r));
	} else {
		for (int i = 0; i < n; ++i) b->addz(a->wrapAtz(i-r));
	}
	th.push(s2);
}

static void shift_(Thread& th, Prim* prim)
{
	int64_t r = th.popInt("shift : r");
	P<List> s = th.popList("shift : s");

	if (r == 0) {
		th.push(s);
		return;
	}

	if (!s->isFinite())
		indefiniteOp("shift", "");

	s = s->pack(th);
	if (s->isEnd()) {
		th.push(s);
		return;
	}

	P<Array> const& a = s->mArray;
	int64_t n = a->size();
	int type = a->elemType;

	P<List> s2 = new List(type, n);
	P<Array> const& b = s2->mArray;
	if (type == itemTypeV) {
		for (int i = 0; i < n; ++i) b->add(a->at(i-r));
	} else {
		for (int i = 0; i < n; ++i) b->addz(a->atz(i-r));
	}
	th.push(s2);
}

static void clipShift_(Thread& th, Prim* prim)
{
	int64_t r = th.popInt("clipShift : r");
	P<List> s = th.popList("clipShift : s");

	if (r == 0) {
		th.push(s);
		return;
	}

	if (!s->isFinite())
		indefiniteOp("clipShift", "");

	s = s->pack(th);
	if (s->isEnd()) {
		th.push(s);
		return;
	}

	P<Array> const& a = s->mArray;
	int64_t n = a->size();
	int type = a->elemType;

	P<List> s2 = new List(type, n);
	P<Array> const& b = s2->mArray;
	if (type == itemTypeV) {
		for (int i = 0; i < n; ++i) b->add(a->clipAt(i-r));
	} else {
		for (int i = 0; i < n; ++i) b->addz(a->clipAtz(i-r));
	}
	th.push(s2);
}

static void foldShift_(Thread& th, Prim* prim)
{
	int64_t r = th.popInt("foldShift : r");
	P<List> s = th.popList("foldShift : s");

	if (r == 0) {
		th.push(s);
		return;
	}

	if (!s->isFinite())
		indefiniteOp("foldShift", "");

	s = s->pack(th);
	if (s->isEnd()) {
		th.push(s);
		return;
	}

	P<Array> const& a = s->mArray;
	int64_t n = a->size();
	int type = a->elemType;

	P<List> s2 = new List(type, n);
	P<Array> const& b = s2->mArray;
	if (type == itemTypeV) {
		for (int i = 0; i < n; ++i) b->add(a->foldAt(i-r));
	} else {
		for (int i = 0; i < n; ++i) b->addz(a->foldAtz(i-r));
	}
	th.push(s2);
}

static void muss_(Thread& th, Prim* prim)
{
	P<List> s = th.popList("muss : s");

	if (!s->isFinite())
		indefiniteOp("muss", "");

	s = s->pack(th);
	if (s->isEnd()) {
		th.push(s);
		return;
	}

	P<Array> const& a = s->mArray;
	P<List> s2 = new List(a->elemType, a->size());
	P<Array> const& a2 = s2->mArray;
	int64_t n = a->size();
	int64_t n1 = n-1;
	a2->setSize(n);
	if (a->isV()) {
		V* p = a2->v();
		V* q = a->v();
		for (int i = 0; i < n; ++i) {
			p[i] = q[i];
		}
		for (int64_t i = 0; i < n1; ++i) {
            int64_t j = th.rgen.irand(i, n1);
            if (j != i)
                std::swap(p[i], p[j]);
		}
	} else {
		Z* p = a2->z();
		Z* q = a->z();
		for (int64_t i = 0; i < n; ++i) {
			p[i] = q[i];
		}
		for (int64_t i = 0; i < n1; ++i) {
            int64_t j = th.rgen.irand(i, n1);
            if (j != i)
                std::swap(p[i], p[j]);
		}
	}
	th.push(s2);
}

V do_at(Thread& th, P<Array> const& a, Arg i);
V do_wrapAt(Thread& th, P<Array> const& a, Arg i);
V do_foldAt(Thread& th, P<Array> const& a, Arg i);
V do_clipAt(Thread& th, P<Array> const& a, Arg i);
V do_degkey(Thread& th, P<Array> const& a, Arg b, Z cycleWidth, int degreesPerCycle);
V do_keydeg(Thread& th, P<Array> const& a, Arg b, Z cycleWidth, int degreesPerCycle);

class AtGenVV : public Gen
{
	P<Array> _a;
	VIn _b;
public:
	AtGenVV(Thread& th, P<Array> const& a, Arg b) : Gen(th, itemTypeV, b.isFinite()), _a(a), _b(b) {}

	virtual const char* TypeName() const override { return "AtGenVV"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int bstride;
			V *b;
			if (_b(th, n,bstride, b)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = do_at(th, _a, *b);
					b += bstride;
				}
				_b.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}

};

class AtGenVZ : public Gen
{
	P<Array> _a;
	ZIn _b;
public:
	AtGenVZ(Thread& th, P<Array> const& a, Arg b) : Gen(th, itemTypeV, b.isFinite()), _a(a), _b(b) {}

	virtual const char* TypeName() const override { return "AtGenVZ"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int bstride;
			Z *b;
			if (_b(th, n,bstride, b)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = _a->at(*b);
					b += bstride;
				}
				_b.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}

};

class AtGenZZ : public Gen
{
	P<Array> _a;
	ZIn _b;
public:
	AtGenZZ(Thread& th, P<Array> const& a, Arg b) : Gen(th, itemTypeZ, b.isFinite()), _a(a), _b(b) {}

	virtual const char* TypeName() const override { return "AtGenZZ"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int bstride;
			Z *b;
			if (_b(th, n,bstride, b)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = _a->atz(*b);
					b += bstride;
				}
				_b.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}

};

class WrapAtGenVV : public Gen
{
	P<Array> _a;
	VIn _b;
public:
	WrapAtGenVV(Thread& th, P<Array> const& a, Arg b) : Gen(th, itemTypeV, b.isFinite()), _a(a), _b(b) {}

	virtual const char* TypeName() const override { return "WrapAtGenVV"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int bstride;
			V *b;
			if (_b(th, n,bstride, b)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = do_wrapAt(th, _a, *b);
					b += bstride;
				}
				_b.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}

};

class WrapAtGenVZ : public Gen
{
	P<Array> _a;
	ZIn _b;
public:
	WrapAtGenVZ(Thread& th, P<Array> const& a, Arg b) : Gen(th, itemTypeV, b.isFinite()), _a(a), _b(b) {}

	virtual const char* TypeName() const override { return "WrapAtGenVZ"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int bstride;
			Z *b;
			if (_b(th, n,bstride, b)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = _a->wrapAt(*b);
					b += bstride;
				}
				_b.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}

};

class WrapAtGenZZ : public Gen
{
	P<Array> _a;
	ZIn _b;
public:
	WrapAtGenZZ(Thread& th, P<Array> const& a, Arg b) : Gen(th, itemTypeZ, b.isFinite()), _a(a), _b(b) {}

	virtual const char* TypeName() const override { return "WrapAtGenZZ"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int bstride;
			Z *b;
			if (_b(th, n,bstride, b)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = _a->wrapAtz(*b);
					b += bstride;
				}
				_b.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}

};

class FoldAtGenVV : public Gen
{
	P<Array> _a;
	VIn _b;
public:
	FoldAtGenVV(Thread& th, P<Array> const& a, Arg b) : Gen(th, itemTypeV, b.isFinite()), _a(a), _b(b) {}

	virtual const char* TypeName() const override { return "FoldAtGenVV"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int bstride;
			V *b;
			if (_b(th, n,bstride, b)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = do_foldAt(th, _a, *b);
					b += bstride;
				}
				_b.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}

};

class FoldAtGenVZ : public Gen
{
	P<Array> _a;
	ZIn _b;
public:
	FoldAtGenVZ(Thread& th, P<Array> const& a, Arg b) : Gen(th, itemTypeV, b.isFinite()), _a(a), _b(b) {}

	virtual const char* TypeName() const override { return "FoldAtGenVZ"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int bstride;
			Z *b;
			if (_b(th, n,bstride, b)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = _a->foldAt(*b);
					b += bstride;
				}
				_b.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}

};

class FoldAtGenZZ : public Gen
{
	P<Array> _a;
	ZIn _b;
public:
	FoldAtGenZZ(Thread& th, P<Array> const& a, Arg b) : Gen(th, itemTypeZ, b.isFinite()), _a(a), _b(b) {}

	virtual const char* TypeName() const override { return "FoldAtGenZZ"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int bstride;
			Z *b;
			if (_b(th, n,bstride, b)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = _a->foldAtz(*b);
					b += bstride;
				}
				_b.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}

};

class ClipAtGenVV : public Gen
{
	P<Array> _a;
	VIn _b;
public:
	ClipAtGenVV(Thread& th, P<Array> const& a, Arg b) : Gen(th, itemTypeV, b.isFinite()), _a(a), _b(b) {}

	virtual const char* TypeName() const override { return "ClipAtGenVV"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int bstride;
			V *b;
			if (_b(th, n,bstride, b)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = do_clipAt(th, _a, *b);
					b += bstride;
				}
				_b.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}

};

class ClipAtGenVZ : public Gen
{
	P<Array> _a;
	ZIn _b;
public:
	ClipAtGenVZ(Thread& th, P<Array> const& a, Arg b) : Gen(th, itemTypeV, b.isFinite()), _a(a), _b(b) {}

	virtual const char* TypeName() const override { return "ClipAtGenVZ"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int bstride;
			Z *b;
			if (_b(th, n,bstride, b)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = _a->clipAt(*b);
					b += bstride;
				}
				_b.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}

};

class ClipAtGenZZ : public Gen
{
	P<Array> _a;
	ZIn _b;
public:
	ClipAtGenZZ(Thread& th, P<Array> const& a, Arg b) : Gen(th, itemTypeZ, b.isFinite()), _a(a), _b(b) {}

	virtual const char* TypeName() const override { return "ClipAtGenZZ"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int bstride;
			Z *b;
			if (_b(th, n,bstride, b)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = _a->clipAtz(*b);
					b += bstride;
				}
				_b.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}

};

static Z degkey(Z degree, P<Array> const& scale, Z cycleWidth, int degreesPerCycle)
{
	Z fidegree = floor(degree + .5);
	//Z frac = degree - fidegree;
	int idegree = (int)fidegree;
	int modDegree = (int)sc_imod(idegree, degreesPerCycle);
	//return frac + scale->atz(modDegree) + cycleWidth * sc_div(idegree, degreesPerCycle);
	return scale->atz(modDegree) + cycleWidth * sc_div(idegree, degreesPerCycle);
}

static Z keydeg(Z key, P<Array> const& scale, Z cycleWidth, int degreesPerCycle)
{
	Z cycles, cyckey;
	sc_fdivmod(key, cycleWidth, cycles, cyckey);

	Z frac = scale->atz(0) + cycleWidth - cyckey;
	Z mindiff = std::abs(frac);
	int idegree = 0;
	for (int i = 0; i < degreesPerCycle; ++i) {
		frac = std::abs(cyckey - scale->atz(i));
		if (frac < mindiff) {
			mindiff = frac;
			idegree = i;
		}
	}

	return idegree + cycles * degreesPerCycle;
}

class DegKeyVV : public Gen
{
	P<Array> _scale;
	Z _cycleWidth;
	int _degreesPerCycle;
	VIn _degree;
public:

	DegKeyVV(Thread& th, P<Array> const& inScale, Arg inDegree, Z inCycleWidth, int inDegreesPerCycle)
		: Gen(th, itemTypeV, inDegree.isFinite()),
		_scale(inScale),
		_degree(inDegree),
		_cycleWidth(inCycleWidth),
		_degreesPerCycle(inDegreesPerCycle)
		{}

	virtual const char* TypeName() const override { return "DegKeyVV"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int bstride;
			V *b;
			if (_degree(th, n,bstride, b)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = do_degkey(th, _scale, *b, _cycleWidth, _degreesPerCycle);
					b += bstride;
				}
				_degree.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}

};

class DegKeyVZ : public Gen
{
	P<Array> _scale;
	Z _cycleWidth;
	int _degreesPerCycle;
	ZIn _degree;
public:

	DegKeyVZ(Thread& th, P<Array> const& inScale, Arg inDegree, Z inCycleWidth, int inDegreesPerCycle)
		: Gen(th, itemTypeV, inDegree.isFinite()),
		_scale(inScale),
		_degree(inDegree),
		_cycleWidth(inCycleWidth),
		_degreesPerCycle(inDegreesPerCycle)
		{}

	virtual const char* TypeName() const override { return "DegKeyVZ"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int bstride;
			Z *b;
			if (_degree(th, n,bstride, b)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = degkey(*b, _scale, _cycleWidth, _degreesPerCycle);
					b += bstride;
				}
				_degree.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}

};

class DegKeyZZ : public Gen
{
	P<Array> _scale;
	Z _cycleWidth;
	int _degreesPerCycle;
	ZIn _degree;
public:

	DegKeyZZ(Thread& th, P<Array> const& inScale, Arg inDegree, Z inCycleWidth, int inDegreesPerCycle)
		: Gen(th, itemTypeZ, inDegree.isFinite()),
		_scale(inScale),
		_degree(inDegree),
		_cycleWidth(inCycleWidth),
		_degreesPerCycle(inDegreesPerCycle)
		{}

	virtual const char* TypeName() const override { return "DegKeyZZ"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int bstride;
			Z *b;
			if (_degree(th, n,bstride, b)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = degkey(*b, _scale, _cycleWidth, _degreesPerCycle);
					b += bstride;
				}
				_degree.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}

};

class KeyDegVV : public Gen
{
	P<Array> _scale;
	Z _cycleWidth;
	int _degreesPerCycle;
	VIn _key;
public:

	KeyDegVV(Thread& th, P<Array> const& inScale, Arg inDegree, Z inCycleWidth, int inDegreesPerCycle)
		: Gen(th, itemTypeV, inDegree.isFinite()),
		_scale(inScale),
		_key(inDegree),
		_cycleWidth(inCycleWidth),
		_degreesPerCycle(inDegreesPerCycle)
		{}

	virtual const char* TypeName() const override { return "KeyDegVV"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int bstride;
			V *b;
			if (_key(th, n,bstride, b)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = do_keydeg(th, _scale, *b, _cycleWidth, _degreesPerCycle);
					b += bstride;
				}
				_key.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}

};

class KeyDegVZ : public Gen
{
	P<Array> _scale;
	Z _cycleWidth;
	int _degreesPerCycle;
	ZIn _key;
public:

	KeyDegVZ(Thread& th, P<Array> const& inScale, Arg inDegree, Z inCycleWidth, int inDegreesPerCycle)
		: Gen(th, itemTypeV, inDegree.isFinite()),
		_scale(inScale),
		_key(inDegree),
		_cycleWidth(inCycleWidth),
		_degreesPerCycle(inDegreesPerCycle)
		{}

	virtual const char* TypeName() const override { return "KeyDegVZ"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int bstride;
			Z *b;
			if (_key(th, n,bstride, b)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = keydeg(*b, _scale, _cycleWidth, _degreesPerCycle);
					b += bstride;
				}
				_key.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}

};

class KeyDegZZ : public Gen
{
	P<Array> _scale;
	Z _cycleWidth;
	int _degreesPerCycle;
	ZIn _key;
public:

	KeyDegZZ(Thread& th, P<Array> const& inScale, Arg inDegree, Z inCycleWidth, int inDegreesPerCycle)
		: Gen(th, itemTypeZ, inDegree.isFinite()),
		_scale(inScale),
		_key(inDegree),
		_cycleWidth(inCycleWidth),
		_degreesPerCycle(inDegreesPerCycle)
		{}

	virtual const char* TypeName() const override { return "KeyDegZZ"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int bstride;
			Z *b;
			if (_key(th, n,bstride, b)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = keydeg(*b, _scale, _cycleWidth, _degreesPerCycle);
					b += bstride;
				}
				_key.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}

};

static Gen* newAtGen(Thread& th, P<Array> const& a, Arg b)
{
	if (b.isVList()) {
		return new AtGenVV(th, a, b);
	} else {
		if (a->isV()) {
			return new AtGenVZ(th, a, b);
		} else {
			return new AtGenZZ(th, a, b);
		}
	}
}

static Gen* newWrapAtGen(Thread& th, P<Array> const& a, Arg b)
{
	if (b.isVList()) {
		return new WrapAtGenVV(th, a, b);
	} else {
		if (a->isV()) {
			return new WrapAtGenVZ(th, a, b);
		} else {
			return new WrapAtGenZZ(th, a, b);
		}
	}
}

static Gen* newDegKeyGen(Thread& th, P<Array> const& a, Arg b, Z cycleWidth, int degreesPerCycle)
{
	if (b.isVList()) {
		return new DegKeyVV(th, a, b, cycleWidth, degreesPerCycle);
	} else {
		if (a->isV()) {
			return new DegKeyVZ(th, a, b, cycleWidth, degreesPerCycle);
		} else {
			return new DegKeyZZ(th, a, b, cycleWidth, degreesPerCycle);
		}
	}
}

static Gen* newKeyDegGen(Thread& th, P<Array> const& a, Arg b, Z cycleWidth, int degreesPerCycle)
{
	if (b.isVList()) {
		return new KeyDegVV(th, a, b, cycleWidth, degreesPerCycle);
	} else {
		if (a->isV()) {
			return new KeyDegVZ(th, a, b, cycleWidth, degreesPerCycle);
		} else {
			return new KeyDegZZ(th, a, b, cycleWidth, degreesPerCycle);
		}
	}
}

static Gen* newFoldAtGen(Thread& th, P<Array> const& a, Arg b)
{
	if (b.isVList()) {
		return new FoldAtGenVV(th, a, b);
	} else {
		if (a->isV()) {
			return new FoldAtGenVZ(th, a, b);
		} else {
			return new FoldAtGenZZ(th, a, b);
		}
	}
}

static Gen* newClipAtGen(Thread& th, P<Array> const& a, Arg b)
{
	if (b.isVList()) {
		return new ClipAtGenVV(th, a, b);
	} else {
		if (a->isV()) {
			return new ClipAtGenVZ(th, a, b);
		} else {
			return new ClipAtGenZZ(th, a, b);
		}
	}
}

V do_at(Thread& th, P<Array> const& a, Arg b)
{
	if (b.isReal()) {
		return a->at(b.asInt());
	} else if (b.isList()) {
		return new List(newAtGen(th, a, b));
	} else wrongType("at : b", "Real or List", b);

	return 0.;
}

V do_wrapAt(Thread& th, P<Array> const& a, Arg b)
{
	if (b.isReal()) {
		return a->wrapAt(b.asInt());
	} else if (b.isList()) {
		return new List(newWrapAtGen(th, a, b));
	} else wrongType("wrapAt : b", "Real or List", b);

	return 0.;
}

V do_degkey(Thread& th, P<Array> const& a, Arg b, Z cycleWidth, int degreesPerCycle)
{
	if (b.isReal()) {
		return degkey(b.asFloat(), a, cycleWidth, degreesPerCycle);
	} else if (b.isList()) {
		return new List(newDegKeyGen(th, a, b, cycleWidth, degreesPerCycle));
	} else wrongType("degkey : b", "Real or List", b);

	return 0.;
}

V do_keydeg(Thread& th, P<Array> const& a, Arg b, Z cycleWidth, int degreesPerCycle)
{
	if (b.isReal()) {
		return keydeg(b.asFloat(), a, cycleWidth, degreesPerCycle);
	} else if (b.isList()) {
		return new List(newKeyDegGen(th, a, b, cycleWidth, degreesPerCycle));
	} else wrongType("keydeg : b", "Real or List", b);

	return 0.;
}

V do_foldAt(Thread& th, P<Array> const& a, Arg b)
{
	if (b.isReal()) {
		return a->foldAt(b.asInt());
	} else if (b.isList()) {
		return new List(newFoldAtGen(th, a, b));
	} else wrongType("foldAt : b", "Real or List", b);

	return 0.;
}

V do_clipAt(Thread& th, P<Array> const& a, Arg b)
{
	if (b.isReal()) {
		return a->clipAt(b.asInt());
	} else if (b.isList()) {
		return new List(newClipAtGen(th, a, b));
	} else wrongType("clipAt : b", "Real or List", b);

	return 0.;
}

static void at_(Thread& th, Prim* prim)
{
	V i = th.pop();
	P<List> s = th.popList("at : s");

	if (!s->isFinite())
		indefiniteOp("at", "");

	s = s->pack(th);
	P<Array> const& a = s->mArray;

	V v = do_at(th, a, i);
	th.push(v);
}

static void wrapAt_(Thread& th, Prim* prim)
{
	V i = th.pop();
	P<List> s = th.popList("wrapAt : s");

	if (!s->isFinite())
		indefiniteOp("wrapAt", "");

	s = s->pack(th);
	P<Array> const& a = s->mArray;

	V v = do_wrapAt(th, a, i);
	th.push(v);
}

static void degkey_(Thread& th, Prim* prim)
{
	P<List> s = th.popList("degkey : s");

	V i = th.pop();

	if (!s->isFinite())
		indefiniteOp("degkey", "");

	s = s->pack(th);
	P<Array> const& a = s->mArray;

	int degreesPerCycle = (int)a->size()-1;
	if (degreesPerCycle <= 0) {
		post("degkey : scale has no degrees");
		throw errFailed;
	}
	Z cycleWidth = a->atz(degreesPerCycle);

	V v = do_degkey(th, a, i, cycleWidth, degreesPerCycle);
	th.push(v);
}

static void keydeg_(Thread& th, Prim* prim)
{
	P<List> s = th.popList("keydeg : s");

	V i = th.pop();

	if (!s->isFinite())
		indefiniteOp("keydeg", "");

	s = s->pack(th);
	P<Array> const& a = s->mArray;

	int degreesPerCycle = (int)a->size()-1;
	if (degreesPerCycle <= 0) {
		post("keydeg : scale has no degrees");
		throw errFailed;
	}
	Z cycleWidth = a->atz(degreesPerCycle);

	V v = do_keydeg(th, a, i, cycleWidth, degreesPerCycle);
	th.push(v);
}

static void foldAt_(Thread& th, Prim* prim)
{
	V i = th.pop();
	P<List> s = th.popList("foldAt : s");

	if (!s->isFinite())
		indefiniteOp("foldAt", "");

	s = s->pack(th);
	P<Array> const& a = s->mArray;

	V v = do_foldAt(th, a, i);
	th.push(v);
}

static void clipAt_(Thread& th, Prim* prim)
{
	V i = th.pop();
	P<List> s = th.popList("clipAt : s");

	if (!s->isFinite())
		indefiniteOp("clipAt", "");

	s = s->pack(th);
	P<Array> const& a = s->mArray;

	V v = do_clipAt(th, a, i);
	th.push(v);
}

#pragma mark CONVERSION

struct VGen : public Gen
{
	ZIn _a;

	VGen(Thread& th, Arg a) : Gen(th, itemTypeV, true), _a(a) {}
	virtual const char* TypeName() const override { return "VGen"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int astride;
			Z *a;
			if (_a(th, n,astride, a)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = *a;
					a += astride;
				}
				_a.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

struct ZGen : public Gen
{
	VIn _a;

	ZGen(Thread& th, Arg a) : Gen(th, itemTypeZ, true), _a(a) {}
	virtual const char* TypeName() const override { return "VGen"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int astride;
			V *a;
			if (_a(th, n,astride, a)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = a->asFloat();
					a += astride;
				}
				_a.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

static P<List> stringToZList(P<String> const& string)
{
	const char* s = string->s;
	size_t n = strlen(s);
	P<List> list = new List(itemTypeZ, n);

	Array* a = list->mArray();
	a->setSize(n);
	Z* z = a->z();

	for (size_t i = 0; i < n; ++i) {
		z[i] = s[i];
	}

	return list;
}

static P<List> stringToVList(P<String> const& string)
{
	const char* s = string->s;
	size_t n = strlen(s);
	P<List> list = new List(itemTypeV, n);

	Array* a = list->mArray();
	a->setSize(n);
	V* v = a->v();

	for (size_t i = 0; i < n; ++i) {
		v[i] = s[i];
	}

	return list;
}

static P<String> vlistToString(Thread& th, P<List> const& list)
{
	if (!list->isFinite())
		indefiniteOp("stream to string", "");

	P<List> packedList = list->pack(th);
	size_t n = packedList->length(th);
	char* s = (char*)malloc(n+1);

	P<String> string = new String(s, "dummy");

	Array* a = packedList->mArray();
	V* v = a->v();

	for (size_t i = 0; i < n; ++i) {
		s[i] = toascii((int)v[i].asFloat());
	}
	s[n] = 0;

	return string;
}

static P<String> zlistToString(Thread& th, P<List> const& list)
{
	if (!list->isFinite())
		indefiniteOp("signal to string", "");

	P<List> packedList = list->pack(th);
	size_t n = packedList->length(th);
	char* s = (char*)malloc(n+1);

	P<String> string = new String(s, "dummy");

	Array* a = packedList->mArray();
	Z* z = a->z();

	for (size_t i = 0; i < n; ++i) {
		s[i] = toascii((int)z[i]);
	}
	s[n] = 0;

	return string;
}

static void V_(Thread& th, Prim* prim)
{
	V a = th.pop();

	if (a.isZList()) {
		Gen* g = new VGen(th, a);
		th.push(new List(g));
	} else if (a.isString()) {
		P<String> s = (String*)a.o();
		th.push(stringToVList(s));
	} else {
		th.push(a);
	}
}

static void Z_(Thread& th, Prim* prim)
{
	V a = th.pop();

	if (a.isVList()) {
		Gen* g = new ZGen(th, a);
		th.push(new List(g));
	} else if (a.isString()) {
		P<String> s = (String*)a.o();
		th.push(stringToZList(s));
	} else {
		th.push(a);
	}
}

static void unspell_(Thread& th, Prim* prim)
{
	V a = th.pop();

	if (a.isVList()) {
		P<List> list = (List*)a.o();
		th.push(vlistToString(th, list));
	} else if (a.isZList()) {
		P<List> list = (List*)a.o();
		th.push(zlistToString(th, list));
	} else if (a.isString()) {
		th.push(a);
	} else {
		wrongType("unspell : list", "List or String", a);
	}
}

#pragma mark NUMERIC SERIES

struct Ever : public Gen
{
	V _val;

	Ever(Thread& th, Arg val) : Gen(th, itemTypeV, false), _val(val) {}

	virtual const char* TypeName() const override { return "Ever"; }

	virtual void pull(Thread& th) override
	{
		int n = mBlockSize;
		V* out = mOut->fulfill(n);
		V v = _val;
		for (int i = 0; i < n; ++i) {
			out[i] = v;
		}
		mOut = mOut->nextp();
	}
};

struct Everz : public Gen
{
	Z _val;

	Everz(Thread& th, Z val) : Gen(th, itemTypeZ, false), _val(val) {}

	virtual const char* TypeName() const override { return "Everz"; }

	virtual void pull(Thread& th) override
	{
		int n = mBlockSize;
		Z* out = mOut->fulfillz(n);
		Z z = _val;
		for (int i = 0; i < n; ++i) {
			out[i] = z;
		}
		mOut = mOut->nextp();
	}
};

extern UnaryOp* gUnaryOpPtr_recip;
extern UnaryOp* gUnaryOpPtr_cb;
extern BinaryOp* gBinaryOpPtr_plus;
extern BinaryOp* gBinaryOpPtr_mul;

struct By : public Gen
{
	V _start;
	V _step;

	By(Thread& th, Arg start, Arg step) : Gen(th, itemTypeV, false), _start(start), _step(step) {}

	virtual const char* TypeName() const override { return "By"; }

	virtual void pull(Thread& th) override
	{
		int n = mBlockSize;
		V* out = mOut->fulfill(n);
		for (int i = 0; i < n; ++i) {
			out[i] = _start;
			_start = _start.binaryOp(th, gBinaryOpPtr_plus, _step);
		}
		mOut = mOut->nextp();
	}
};

struct Byz : public Gen
{
	Z _start;
	Z _step;

	Byz(Thread& th, Z start, Z step) : Gen(th, itemTypeZ, false), _start(start), _step(step) {}

	virtual const char* TypeName() const override { return "Byz"; }

	virtual void pull(Thread& th) override
	{
		int n = mBlockSize;
		Z* out = mOut->fulfillz(n);
		Z start = _start;
		Z step = _step;
		for (int i = 0; i < n; ++i) {
			out[i] = start;
			start += step;
		}
		_start = start;
		mOut = mOut->nextp();
	}
};

struct Grow : public Gen
{
	V _start;
	V _step;

	Grow(Thread& th, Arg start, Arg step) : Gen(th, itemTypeV, false), _start(start), _step(step) {}

	virtual const char* TypeName() const override { return "Grow"; }

	virtual void pull(Thread& th) override
	{
		int n = mBlockSize;
		V* out = mOut->fulfill(n);
		for (int i = 0; i < n; ++i) {
			out[i] = _start;
			_start = _start.binaryOp(th, gBinaryOpPtr_mul, _step);
		}
		mOut = mOut->nextp();
	}
};

struct Growz : public Gen
{
	Z _start;
	Z _step;

	Growz(Thread& th, Z start, Z step) : Gen(th, itemTypeZ, false), _start(start), _step(step) {}

	virtual const char* TypeName() const override { return "Growz"; }

	virtual void pull(Thread& th) override
	{
		int n = mBlockSize;
		Z* out = mOut->fulfillz(n);
		Z start = _start;
		Z step = _step;
		for (int i = 0; i < n; ++i) {
			out[i] = start;
			start *= step;
		}
		_start = start;
		mOut = mOut->nextp();
	}
};

struct CubicLine : public Gen
{
	V _start;
	V _step;

	CubicLine(Thread& th, Arg start, Arg step) : Gen(th, itemTypeV, false), _start(start), _step(step) {}

	virtual const char* TypeName() const override { return "CubicLine"; }

	virtual void pull(Thread& th) override
	{
		int n = mBlockSize;
		V* out = mOut->fulfill(n);
		for (int i = 0; i < n; ++i) {
			V cubed = _start.unaryOp(th, gUnaryOpPtr_cb);
			out[i] = cubed;
			_start = _start.binaryOp(th, gBinaryOpPtr_plus, _step);
		}
		mOut = mOut->nextp();
	}
};

struct CubicLinez : public Gen
{
	Z _start;
	Z _step;

	CubicLinez(Thread& th, Z start, Z step) : Gen(th, itemTypeZ, false), _start(start), _step(step) {}

	virtual const char* TypeName() const override { return "CubicLinez"; }

	virtual void pull(Thread& th) override
	{
		int n = mBlockSize;
		Z* out = mOut->fulfillz(n);
		Z start = _start;
		Z step = _step;
		for (int i = 0; i < n; ++i) {
			out[i] = start*start*start;
			start += step;
		}
		_start = start;
		mOut = mOut->nextp();
	}
};

struct Inv : public Gen
{
	V _start;

	Inv(Thread& th) : Gen(th, itemTypeV, false), _start(1.) {}

	virtual const char* TypeName() const override { return "Inv"; }

	virtual void pull(Thread& th) override
	{
		int n = mBlockSize;
		V* out = mOut->fulfill(n);
		V vone = 1.;
		for (int i = 0; i < n; ++i) {
			V vout = _start.unaryOp(th, gUnaryOpPtr_recip);
			out[i] = vout;
			_start = _start.binaryOp(th, gBinaryOpPtr_plus, vone);
		}
		mOut = mOut->nextp();
	}
};

struct Invz : public Gen
{
	Z _start;

	Invz(Thread& th) : Gen(th, itemTypeZ, false), _start(1.) {}

	virtual const char* TypeName() const override { return "Invz"; }

	virtual void pull(Thread& th) override
	{
		int n = mBlockSize;
		Z* out = mOut->fulfillz(n);
		Z start = _start;
		for (int i = 0; i < n; ++i) {
			out[i] = 1. / start;
			start += 1.;
		}
		_start = start;
		mOut = mOut->nextp();
	}
};

struct NInv : public Gen
{
	V _start;
	int64_t _n;

	NInv(Thread& th, int64_t n) : Gen(th, itemTypeV, true), _start(1.), _n(n) {}

	virtual const char* TypeName() const override { return "NInv"; }

	virtual void pull(Thread& th) override
	{
		if (_n <= 0) {
			end();
		} else {
			int n = (int)std::min(_n, (int64_t)mBlockSize);
			V* out = mOut->fulfill(n);
			V vone = 1.;
			for (int i = 0; i < n; ++i) {
				V vout = _start.unaryOp(th, gUnaryOpPtr_recip);
				out[i] = vout;
				_start = _start.binaryOp(th, gBinaryOpPtr_plus, vone);
			}
			_n -= n;
			mOut = mOut->nextp();
		}
	}
};

struct NInvz : public Gen
{
	Z _start;
	int64_t _n;

	NInvz(Thread& th, int64_t n) : Gen(th, itemTypeZ, true), _start(1.), _n(n) {}

	virtual const char* TypeName() const override { return "NInvz"; }

	virtual void pull(Thread& th) override
	{
		if (_n <= 0) {
			end();
		} else {
			int n = (int)std::min(_n, (int64_t)mBlockSize);
			Z* out = mOut->fulfillz(n);
			Z start = _start;
			for (int i = 0; i < n; ++i) {
				out[i] = 1. / start;
				start += 1.;
			}
			_start = start;
			_n -= n;
			mOut = mOut->nextp();
		}
	}
};

struct NBy : public Gen
{
	V _start;
	V _step;
	int64_t _n;

	NBy(Thread& th, Arg start, Arg step, int64_t n) : Gen(th, itemTypeV, true), _start(start), _step(step), _n(n) {}

	virtual const char* TypeName() const override { return "NBy"; }

	virtual void pull(Thread& th) override
	{
		if (_n <= 0) {
			end();
		} else {
			int n = (int)std::min(_n, (int64_t)mBlockSize);
			V* out = mOut->fulfill(n);
			for (int i = 0; i < n; ++i) {
				out[i] = _start;
				_start = _start.binaryOp(th, gBinaryOpPtr_plus, _step);
			}
			_n -= n;
			mOut = mOut->nextp();
		}
	}
};

struct NByz : public Gen
{
	Z _start;
	Z _step;
	int64_t _n;

	NByz(Thread& th, Z start, Z step, int64_t n) : Gen(th, itemTypeZ, true), _start(start), _step(step), _n(n) {}

	virtual const char* TypeName() const override { return "NByz"; }

	virtual void pull(Thread& th) override
	{
		if (_n <= 0) {
			end();
		} else {
			int n = (int)std::min(_n, (int64_t)mBlockSize);
			Z* out = mOut->fulfillz(n);
			Z start = _start;
			Z step = _step;
			for (int i = 0; i < n; ++i) {
				out[i] = start;
				start += step;
			}
			_start = start;
			_n -= n;
			mOut = mOut->nextp();
		}
	}
};

struct NGrow : public Gen
{
	V _start;
	V _step;
	int64_t _n;

	NGrow(Thread& th, Arg start, Arg step, int64_t n) : Gen(th, itemTypeV, true), _start(start), _step(step), _n(n) {}

	virtual const char* TypeName() const override { return "NGrow"; }

	virtual void pull(Thread& th) override
	{
		if (_n <= 0) {
			end();
		} else {
			int n = (int)std::min(_n, (int64_t)mBlockSize);
			V* out = mOut->fulfill(n);
			for (int i = 0; i < n; ++i) {
				out[i] = _start;
				_start = _start.binaryOp(th, gBinaryOpPtr_mul, _step);
			}
			_n -= n;
			mOut = mOut->nextp();
		}
	}
};

struct NGrowz : public Gen
{
	Z _start;
	Z _step;
	int64_t _n;

	NGrowz(Thread& th, Z start, Z step, int64_t n) : Gen(th, itemTypeZ, true), _start(start), _step(step), _n(n) {}

	virtual const char* TypeName() const override { return "NGrowz"; }

	virtual void pull(Thread& th) override
	{
		if (_n <= 0) {
			end();
		} else {
			int n = (int)std::min(_n, (int64_t)mBlockSize);
			Z* out = mOut->fulfillz(n);
			Z start = _start;
			Z step = _step;
			for (int i = 0; i < n; ++i) {
				out[i] = start;
				start *= step;
			}
			_start = start;
			_n -= n;
			mOut = mOut->nextp();
		}
	}
};

struct NCubicLinez : public Gen
{
	Z _start;
	Z _step;
	int64_t _n;

	NCubicLinez(Thread& th, Z start, Z step, int64_t n) : Gen(th, itemTypeZ, true), _start(start), _step(step), _n(n) {}

	virtual const char* TypeName() const override { return "NCubicLinez"; }

	virtual void pull(Thread& th) override
	{
		if (_n <= 0) {
			end();
		} else {
			int n = (int)std::min(_n, (int64_t)mBlockSize);
			Z* out = mOut->fulfillz(n);
			Z start = _start;
			Z step = _step;
			for (int i = 0; i < n; ++i) {
				out[i] = start*start*start;
				start += step;
			}
			_start = start;
			_n -= n;
			mOut = mOut->nextp();
		}
	}
};

struct Fib : public Gen
{
	V _a;
	V _b;

	Fib(Thread& th, Arg a, Arg b) : Gen(th, itemTypeV, false), _a(a), _b(b) {}

	virtual const char* TypeName() const override { return "Fib"; }

	virtual void pull(Thread& th) override
	{
		int n = mBlockSize;
		V* out = mOut->fulfill(n);
		for (int i = 0; i < n; ++i) {
			V a = _a;
            out[i] = a;
            _a = _b;
            _b = a.binaryOp(th, gBinaryOpPtr_plus, _b);
		}
		mOut = mOut->nextp();
	}
};

struct Fibz : public Gen
{
	Z _a;
	Z _b;

	Fibz(Thread& th, Z a, Z b) : Gen(th, itemTypeZ, false), _a(a), _b(b) {}

	virtual const char* TypeName() const override { return "Fibz"; }

	virtual void pull(Thread& th) override
	{
        int n = mBlockSize;
		Z* out = mOut->fulfillz(n);
		Z a = _a;
		Z b = _b;
		for (int i = 0; i < n; ++i) {
			out[i] = a;
            Z aa = a;
            a = b;
            b += aa;
		}
		_a = a;
		_b = b;
		mOut = mOut->nextp();
    }
};

static void L_(Thread& th, Prim* prim)
{
	if (!th.top().isVList()) {
		th.push(new List(new Ever(th, th.pop())));
	}
}

static void L1_(Thread& th, Prim* prim)
{
	if (!th.top().isVList()) {
		P<List> list = new List(itemTypeV, 1);
		list->add(th.pop());
		th.push(list);
	}
}

static void ever_(Thread& th, Prim* prim)
{
	V value  = th.pop();

	Gen* g = new Ever(th, value);
	th.push(new List(g));
}

static void everz_(Thread& th, Prim* prim)
{
	Z value  = th.popFloat("everz : value");

	Gen* g = new Everz(th, value);
	th.push(new List(g));
}

static void by_(Thread& th, Prim* prim)
{
	V step = th.pop();
	V start = th.pop();

	Gen* g = new By(th, start, step);
	th.push(new List(g));
}

static void nby_(Thread& th, Prim* prim)
{
	V step = th.pop();
	V start = th.pop();
	int64_t n = th.popInt("nby : n");

	Gen* g = new NBy(th, start, step, n);
	th.push(new List(g));
}

static void to_(Thread& th, Prim* prim)
{
	Z end = th.popFloat("to : end");
	Z start = th.popFloat("to : start");
	Z step = start < end ? 1. : -1.;
	int64_t n = (int64_t)((end - start) * step) + 1;

	Gen* g = new NBy(th, start, step, n);
	th.push(new List(g));
}

static void toz_(Thread& th, Prim* prim)
{
	Z end = th.popFloat("toz : end");
	Z start = th.popFloat("toz : start");
	Z step = start < end ? 1. : -1.;
	int64_t n = (int64_t)((end - start) * step) + 1;

	Gen* g = new NByz(th, start, step, n);
	th.push(new List(g));
}

static void lindiv_(Thread& th, Prim* prim)
{
	Z end = th.popFloat("lindiv : end");
	Z start = th.popFloat("lindiv : start");
	int64_t n = th.popInt("lindiv : n");
	Z step = (end - start) / (n - 1);

	Gen* g = new NBy(th, start, step, n);
	th.push(new List(g));
}

static void lindivz_(Thread& th, Prim* prim)
{
	Z end = th.popFloat("lindivz : end");
	Z start = th.popFloat("lindivz : start");
	int64_t n = th.popInt("lindivz : n");
	Z step = (end - start) / (n - 1);

	Gen* g = new NByz(th, start, step, n);
	th.push(new List(g));
}

static void expdiv_(Thread& th, Prim* prim)
{
	Z end = th.popFloat("expdiv : end");
	Z start = th.popFloat("expdiv : start");
	int64_t n = th.popInt("expdiv : n");
	Z step = pow(end/start, 1. / (n - 1));

	Gen* g = new NGrow(th, start, step, n);
	th.push(new List(g));
}

static void expdivz_(Thread& th, Prim* prim)
{
	Z end = th.popFloat("expdivz : end");
	Z start = th.popFloat("expdivz : start");
	int64_t n = th.popInt("expdivz : n");
	Z step = pow(end/start, 1. / (n - 1));

	Gen* g = new NGrowz(th, start, step, n);
	th.push(new List(g));
}

static void lindiv1_(Thread& th, Prim* prim)
{
	Z end = th.popFloat("lindiv1 : end");
	Z start = th.popFloat("lindiv1 : start");
	int64_t n = th.popInt("lindiv1 : n");
	Z step = (end - start) / n;

	Gen* g = new NBy(th, start, step, n);
	th.push(new List(g));
}

static void lindiv1z_(Thread& th, Prim* prim)
{
	Z end = th.popFloat("lindiv1z : end");
	Z start = th.popFloat("lindiv1z : start");
	int64_t n = th.popInt("lindiv1z : n");
	Z step = (end - start) / n;

	Gen* g = new NByz(th, start, step, n);
	th.push(new List(g));
}

static void expdiv1_(Thread& th, Prim* prim)
{
	Z end = th.popFloat("expdiv1 : end");
	Z start = th.popFloat("expdiv1 : start");
	int64_t n = th.popInt("expdiv1 : n");
	Z step = pow(end/start, 1. / n);

	Gen* g = new NGrow(th, start, step, n);
	th.push(new List(g));
}

static void expdiv1z_(Thread& th, Prim* prim)
{
	Z end = th.popFloat("expdiv1z : end");
	Z start = th.popFloat("expdiv1z : start");
	int64_t n = th.popInt("expdiv1z : n");
	Z step = pow(end/start, 1. / n);

	Gen* g = new NGrowz(th, start, step, n);
	th.push(new List(g));
}

static void line_(Thread& th, Prim* prim)
{
	Z end = th.popFloat("line : end");
	Z start = th.popFloat("line : start");
	Z dur = th.popFloat("line : dur");
	double n = std::max(1., floor(dur * th.rate.sampleRate + .5));
	Z step = (end - start) / n;

	Gen* g = new NByz(th, start, step, (int64_t)n);
	th.push(new List(g));
}

static void xline_(Thread& th, Prim* prim)
{
	Z end = th.popFloat("xline : end");
	Z start = th.popFloat("xline : start");
	Z dur = th.popFloat("xline : dur");
	double n = std::max(1., floor(dur * th.rate.sampleRate + .5));

	Gen* g;
	if (sc_sgn(start) != sc_sgn(end) || start == 0. || end == 0.) {
		start = sc_sgn(start) * pow(fabs(start), kOneThird);
		end = sc_sgn(end) * pow(fabs(end), kOneThird);
		Z step = (end - start) / n;
		g = new NCubicLinez(th, start, step, (int64_t)n);
	} else {
		Z step = pow(end/start, 1. / n);
		g = new NGrowz(th, start, step, (int64_t)n);
	}
	th.push(new List(g));
}

static void grow_(Thread& th, Prim* prim)
{
	V step = th.pop();
	V start = th.pop();

	Gen* g = new Grow(th, start, step);
	th.push(new List(g));
}

static void ngrow_(Thread& th, Prim* prim)
{
	V step = th.pop();
	V start = th.pop();
	int64_t n = th.popInt("ngrow : n");

	Gen* g = new NGrow(th, start, step, n);
	th.push(new List(g));
}

static void byz_(Thread& th, Prim* prim)
{
	Z step  = th.popFloat("byz : step");
	Z start = th.popFloat("byz : start");

	Gen* g = new Byz(th, start, step);
	th.push(new List(g));
}

static void nbyz_(Thread& th, Prim* prim)
{
	Z step  = th.popFloat("nbyz : step");
	Z start = th.popFloat("nbyz : start");
	int64_t n = th.popInt("nbyz : n");

	Gen* g = new NByz(th, start, step, n);
	th.push(new List(g));
}

static void growz_(Thread& th, Prim* prim)
{
	Z step  = th.popFloat("growz : step");
	Z start = th.popFloat("growz : start");

	Gen* g = new Growz(th, start, step);
	th.push(new List(g));
}

static void ngrowz_(Thread& th, Prim* prim)
{
	Z step  = th.popFloat("ngrowz : step");
	Z start = th.popFloat("ngrowz : start");
	int64_t n = th.popInt("ngrowz : n");

	Gen* g = new NGrowz(th, start, step, n);
	th.push(new List(g));
}

static void ord_(Thread& th, Prim* prim)
{
	Gen* g = new By(th, 1., 1.);
	th.push(new List(g));
}

static void negs_(Thread& th, Prim* prim)
{
	Gen* g = new By(th, -1., -1.);
	th.push(new List(g));
}

static void nat_(Thread& th, Prim* prim)
{
	Gen* g = new By(th, 0., 1.);
	th.push(new List(g));
}

static void evens_(Thread& th, Prim* prim)
{
	Gen* g = new By(th, 0., 2.);
	th.push(new List(g));
}

static void odds_(Thread& th, Prim* prim)
{
	Gen* g = new By(th, 1., 2.);
	th.push(new List(g));
}

static void invs_(Thread& th, Prim* prim)
{
	Gen* g = new Inv(th);
	th.push(new List(g));
}

static void invz_(Thread& th, Prim* prim)
{
	Gen* g = new Invz(th);
	th.push(new List(g));
}

static void ninvs_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("ninvs : n");
	Gen* g = new NInv(th, n);
	th.push(new List(g));
}

static void ninvz_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("ninvz : n");
	Gen* g = new NInvz(th, n);
	th.push(new List(g));
}

static void ordz_(Thread& th, Prim* prim)
{
	Gen* g = new Byz(th, 1., 1.);
	th.push(new List(g));
}

static void negz_(Thread& th, Prim* prim)
{
	Gen* g = new Byz(th, -1., -1.);
	th.push(new List(g));
}

static void natz_(Thread& th, Prim* prim)
{
	Gen* g = new Byz(th, 0., 1.);
	th.push(new List(g));
}

static void evenz_(Thread& th, Prim* prim)
{
	Gen* g = new Byz(th, 0., 2.);
	th.push(new List(g));
}

static void oddz_(Thread& th, Prim* prim)
{
	Gen* g = new Byz(th, 1., 2.);
	th.push(new List(g));
}

static void fib_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a  = th.pop();

	Gen* g = new Fib(th, a, b);
	th.push(new List(g));
}

static void fibz_(Thread& th, Prim* prim)
{
	Z b = th.popFloat("fibz : b");
	Z a  = th.popFloat("fibz : a");

	Gen* g = new Fibz(th, a, b);
	th.push(new List(g));
}

struct Ints : public Gen
{
	Z _a;

	Ints(Thread& th) : Gen(th, itemTypeV, false), _a(0.) {}

	virtual const char* TypeName() const override { return "Ints"; }

	virtual void pull(Thread& th) override
	{
		int n = mBlockSize;
		V* out = mOut->fulfill(n);
        Z a = _a;
		for (int i = 0; i < n; ++i) {
            out[i] = a;
            if (a <= 0.) a = 1. - a;
            else a = -a;
		}
        _a = a;
		mOut = mOut->nextp();
	}
};

struct Intz : public Gen
{
	Z _a;

	Intz(Thread& th) : Gen(th, itemTypeZ, false), _a(0.) {}

	virtual const char* TypeName() const override { return "Intz"; }

	virtual void pull(Thread& th) override
	{
		int n = mBlockSize;
		Z* out = mOut->fulfillz(n);
        Z a = _a;
		for (int i = 0; i < n; ++i) {
            out[i] = a;
            if (a <= 0.) a = 1. - a;
            else a = -a;
		}
        _a = a;
		mOut = mOut->nextp();
	}
};

static void ints_(Thread& th, Prim* prim)
{
	Gen* g = new Ints(th);
	th.push(new List(g));
}

static void intz_(Thread& th, Prim* prim)
{
	Gen* g = new Intz(th);
	th.push(new List(g));
}

#include "primes.hpp"

struct Primes : Gen
{
	int byte;
	int bit;

	Primes(Thread& th) : Gen(th, itemTypeV, false), byte(-1), bit(0) {}

	virtual const char* TypeName() const override { return "Primes"; }

	virtual void pull(Thread& th) override
	{
		int n = mBlockSize;
		V* out = mOut->fulfill(n);
		for (int i = 0; i < n; ++i) {
            if (byte < 0) {
                out[i] = gLowPrimes[bit];
                if (++bit >= 10) {
                    byte = 0;
                    bit = 0;
                }
            } else {
                while (1) {
					if (byte >= kPrimesMaskSize) {
						setDone();
						produce(n - i);
						return;
					} else if (gPrimesMask[byte] & (1 << bit)) {
                        out[i] = 30 * (1 + byte) + gPrimeOffsets[bit];
						if (++bit >= 8) {
                            ++byte;
                            bit = 0;
                        }
                        break;
                    } else {
						if (++bit >= 8) {
                            ++byte;
                            bit = 0;
                        }
                    }
                }
            }
        }
		mOut = mOut->nextp();
    }
};

struct Primez : Gen
{
	int byte;
	int bit;

	Primez(Thread& th) : Gen(th, itemTypeZ, false), byte(-1), bit(0) {}

	virtual const char* TypeName() const override { return "Primez"; }

	virtual void pull(Thread& th) override
	{
		int n = mBlockSize;
		Z* out = mOut->fulfillz(n);
		for (int i = 0; i < n; ++i) {
            if (byte < 0) {
                out[i] = gLowPrimes[bit];
                if (++bit >= 10) {
                    byte = 0;
                    bit = 0;
                }
            } else {
                while (1) {
					if (byte >= kPrimesMaskSize) {
						setDone();
						produce(n - i);
						return;
                    } else if (gPrimesMask[byte] & (1 << bit)) {
                        out[i] = 30 * (1 + byte) + gPrimeOffsets[bit];
						if (++bit >= 8) {
                            ++byte;
                            bit = 0;
                        }
                        break;
                    } else {
                        if (++bit >= 8) {
                            ++byte;
                            bit = 0;
                        }
                    }
                }
            }
        }
		mOut = mOut->nextp();
    }
};

static void primes_(Thread& th, Prim* prim)
{
	Gen* g = new Primes(th);
	th.push(new List(g));
}

static void primez_(Thread& th, Prim* prim)
{
	Gen* g = new Primez(th);
	th.push(new List(g));
}

#pragma mark ORDERING

class Perms : public Gen
{
	std::vector<int> mOrder;
	std::vector<V> mItems;
	int64_t m;
public:

	Perms(Thread& th, P<Array> const& inItems)
		: Gen(th, itemTypeV, true)
	{
		mOrder.reserve(inItems->size());
		mItems.reserve(inItems->size());
		for (int i = 0; i < inItems->size(); ++i) {
			mItems.push_back(inItems->_at(i));
			mOrder.push_back(i);
		}
		numPerms();
	}

	void numPerms()
	{
		m = 1;
		for (int64_t i = 2; i <= (int64_t)mItems.size(); ++i) {
			m *= i;
		}
	}

	virtual const char* TypeName() const override { return "Perms"; }

	void pull(Thread& th) override
	{
		if (m <= 0) {
			end();
		} else {
			int n = (int)std::min(m, (int64_t)mBlockSize);
			V* out = mOut->fulfill(n);
			for (int i = 0; i < n; ++i) {
				const int len = (int)mItems.size();
				P<List> list = new List(itemTypeV, len);
				P<Array> arr = list->mArray;
				V* outItems = arr->v();

				for (int j = 0; j < len; ++j) {
					outItems[j] = mItems[mOrder[j]];
				}
				arr->setSize(len);
				out[i] = list;
				getNext();
			}
			m -= n;
			mOut = mOut->nextp();
		}
	}

	void getNext()
	{
		int N = (int)mOrder.size();
		int i = N - 1;
		while (mOrder[i-1] >= mOrder[i])
			i = i-1;

		if (i <= 0) return;

		int j = N;
		while (mOrder[j-1] <= mOrder[i-1])
			j = j-1;

		std::swap(mOrder[i-1], mOrder[j-1]);

		i++; j = N;
		while (i < j) {
			std::swap(mOrder[i-1], mOrder[j-1]);
			i++;
			j--;
		}
	}
};

class Permz : public Gen
{
	std::vector<int> mOrder;
	std::vector<Z> mItems;
	int64_t m;
public:

	Permz(Thread& th, P<Array> const& inItems)
		: Gen(th, itemTypeV, true)
	{
		mOrder.reserve(inItems->size());
		mItems.reserve(inItems->size());
		for (int i = 0; i < inItems->size(); ++i) {
			mItems.push_back(inItems->_atz(i));
			mOrder.push_back(i);
		}
		numPerms();
	}

	void numPerms()
	{
		m = 1;
		for (int64_t i = 2; i <= (int64_t)mItems.size(); ++i) {
			m *= i;
		}
	}

	virtual const char* TypeName() const override { return "Permz"; }

	void pull(Thread& th) override
	{
		if (m <= 0) {
			end();
		} else {
			int n = (int)std::min(m, (int64_t)mBlockSize);
			V* out = mOut->fulfill(n);
			for (int i = 0; i < n; ++i) {
				const int len = (int)mItems.size();
				P<List> list = new List(itemTypeZ, len);
				P<Array> arr = list->mArray;
				Z* outItems = arr->z();

				for (int j = 0; j < len; ++j) {
					outItems[j] = mItems[mOrder[j]];
				}
				arr->setSize(len);
				out[i] = list;
				getNext();
			}
			m -= n;
			mOut = mOut->nextp();
		}
	}

	void getNext()
	{
		int N = (int)mOrder.size();
		int i = N - 1;
		while (mOrder[i-1] >= mOrder[i])
			i = i-1;

		if (i <= 0) return;

		int j = N;
		while (mOrder[j-1] <= mOrder[i-1])
			j = j-1;

		std::swap(mOrder[i-1], mOrder[j-1]);

		i++; j = N;
		while (i < j) {
			std::swap(mOrder[i-1], mOrder[j-1]);
			i++;
			j--;
		}
	}
};

static void perms_(Thread& th, Prim* prim)
{
	P<List> a = th.popVList("perms : list");
	if (!a->isFinite())
		indefiniteOp("perms : list", "");

	a = a->pack(th);
    P<Array> arr = a->mArray;
	Gen* g = new Perms(th, arr);
	th.push(new List(g));
}

static void permz_(Thread& th, Prim* prim)
{
	P<List> a = th.popZList("permz : list");
	if (!a->isFinite())
		indefiniteOp("permz : list", "");

	a = a->pack(th);
    P<Array> arr = a->mArray;
	Gen* g = new Permz(th, arr);
	th.push(new List(g));
}

class PermsWithRepeatedItems : public Gen
{
	std::vector<V> mOrig;
	std::vector<V> mItems;
	bool mThereafter = false;
public:

	PermsWithRepeatedItems(Thread& th, P<Array> const& inItems)
		: Gen(th, itemTypeV, true)
	{
		mOrig.reserve(inItems->size());
		for (int i = 0; i < inItems->size(); ++i) {
			mOrig.push_back(inItems->_at(i));
		}
		mItems = mOrig;
	}

	virtual const char* TypeName() const override { return "PermsWithRepeatedItems"; }

	void pull(Thread& th) override
	{
		int n = mBlockSize;
		int framesRemaining = n;
		V* out = mOut->fulfill(n);
		for (int i = 0; i < n; ++i) {
			if (mThereafter) {
				if (getNext(th)) {
					setDone();
					break;
				}
			} else {
				mThereafter = true;
			}
			const int len = (int)mItems.size();
			P<List> list = new List(itemTypeV, len);
			P<Array> arr = list->mArray;
			V* outItems = arr->v();

			for (int j = 0; j < len; ++j) {
				outItems[j] = mItems[j];
			}
			arr->setSize(len);
			out[i] = list;
			--framesRemaining;
		}
		produce(framesRemaining);
	}

	bool getNext(Thread& th)
	{
		auto vless = [&](Arg a, Arg b){ return ::Compare(th, a, b) < 0; };
		next_permutation(mItems.begin(), mItems.end(), vless);

		for (size_t i = 0; i < mItems.size(); ++i) {
			if (!::Equals(th, mItems[i], mOrig[i])) return false;
		}
		return true;
	}
};

class PermsWithRepeatedItemsZ : public Gen
{
	std::vector<Z> mOrig;
	std::vector<Z> mItems;
	bool mThereafter = false;
public:

	PermsWithRepeatedItemsZ(Thread& th, P<Array> const& inItems)
		: Gen(th, itemTypeV, true)
	{
		mOrig.reserve(inItems->size());
		for (int i = 0; i < inItems->size(); ++i) {
			mOrig.push_back(inItems->_atz(i));
		}
		mItems = mOrig;
	}

	virtual const char* TypeName() const override { return "PermsWithRepeatedItemsZ"; }

	void pull(Thread& th) override
	{
		int n = mBlockSize;
		int framesRemaining = n;
		Z* out = mOut->fulfillz(n);
		for (int i = 0; i < n; ++i) {
			if (mThereafter) {
				if (getNext(th)) {
					setDone();
					break;
				}
			} else {
				mThereafter = true;
			}
			const int len = (int)mItems.size();
			P<List> list = new List(itemTypeV, len);
			P<Array> arr = list->mArray;
			V* outItems = arr->v();

			for (int j = 0; j < len; ++j) {
				outItems[j] = mItems[j];
			}
			arr->setSize(len);
			out[i] = list;
			--framesRemaining;
		}
		produce(framesRemaining);
	}

	bool getNext(Thread& th)
	{
		auto vless = [&](Arg a, Arg b){ return ::Compare(th, a, b) < 0; };
		next_permutation(mItems.begin(), mItems.end(), vless);

		for (size_t i = 0; i < mItems.size(); ++i) {
			if (!::Equals(th, mItems[i], mOrig[i])) return false;
		}
		return true;
	}
};

static void permswr_(Thread& th, Prim* prim)
{
	P<List> a = th.popVList("permswr : list");
	if (!a->isFinite())
		indefiniteOp("permswr : list", "");

	a = a->pack(th);
    P<Array> arr = a->mArray;
	Gen* g = new PermsWithRepeatedItems(th, arr);
	th.push(new List(g));
}

static void permzwr_(Thread& th, Prim* prim)
{
	P<List> a = th.popZList("permzwr : list");
	if (!a->isFinite())
		indefiniteOp("permzwr : list", "");

	a = a->pack(th);
    P<Array> arr = a->mArray;
	Gen* g = new PermsWithRepeatedItemsZ(th, arr);
	th.push(new List(g));
}

struct Repeat : Gen
{
    V _a;
	int64_t _m;

	Repeat(Thread& th, Arg a, int64_t m) : Gen(th, itemTypeV, m < LLONG_MAX), _a(a), _m(m) {}

	virtual const char* TypeName() const override { return "Repeat"; }

	virtual void pull(Thread& th) override {
		if (_m <= 0) {
			end();
		} else {
            int n = (int)std::min(_m, (int64_t)mBlockSize);
            V* out = mOut->fulfill(n);
            V a = _a;
            for (int i = 0; i < n; ++i) {
                out[i] = a;
            }
            _m -= n;
			mOut = mOut->nextp();
        }
	}

};

struct RepeatFun : Gen
{
    V _a;
	Z _b;
	int64_t _m;

	RepeatFun(Thread& th, Arg a, int64_t m) : Gen(th, itemTypeV, m < LLONG_MAX), _a(a), _b(0.), _m(m) {}

	virtual const char* TypeName() const override { return "Repeat"; }

	virtual void pull(Thread& th) override {
		if (_m <= 0) {
			end();
		} else {
            int n = (int)std::min(_m, (int64_t)mBlockSize);
            V* out = mOut->fulfill(n);
            for (int i = 0; i < n; ++i) {
				SaveStack ss(th);
				th.push(_b);
				_b += 1.;
				_a.apply(th);
                out[i] = th.pop();
            }
            _m -= n;
			mOut = mOut->nextp();
        }
	}

};

struct InfRepeatFun : Gen
{
    V _a;
	Z _b;

	InfRepeatFun(Thread& th, Arg a) : Gen(th, itemTypeV, false), _a(a), _b(0.) {}

	virtual const char* TypeName() const override { return "InfRepeatFun"; }

	virtual void pull(Thread& th) override {
		int n = mBlockSize;
		V* out = mOut->fulfill(n);
		for (int i = 0; i < n; ++i) {
			SaveStack ss(th);
			th.push(_b);
			_b += 1.;
			_a.apply(th);
			out[i] = th.pop();
		}
		mOut = mOut->nextp();
	}

};

struct Repeatz : Gen
{
    Z _a;
	int64_t _m;

	Repeatz(Thread& th, Z a, int64_t m) : Gen(th, itemTypeZ, true), _a(a), _m(m) {}

	virtual const char* TypeName() const override { return "Repeatz"; }

	virtual void pull(Thread& th) override {
		if (_m <= 0) {
			end();
		} else {
            int n = (int)std::min(_m, (int64_t)mBlockSize);
            Z* out = mOut->fulfillz(n);
            Z a = _a;
            for (int i = 0; i < n; ++i) {
                out[i] = a;
            }
            _m -= n;
			mOut = mOut->nextp();
        }
    }
};

struct RepeatFunz : Gen
{
    V _a;
	Z _b;
	int64_t _m;

	RepeatFunz(Thread& th, Arg a, int64_t m) : Gen(th, itemTypeZ, true), _a(a), _b(0.), _m(m) {}

	virtual const char* TypeName() const override { return "RepeatFunz"; }

	virtual void pull(Thread& th) override {
		if (_m <= 0) {
			end();
		} else {
            int n = (int)std::min(_m, (int64_t)mBlockSize);
            Z* out = mOut->fulfillz(n);
            for (int i = 0; i < n; ++i) {
				SaveStack ss(th);
				th.push(_b);
				_b += 1.;
				_a.apply(th);
                out[i] = th.pop().asFloat();
            }
            _m -= n;
			mOut = mOut->nextp();
        }
    }
};

struct InfRepeatFunz : Gen
{
    V _a;
	Z _b;

	InfRepeatFunz(Thread& th, Arg a) : Gen(th, itemTypeZ, false), _a(a), _b(0.) {}

	virtual const char* TypeName() const override { return "InfRepeatFunz"; }

	virtual void pull(Thread& th) override {
		int n = mBlockSize;
		Z* out = mOut->fulfillz(n);
		for (int i = 0; i < n; ++i) {
			SaveStack ss(th);
			th.push(_b);
			_b += 1.;
			_a.apply(th);
			out[i] = th.pop().asFloat();
		}
		mOut = mOut->nextp();
    }
};

struct RCyc : public Gen
{
	V _ref;
	P<List> _a0;
	P<List> _a;

	RCyc(Thread& th, Arg ref, P<List> const& a) : Gen(th, a->elemType, false), _ref(ref), _a0(a), _a(a) {}

	virtual const char* TypeName() const override { return "RCyc"; }

	virtual void pull(Thread& th) override
	{
		if (!_a) {
			V v = _ref.deref();
			if (v.isList()) {
				_a0 = (List*)v.o();
			}
			_a = _a0;
		}
		_a->force(th);
		mOut->fulfill(_a->mArray);
		_a = _a->next();
		mOut = mOut->nextp();
	}
};

struct Cyc : public Gen
{
	P<List> _a0;
	P<List> _a;

	Cyc(Thread& th, P<List> const& a) : Gen(th, a->elemType, false), _a0(a), _a(a) {}

	virtual const char* TypeName() const override { return "Cyc"; }

	virtual void pull(Thread& th) override
	{
		if (!_a) _a = _a0;
		_a->force(th);
		mOut->fulfill(_a->mArray);
		_a = _a->next();
		mOut = mOut->nextp();
	}
};

struct NCyc : public Gen
{
	P<List> _a0;
	P<List> _a;
	int64_t _n;

	NCyc(Thread& th, int64_t n, P<List> const& a) : Gen(th, a->elemType, true), _a0(a), _a(a), _n(n) {}

	virtual const char* TypeName() const override { return "Cyc"; }

	virtual void pull(Thread& th) override
	{
		if (!_a) {
			if (_n <= 1) {
				end();
				return;
			} else {
				_a = _a0;
				--_n;
			}
		}

		_a->force(th);
		mOut->fulfill(_a->mArray);
		_a = _a->next();
		mOut = mOut->nextp();
	}
};

static void repeat_(Thread& th, Prim* prim)
{
	Z x = th.popFloat("X : n");
	V a = th.pop();

	if (x <= 0.) {
		th.push(vm._nilv);
	} else {
		Gen* g;
		if (x >= (Z)LONG_MAX) {
			if (a.isFunOrPrim()) {
				g = new InfRepeatFun(th, a);
			} else {
				g = new Ever(th, a);
			}
		} else {
			int64_t n = (int64_t)floor(x + .5);
			if (a.isFunOrPrim()) {
				g = new RepeatFun(th, a, n);
			} else {
				g = new Repeat(th, a, n);
			}
		}
		th.push(new List(g));
	}
}

static void repeatz_(Thread& th, Prim* prim)
{
	Z x = th.popFloat("XZ : n");
	V a = th.pop();

	if (x <= 0.) {
		th.push(vm._nilv);
	} else {
		Gen* g;
		if (x >= (Z)LONG_MAX) {
			if (a.isFunOrPrim()) {
				g = new InfRepeatFunz(th, a);
			} else {
				g = new Everz(th, a.asFloat());
			}
		} else {
			int64_t n = (int64_t)floor(x + .5);
			if (a.isFunOrPrim()) {
				g = new RepeatFunz(th, a, n);
			} else {
				g = new Repeatz(th, a.asFloat(), n);
			}
		}
		th.push(new List(g));
	}
}

struct Silence : Gen
{
	int64_t _m;

	Silence(Thread& th, int64_t m) : Gen(th, itemTypeZ, true), _m(m) {}

	virtual const char* TypeName() const override { return "Silence"; }

	virtual void pull(Thread& th) override {
		if (_m <= 0) {
			end();
		} else {
            int n = (int)std::min(_m, (int64_t)mBlockSize);
            Z* out = mOut->fulfillz(n);
			memset(out, 0, n * sizeof(Z));
            _m -= n;
			mOut = mOut->nextp();
        }
    }
};

static void mum_(Thread& th, Prim* prim)
{
	Z t = th.popFloat("mum : duration");

	int64_t n = (int64_t)floor(.5 + th.rate.sampleRate * t);
	if (isinf(t) || (n <= 0 && t > 0.)) {
		th.push(new List(new Everz(th, 0.)));
	} else {
		Gen* g = new Silence(th, n);
		th.push(new List(g));
	}
}

static void cyc_(Thread& th, Prim* prim)
{
	V v = th.pop();

	if (!v.isList()) {
		th.push(v);
		return;
	}

	P<List> s = (List*)v.o();

    s->force(th);
    if (s->isEnd()) {
        th.push(s);
        return;
    }

	Gen* g = new Cyc(th, s);
	th.push(new List(g));
}

static void rcyc_(Thread& th, Prim* prim)
{
	V ref = th.pop();
	V v = ref.deref();
	if (!v.isList()) {
		wrongType("rcyc : ref get", "List", v);
	}

	P<List> list = (List*)v.o();

	Gen* g = new RCyc(th, ref, list);
	th.push(new List(g));
}

static void ncyc_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("ncyc : n");
	P<List> s = th.popList("ncyc : seq");

    s->force(th);
    if (s->isEnd()) {
        th.push(s);
        return;
    }

	if (n <= 0) {
		th.push(vm.getNil(s->elemType));
	} else {
		Gen* g = new NCyc(th, n, s);
		th.push(new List(g));
	}
}

struct Append : Gen
{
	P<List> _a;
	V _b;

	Append(Thread& th, P<List> const& a, Arg b, bool inFinite) : Gen(th, a->elemType, inFinite), _a(a), _b(b) {}
	virtual const char* TypeName() const override { return "Append"; }

	virtual void pull(Thread& th) override
	{
		if (_a) {
			_a->force(th);
			mOut->fulfill(_a->mArray);
			_a = _a->next();
			mOut = mOut->nextp();
		} else {
			if (_b.isFunOrPrim()) {
				SaveStack ss(th);
				_b.apply(th);
				_b = th.pop();
			}
			if (!_b.isList()) {
				post("$ : b is not a sequence  '%s'\n", _b.TypeName());
				end();
				return;
			}

			List* b = (List*)_b.o();
			if (elemType != b->elemType) {
				post("$ : b item type doesn't match\n");
				end();
				return;
			}
			setDone();
			mOut->link(th, b);
		}
	}
};

struct Cat : Gen
{
	VIn _a;
	VIn _b;

	Cat(Thread& th, Arg a, P<List> const& b) : Gen(th, itemTypeV, b->isFinite()), _a(a), _b(b)
	{
		V v;
		_b.one(th, v); // skip over a.
	}
	virtual const char* TypeName() const override { return "Cat"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int astride;
			V *a;
			if (_a(th, n,astride, a)) {
				V b;
				if (_b.one(th, b)) {
					setDone();
					break;
				} else {
					if (b.isFunOrPrim()) {
						SaveStack ss(th);
						try {
							b.apply(th);
						} catch (...) {
							setDone();
							produce(framesToFill);
							throw;
						}
						b = th.pop();
					}
					if (!b.isVList()) {
						setDone();
						break;
					}
					_a.set(b);
					continue;
				}
			}
			for (int i = 0; i < n; ++i) {
				out[i] = a[i];
			}
			_a.advance(n);
			framesToFill -= n;
			out += n;
		}
		produce(framesToFill);
	}
};

struct CatZ : Gen
{
	ZIn _a;
	VIn _b;

	// this makes the assumption that all of the sublists of b are finite! if they are not then this will not prevent infinite loops.
	CatZ(Thread& th, Arg a, P<List> const& b) : Gen(th, itemTypeZ, b->isFinite()), _a(a), _b(b)
	{
		V v;
		_b.one(th, v); // skip over a.
	}
	virtual const char* TypeName() const override { return "CatZ"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int astride;
			Z *a;
			if (_a(th, n,astride, a)) {
				V b;
				if (_b.one(th, b)) {
					setDone();
					break;
				} else {
					if (b.isFunOrPrim()) {
						SaveStack ss(th);
						try {
							b.apply(th);
						} catch (...) {
							setDone();
							produce(framesToFill);
							throw;
						}
						b = th.pop();
					}
					if (!b.isZList()) {
						setDone();
						break;
					}
					_a.set(b);
					continue;
				}
			}
			for (int i = 0; i < n; ++i) {
				out[i] = a[i];
			}
			_a.advance(n);
			framesToFill -= n;
			out += n;
		}
		produce(framesToFill);
	}
};

#include <stack>

struct Flat : Gen
{
	std::stack<VIn> in; // stack of list continuations

	Flat(Thread& th, Arg inA) : Gen(th, itemTypeV, inA.isFinite())
	{
		VIn vin(inA);
		in.push(vin);
	}
	virtual const char* TypeName() const override { return "Flat"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		VIn* vin = &in.top();
		for (int i = 0; framesToFill; ) {
			V a;
			if (vin->one(th, a)) {
				if (in.size() == 1) {
					setDone();
					break;
				} else {
					in.pop();
					vin = &in.top();
				}
			} else if (a.isVList()) {
				VIn vin2(a);
				in.push(vin2);
				vin = &in.top();
			} else {
				out[i++] = a;
				--framesToFill;
			}
		}
		produce(framesToFill);
	}
};

struct Flatten : Gen
{
	std::stack<VIn> in; // stack of list continuations
	size_t depth;

	Flatten(Thread& th, Arg inA, size_t inDepth) : Gen(th, itemTypeV, inA.isFinite()), depth(inDepth)
	{
		VIn vin(inA);
		in.push(vin);
	}
	virtual const char* TypeName() const override { return "Flat"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		VIn* vin = &in.top();
		for (int i = 0; framesToFill; ) {
			V a;
			if (vin->one(th, a)) {
				if (in.size() == 1) {
					setDone();
					break;
				} else {
					in.pop();
					vin = &in.top();
				}
			} else if (a.isVList() && in.size() <= depth) {
				VIn vin2(a);
				in.push(vin2);
				vin = &in.top();
			} else {
				out[i++] = a;
				--framesToFill;
			}
		}
		produce(framesToFill);
	}
};

struct Keep : Gen
{
	VIn _a;
	int64_t _n;

	Keep(Thread& th, int64_t n, Arg a) : Gen(th, itemTypeV, true), _a(a), _n(n) {}
	virtual const char* TypeName() const override { return "Keep"; }

	virtual void pull(Thread& th) override {
		if (_n <= 0) {
			end();
		} else {
            int framesToFill = (int)std::min(_n, (int64_t)mBlockSize);
            V* out = mOut->fulfill(framesToFill);
            _n -= framesToFill;
           while (framesToFill) {
                int n = framesToFill;
                int astride;
                V *a;
                if (_a(th, n,astride, a)) {
                    setDone();
                    break;
                } else {
                    for (int i = 0; i < n; ++i) {
                        out[i] = *a;
						a += astride;
                    }
                    _a.advance(n);
                    framesToFill -= n;
                    out += n;
                }
            }
            produce(framesToFill);
        }
	}
};

struct Take : Gen
{
	VIn _a;
	int64_t _n;

	Take(Thread& th, int64_t n, Arg a) : Gen(th, itemTypeV, true), _a(a), _n(n) {}
	virtual const char* TypeName() const override { return "Take"; }

	virtual void pull(Thread& th) override {
		if (_n <= 0) {
			end();
		} else {
            int framesToFill = (int)std::min(_n, (int64_t)mBlockSize);
            V* out = mOut->fulfill(framesToFill);
           while (framesToFill) {
                int n = framesToFill;
                int astride;
                V *a;
                if (_a(th, n,astride, a)) {
					produce(framesToFill);
					P<List> g = new List(new Repeat(th, 0., _n));
					setDone();
					mOut->link(th, g());
                    return;
                } else {
					_n -= framesToFill;
					for (int i = 0; i < n; ++i) {
                        out[i] = *a;
						a += astride;
                    }
                    _a.advance(n);
                    framesToFill -= n;
                    out += n;
               }
           }
           produce(framesToFill);
        }
	}
};

struct Keepz : Gen
{
	ZIn _a;
	int64_t _n;

	Keepz(Thread& th, int64_t n, Arg a) : Gen(th, itemTypeZ, true), _a(a), _n(n) {}
	virtual const char* TypeName() const override { return "Keepz"; }

	virtual void pull(Thread& th) override {
		if (_n <= 0) {
			end();
		} else {
            int framesToFill = (int)std::min(_n, (int64_t)mBlockSize);
            Z* out = mOut->fulfillz(framesToFill);
            _n -= framesToFill;
            while (framesToFill) {
                int n = framesToFill;
                int astride;
                Z *a;
                if (_a(th, n,astride, a)) {
                  setDone();
                    break;
                } else {
                    for (int i = 0; i < n; ++i) {
                        out[i] = *a;
						a += astride;
                    }
                    _a.advance(n);
                    framesToFill -= n;
                    out += n;
                }
            }
           produce(framesToFill);
        }
	}
};

struct Takez : Gen
{
	ZIn _a;
	int64_t _n;

	Takez(Thread& th, int64_t n, Arg a) : Gen(th, itemTypeZ, true), _a(a), _n(n) {}
	virtual const char* TypeName() const override { return "Takez"; }

	virtual void pull(Thread& th) override {
		if (_n <= 0) {
			end();
		} else {
            int framesToFill = (int)std::min(_n, (int64_t)mBlockSize);
            Z* out = mOut->fulfillz(framesToFill);
            while (framesToFill) {
                int n = framesToFill;
                int astride;
                Z *a;
                if (_a(th, n,astride, a)) {
					produce(framesToFill);
					P<List> g = new List(new Repeatz(th, 0., _n));
                    setDone();
					mOut->link(th, g());
                    return;
                } else {
					_n -= framesToFill;
					for (int i = 0; i < n; ++i) {
                        out[i] = *a;
						a += astride;
                    }
                    _a.advance(n);
                    framesToFill -= n;
                    out += n;
                }
            }
            produce(framesToFill);
        }
	}
};

static void append_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();

	if (a.isString() && b.isString()) {
		std::string s;
		a.print(th, s);
		b.print(th, s);
		th.push(new String(s.c_str()));
	} else if (a.isList()) {
		P<List> list = (List*)a.o();
		th.push(new List(new Append(th, list, b, leastFinite(a,b))));
	} else {
		wrongType("$ : a", "List or String", a);
	}
}

struct AppendSubs : Gen
{
	VIn _a;
	VIn _b;

	AppendSubs(Thread& th, Arg a, Arg b) : Gen(th, itemTypeV, mostFinite(a,b)), _a(a), _b(b) {}

	virtual const char* TypeName() const override { return "AppendSubs"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int astride, bstride;
			V *a, *b;
			if (_a(th, n,astride, a) || _b(th, n,bstride, b)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					SaveStack ss(th);
					if (!a->isList())
						wrongType("$$ : *a", "List", *a);

					List* aa = (List*)a->o();

					out[i] = new List(new Append(th, aa, *b, mostFinite(*a,*b)));
					a += astride;
					b += bstride;
				}
				_a.advance(n);
				_b.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

static void appendSubs_(Thread& th, Prim* prim)
{
	V b = th.popVList("$$ : b");
	V a = th.popVList("$$ : a");

    th.push(new List(new AppendSubs(th, a, b)));
}

static void cat_(Thread& th, Prim* prim)
{
	V v = th.pop();
	if (!v.isList()) {
		th.push(v);
		return;
	}

	P<List> b = (List*)v.o();

	b->force(th);
	if (b->isEnd()) {
		th.push(vm.getNil(b->elemType));
		return;
	}

	VIn a_(b);
	V a;
	if (a_.one(th, a)) {
		th.push(vm.getNil(b->elemType));
		return;
	}

	//V a = b->mArray->v()[0];

	if (a.isString()) {
		if (!b->isFinite())
			indefiniteOp("$/ : list of strings", "");

		std::string s;

		VIn in_(b);
		while (true) {
			V in;
			if (in_.one(th, in)) {
				th.push(new String(s.c_str()));
				return;
			}
			in.print(th, s);
		}

	} else if (!a.isList()) {
		wrongType("$/ : b", "List", a);
	}

	Gen* g;
	if (a.isVList()) g = new Cat(th, a, b);
	else g = new CatZ(th, a, b);
	th.push(new List(g));

}

static void flat_(Thread& th, Prim* prim)
{
	V a = th.pop();

	if (a.isVList()) th.push(new List(new Flat(th, a)));
	else th.push(a);
}

static void flatten_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("flatten : n");
	V a = th.pop();

	if (a.isVList()) th.push(new List(new Flatten(th, a, n)));
	else th.push(a);
}

static void N_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("N : n");
	V v = th.pop();

    if (v.isVList()) {
		if (n <= 0) v = vm._nilv;
        else v = new List(new Keep(th, n, v));
    } else if (v.isZList()) {
		if (n <= 0) v = vm._nilz;
        else v = new List(new Keepz(th, n, v));
	}

    th.push(v);
}

static void NZ_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("NZ : n");
	V v = th.pop();

    if (v.isZList()) {
		if (n <= 0) v = vm._nilz;
        else v = new List(new Keepz(th, n, v));
	}

    th.push(v);
}

static void T_(Thread& th, Prim* prim)
{
	Z t = th.popFloat("T : t");
	V v = th.pop();

	int64_t n = (int64_t)floor(.5 + th.rate.sampleRate * t);

    if (v.isVList()) {
		if (n <= 0) v = vm._nilv;
        else v = new List(new Keep(th, n, v));
    } else if (v.isZList()) {
		if (n <= 0) v = vm._nilz;
        else v = new List(new Keepz(th, n, v));
	}

    th.push(v);
}

static void take_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("take : n");
	P<List> s = th.popList("take : s");

    Gen* g;
	if (n > 0) {
		if (s->isVList())
			g = new Take(th, n, s);
		else
			g = new Takez(th, n, s);
		th.push(new List(g));
    } else if (n < 0) {
		if (!s->isFinite())
			indefiniteOp("take", "");

		s = s->pack(th);
		int64_t size = s->length(th);
		n = -n;

		List* s2 = new List(s->elemType, n);
		th.push(s2);
		s2->mArray->setSize(n);
		if (s->isVList()) {
			V* p = s2->mArray->v();
			V* q = s->mArray->v();
			if (size < n) {
				int64_t offset = n - size;
				for (int64_t i = 0; i < offset; ++i) p[i] = 0.;
				for (int64_t i = 0, j = offset; i < size; ++i, ++j) p[j] = q[i];
			} else {
				for (int64_t i = 0, j = size - n; i < n; ++i, ++j) p[i] = q[j];
			}
		} else {
			Z* p = s2->mArray->z();
			Z* q = s->mArray->z();
			size_t elemSize = s2->mArray->elemSize();
			if (size < n) {
				int64_t offset = n - size;
				memset(p, 0, offset * elemSize);
				memcpy(p + offset, q, size * elemSize);
			} else {
				memcpy(p, q + size - n, n * elemSize);
			}
		}
		return;

	} else {
		if (s->isVList())
			th.push(vm._nilv);
		else
			th.push(vm._nilz);
		return;

	}
}

static void skip_positive_(Thread& th, P<List>& list, int64_t n)
{
	if (n <= 0) return;

	int itemType = list->elemType;

	while (list && n > 0) {
		list->force(th);

		Array* a = list->mArray();
		int64_t asize = a->size();
		if (asize > n) {
			int64_t remain = asize - n;
			Array* a2 = new Array(list->elemType, remain);
			a2->setSize(remain);
			if (list->isVList()) {
				for (int64_t i = 0, j = n; i < remain; ++i, ++j) {
					a2->v()[i] = a->v()[j];
				}
			} else {
				memcpy(a2->z(), a->z() + n, remain * a->elemSize());
			}
			list = new List(a2, list->next());
			return;
		}
		n -= asize;
		list = list->next();
	}

	if (!list) {
		list = vm.getNil(itemType);
	}
}

static void skip_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("skip : n");
	P<List> s = th.popList("skip : s");

	if (n <= 0) {
		th.push(s);
		return;
	}

	skip_positive_(th, s, n);
	th.push(s);
}

static void skipT_(Thread& th, Prim* prim)
{
	Z t = th.popFloat(">T : t");
	P<List> s = th.popList(">T : s");

	int64_t n = (int64_t)floor(.5 + th.rate.sampleRate * t);

	skip_positive_(th, s, n);
	th.push(s);
}

struct Hops : Gen
{
	P<List> _a;
	BothIn _hop;
	bool _once = true;

	Hops(Thread& th, Arg hop, P<List> const& a) : Gen(th, itemTypeV, true), _a(a), _hop(hop) {}
	virtual const char* TypeName() const override { return "Hops"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		for (int i = 0; i < mBlockSize; ++i) {
			if (_once) {
				_once = false;
			} else {
				int64_t hop;
				if (_hop.onei(th, hop)) {
					setDone();
					break;
				} else {
					skip_positive_(th, _a, hop);
				}
			}
			out[i] = _a;
			framesToFill -= 1;
		}
		produce(framesToFill);
	}
};

struct HopTs : Gen
{
	P<List> _a;
	BothIn _hop;
	bool _once = true;

	HopTs(Thread& th, Arg hop, P<List> const& a) : Gen(th, itemTypeV, true), _a(a), _hop(hop) {}
	virtual const char* TypeName() const override { return "HopTs"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		for (int i = 0; i < framesToFill; ++i) {
			if (_once) {
				_once = false;
			} else {
				Z hop;
				if (_hop.onez(th, hop)) {
					setDone();
					break;
				} else {
					int64_t n = (int64_t)floor(.5 + th.rate.sampleRate * hop);
					skip_positive_(th, _a, n);
				}
			}
			out[i] = _a;
			framesToFill -= 1;
		}
		produce(framesToFill);
	}
};

static void hops_(Thread& th, Prim* prim)
{
	V n = th.pop();
	P<List> s = th.popList("N>> : list");

	th.push(new List(new Hops(th, n, s)));
}

static void hopTs_(Thread& th, Prim* prim)
{
	V n = th.pop();
	P<List> s = th.popList("T>> : list");

	th.push(new List(new HopTs(th, n, s)));
}

static void drop_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("drop : n");
	P<List> s = th.popList("drop : s");

	if (n == 0) {
		th.push(s);
	} else if (n > 0) {
		skip_positive_(th, s, n);
		th.push(s);
	} else {
		if (!s->isFinite())
			indefiniteOp("drop", "");

		s = s->pack(th);
		int64_t size = s->length(th);
		n = -n;

		int64_t remain = std::max(0LL, size - n);
		if (remain <= 0) {
			th.push(vm.getNil(s->elemType));
			return;
		}

		P<List> s2 = new List(s->elemType, remain);
		th.push(s2);
		s2->mArray->setSize(remain);
		size_t elemSize = s2->mArray->elemSize();
		if (s->isVList()) {
			V* y =  s->mArray->v();
			V* x = s2->mArray->v();
			for (int64_t i = 0; i < remain; ++i) {
				x[i] = y[i];
			}
		} else {
			memcpy(s2->mArray->z(), s->mArray->z(), remain * elemSize);
		}
	}
}

static void choff_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("choff : n");
	int64_t c = th.popInt("choff : c");
	V a = th.pop();

	P<List> s2 = new List(itemTypeV, n);
	s2->mArray->setSize(n);

	if (a.isVList()) {
		if (!a.isFinite())
			indefiniteOp("choff : a", "");

		P<List> aa = ((List*)a.o())->pack(th);
		int64_t m = aa->length(th);
		int64_t mn = std::min(m,n);
		for (int64_t i = 0; i < mn; ++i) {
			int64_t j = sc_imod(i+c, n);
			s2->mArray->put(j, aa->at(i));
		}
	} else {
		c = sc_imod(c, n);
		s2->mArray->put(c, a);
	}

	th.push(s2);
}

static int64_t countWhileTrue(Thread& th, List* list)
{
    int64_t n = 0;
    while (list) {
        list->force(th);

        Array* a = list->mArray();
        int64_t asize = a->size();

        for (int i = 0; i < asize; ++i) {
            if (a->at(i).isTrue()) ++n;
            else return n;
        }
        list = list->nextp();
    }
	return n;
}

static void skipWhile_(Thread& th, Prim* prim)
{
	V f = th.pop();
	P<List> s = th.popList("skipWhile : s");

    if (f.isList()) {
        int64_t n = countWhileTrue(th, (List*)f.o());
        skip_positive_(th, s, n);
		th.push(s);
    } else {

        List* list = s();

        while (1) {
            list->force(th);
            if (list->isEnd()) {
                th.push(vm.getNil(s->elemType));
                return;
            }

            Array* a = list->mArray();
            int64_t asize = a->size();

            for (int i = 0; i < asize; ++i) {
                V v;
                {
                    SaveStack ss(th);
                    th.push(a->at(i));
					f.apply(th);
                    v = th.pop();
                }
                if (v.isFalse()) {
                    if (i == 0) {
                        th.push(list);
                    } else {
                        int64_t remain = asize - i;
                        Array* a2 = new Array(s->elemType, remain);
                        th.push(new List(a2, list->next()));
                        a2->setSize(remain);
						if (a->isV()) {
							for (int64_t j = 0; j < remain; ++j) a2->v()[j] = a->v()[j+i];
						} else {
							memcpy(a2->v(), a->v() + i, remain * a->elemSize());
						}
                    }
                    return;
                }
            }
            list = list->nextp();
        }
        th.push(list);
    }
}

struct KeepWhile : Gen
{
	VIn _a;
	VIn _b;

	KeepWhile(Thread& th, Arg a, Arg b) : Gen(th, itemTypeV, true), _a(a), _b(b) {}
	virtual const char* TypeName() const override { return "KeepWhile"; }

	virtual void pull(Thread& th) override {
        int framesToFill = mBlockSize;
        V* out = mOut->fulfill(framesToFill);
        while (framesToFill && !mDone) {
            int n = framesToFill;
            int astride, bstride;
            V *a, *b;
            if (_a(th, n,astride, a) || _b(th, n,bstride, b)) {
                setDone();
                break;
            } else {
                int k = 0;
                for (int i = 0; i < n; ++i) {
					if (b->isFunOrPrim()) {
						SaveStack ss(th);
						th.push(*a);
						b->apply(th);
						V v = th.pop();
						if (v.isFalse()) {
							setDone();
							break;
						} else {
							out[k++] = *a;
						}
					} else {
						if (b->isFalse()) {
							setDone();
							break;
						} else {
							out[k++] = *a;
						}
					}
                    a += astride;
                    b += bstride;
                }
                _a.advance(n);
                _b.advance(n);
                framesToFill -= k;
                out += k;
            }
        }
        produce(framesToFill);
	}
};

struct KeepWhileZ : Gen
{
	ZIn _a;
	ZIn _b;

	KeepWhileZ(Thread& th, Arg a, Arg b) : Gen(th, itemTypeZ, true), _a(a), _b(b) {}
	virtual const char* TypeName() const override { return "KeepWhileZ"; }

	virtual void pull(Thread& th) override {
        int framesToFill = mBlockSize;
        Z* out = mOut->fulfillz(framesToFill);
        while (framesToFill && !mDone) {
            int n = framesToFill;
            int astride, bstride;
            Z *a, *b;
            if (_a(th, n,astride, a) || _b(th, n,bstride, b)) {
                setDone();
                break;
            } else {
                int k = 0;
                for (int i = 0; i < n; ++i) {
                    if (*b == 0.) {
                        setDone();
                        break;
                    } else {
                        out[k++] = *a;
                    }
                    a += astride;
                    b += bstride;
                }
                _a.advance(n);
                _b.advance(n);
                framesToFill -= k;
                out += k;
            }
        }
        produce(framesToFill);
	}
};

struct KeepWhileVZ : Gen
{
	VIn _a;
	ZIn _b;

	KeepWhileVZ(Thread& th, Arg a, Arg b) : Gen(th, itemTypeV, true), _a(a), _b(b) {}
	virtual const char* TypeName() const override { return "KeepWhileVZ"; }

	virtual void pull(Thread& th) override {
        int framesToFill = mBlockSize;
        V* out = mOut->fulfill(framesToFill);
        while (framesToFill && !mDone) {
            int n = framesToFill;
            int astride, bstride;
            V *a;
			Z *b;
            if (_a(th, n,astride, a) || _b(th, n,bstride, b)) {
                setDone();
                break;
            } else {
                int k = 0;
                for (int i = 0; i < n; ++i) {
                    if (*b == 0.) {
                        setDone();
                        break;
                    } else {
                        out[k++] = *a;
                    }
                    a += astride;
                    b += bstride;
                }
                _a.advance(n);
                _b.advance(n);
                framesToFill -= k;
                out += k;
            }
        }
        produce(framesToFill);
	}
};

struct KeepWhileZV : Gen
{
	ZIn _a;
	VIn _b;

	KeepWhileZV(Thread& th, Arg a, Arg b) : Gen(th, itemTypeZ, true), _a(a), _b(b) {}
	virtual const char* TypeName() const override { return "KeepWhileZV"; }

	virtual void pull(Thread& th) override {
        int framesToFill = mBlockSize;
        Z* out = mOut->fulfillz(framesToFill);
        while (framesToFill && !mDone) {
            int n = framesToFill;
            int astride, bstride;
            Z *a;
			V *b;
            if (_a(th, n,astride, a) || _b(th, n,bstride, b)) {
                setDone();
                break;
            } else {
                int k = 0;
                for (int i = 0; i < n; ++i) {
					if (b->isFunOrPrim()) {
						SaveStack ss(th);
						th.push(*a);
						b->apply(th);
						V v = th.pop();
						if (v.isFalse()) {
							setDone();
							break;
						} else {
							out[k++] = *a;
						}
					} else {
						if (b->isFalse()) {
							setDone();
							break;
						} else {
							out[k++] = *a;
						}
					}
                    a += astride;
                    b += bstride;
                }
                _a.advance(n);
                _b.advance(n);
                framesToFill -= k;
                out += k;
            }
        }
        produce(framesToFill);
	}
};

static void keepWhile_(Thread& th, Prim* prim)
{
	V f = th.pop();
	P<List> s = th.popList("keepWhile : s");

	if (s->isZ()) {
		if (f.isZList()) {
			th.push(new List(new KeepWhileZ(th, s, f)));
		} else {
			th.push(new List(new KeepWhileZV(th, s, f)));
		}
	} else {
		if (f.isZList()) {
			th.push(new List(new KeepWhileVZ(th, s, f)));
		} else {
			th.push(new List(new KeepWhile(th, s, f)));
		}
	}
}

struct Tog : Gen
{
	VIn _in[2];
	int tog;

	Tog(Thread& th, Arg a, Arg b) : Gen(th, itemTypeV, mostFinite(a,b)), tog(0) { _in[0] = a; _in[1] = b; }
	virtual const char* TypeName() const override { return "Tog"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			V v;
			if (_in[tog].one(th, v)) {
				produce(framesToFill);
				setDone();
				return;
			}
			*out++ = v;
			--framesToFill;
			tog = 1 - tog;
		}
		produce(framesToFill);
	}
};

struct Togz : Gen
{
	ZIn _a;
	ZIn _b;

	Togz(Thread& th, Arg a, Arg b) : Gen(th, itemTypeZ, mostFinite(a,b)), _a(a), _b(b) {}
	virtual const char* TypeName() const override { return "Togz"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill / 2;
			int astride, bstride;
			Z *a, *b;
			if (_a(th, n,astride, a) || _b(th, n,bstride, b)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					*out++ = *a;
					*out++ = *b;
					a += astride;
					b += bstride;
				}
				_a.advance(n);
				_b.advance(n);
				framesToFill -= 2*n;
			}
		}
		produce(framesToFill);
	}
};

static void tog_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();

	th.push(new List(new Tog(th, a, b)));
}

static void togz_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();

	th.push(new List(new Togz(th, a, b)));
}

struct Tog1 : Gen
{
	VIn _in[2];
	int tog;

	Tog1(Thread& th, Arg a, Arg b) : Gen(th, itemTypeV, mostFinite(a,b)), tog(0) { _in[0] = a; _in[1] = b; }
	virtual const char* TypeName() const override { return "Tog1"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			V v;
			if (_in[tog].one(th, v)) {
				produce(framesToFill);
				setDone();
				return;
			}
			*out++ = v;
			--framesToFill;
			tog = 1 - tog;
		}
		produce(framesToFill);
	}
};

struct Hang : Gen
{
	P<List> _a;
	V _b;

	Hang(Thread& th, P<List> const& a) : Gen(th, itemTypeV, false), _a(a) {}
	virtual const char* TypeName() const override { return "Hang"; }

	virtual void pull(Thread& th) override
	{
		if (_a) {
			_a->force(th);
			if (_a->isEnd())
				goto ended;
			mOut->fulfill(_a->mArray);
			if (_a->mArray->size()) {
				_b = _a->mArray->v()[_a->mArray->size() - 1];
			}
			_a = _a->next();
		} else {
ended:
			_a = nullptr;
			V* out = mOut->fulfill(mBlockSize);
			for (int i = 0; i < mBlockSize; ++i) {
				out[i] = _b;
			}
		}
		mOut = mOut->nextp();
	}
};

struct Hangz : Gen
{
	P<List> _a;
	Z _b;

	Hangz(Thread& th, P<List> const& a) : Gen(th, itemTypeZ, false), _a(a) {}
	virtual const char* TypeName() const override { return "Hangz"; }

	virtual void pull(Thread& th) override
	{
		if (_a) {
			_a->force(th);
			if (_a->isEnd())
				goto ended;
			mOut->fulfillz(_a->mArray);
			if (_a->mArray->size()) {
				_b = _a->mArray->z()[_a->mArray->size() - 1];
			}
			_a = _a->next();
		} else {
ended:
			_a = nullptr;
			Z* out = mOut->fulfillz(mBlockSize);
			for (int i = 0; i < mBlockSize; ++i) {
				out[i] = _b;
			}
		}
		mOut = mOut->nextp();
	}
};

static void hang_(Thread& th, Prim* prim)
{
	P<List> a = th.popList("hang : a");

	if (a->isV())
		th.push(new List(new Hang(th, a)));
	else
		th.push(new List(new Hangz(th, a)));
}

static void hangz_(Thread& th, Prim* prim)
{
	P<List> a = th.popZList("hangz : a");

	th.push(new List(new Hangz(th, a)));
}

static void histo_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("histo : n");
	P<List> a = th.popList("histo : list");

	if (!a->isFinite()) {
		indefiniteOp("histo : list", "");
	}

	a = a->pack(th);

	int64_t size = a->mArray->size();

	P<List>outList = new List(itemTypeZ, n);
	outList->mArray->setSize(n);
	Z* out = outList->mArray->z();
	memset(out, 0, sizeof(Z) * n);

	Z n1 = n - 1;
	if (a->isZ()) {
		Z* in = a->mArray->z();

		for (int64_t i = 0; i < size; ++i) {
			int64_t j = (int64_t)std::clamp(in[i], 0., n1);
			out[j] += 1.;
		}
	} else {
		V* in = a->mArray->v();
		for (int64_t i = 0; i < size; ++i) {
			int64_t j = (int64_t)std::clamp(in[i].asFloat(), 0., n1);
			out[j] += 1.;
		}
	}

	th.push(outList);

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma mark MAP FILTER REDUCE

struct Stutter : Gen
{
	VIn _a;
	BothIn _b;
	int n_;
	V aa_;

	Stutter(Thread& th, Arg a, Arg b) : Gen(th, itemTypeV, mostFinite(a,b)), _a(a), _b(b), n_(0) {}

	virtual const char* TypeName() const override { return "Stutter"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			if (n_) {
				int n = std::min(n_, framesToFill);
				for (int i = 0; i < n; ++i) {
					out[i] = aa_;
				}
				framesToFill -= n;
				n_ -= n;
				out += n;

				if (framesToFill == 0) break;
			}
			V b;
			if (_a.one(th, aa_) || _b.one(th, b)) {
				setDone();
				break;
			} else {
				if (b.isFunOrPrim()) {
					SaveStack ss(th);
					th.push(aa_);
					try {
						b.apply(th);
					} catch (...) {
						setDone();
						produce(framesToFill);
						throw;
					}
					b = th.pop();
				}
				n_ = b.asFloat();
			}
		}
		produce(framesToFill);
	}
};

struct Stutterz : Gen
{
	ZIn _a;
	BothIn _b;
	int n_;
	Z aa_;

	Stutterz(Thread& th, Arg a, Arg b) : Gen(th, itemTypeZ, mostFinite(a,b)), _a(a), _b(b), n_(0) {}

	virtual const char* TypeName() const override { return "Stutterz"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		Z a = aa_;
		while (framesToFill) {
			if (n_) {
				int n = std::min(n_, framesToFill);
				for (int i = 0; i < n; ++i) {
					out[i] = a;
				}
				framesToFill -= n;
				n_ -= n;
				out += n;

				if (framesToFill == 0) break;
			}
			V b;
			if (_a.onez(th, a) || _b.one(th, b)) {
				setDone();
				break;
			} else {
				if (b.isFunOrPrim()) {
					SaveStack ss(th);
					th.push(a);
					try {
						b.apply(th);
					} catch (...) {
						setDone();
						produce(framesToFill);
						throw;
					}
					b = th.pop();
				}
				n_ = b.asFloat();
			}
		}
		aa_ = a;
		produce(framesToFill);
	}
};

static void filter_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.popList("? : a");

	if (a.isVList()) {
		th.push(new List(new Stutter(th, a, b)));
	} else {
		th.push(new List(new Stutterz(th, a, b)));
	}
}

struct Change : Gen
{
	VIn _a;
	V _prev;

	Change(Thread& th, Arg a) : Gen(th, itemTypeV, a.isFinite()), _a(a), _prev(12347918239.19798729839470170) {}

	virtual const char* TypeName() const override { return "Change"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
        V a;
        for (int i = 0; framesToFill; ) {
            if (_a.one(th, a)) {
                setDone();
                break;
            }
            if (!a.Equals(th, _prev)) {
                out[i++] = a;
                --framesToFill;
                _prev = a;
            }
		}
		produce(framesToFill);
	}
};

struct Changez : Gen
{
	ZIn _a;
	Z _prev;

	Changez(Thread& th, Arg a) : Gen(th, itemTypeZ, a.isFinite()), _a(a), _prev(12347918239.19798729839470170) {}

	virtual const char* TypeName() const override { return "Changez"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
        Z a;
        for (int i = 0; framesToFill; ) {
            if (_a.onez(th, a)) {
                setDone();
                break;
            }
            if (a != _prev) {
                out[i++] = a;
                --framesToFill;
                _prev = a;
            }
		}
		produce(framesToFill);
	}
};

static void change_(Thread& th, Prim* prim)
{
	V a = th.popList("change : a");

	if (a.isVList()) {
		th.push(new List(new Change(th, a)));
	} else {
		th.push(new List(new Changez(th, a)));
	}
}

static void changez_(Thread& th, Prim* prim)
{
	V a = th.popZList("change : a");

    th.push(new List(new Changez(th, a)));
}

struct Spread : Gen
{
	VIn _a;
	BothIn _b;
	int n_;
	V aa_;

	Spread(Thread& th, Arg a, Arg b) : Gen(th, itemTypeV, mostFinite(a,b)), _a(a), _b(b), n_(0) {}

	virtual const char* TypeName() const override { return "Spread"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		V a = aa_;
		while (framesToFill) {
			if (n_) {
				int n = std::min(n_, framesToFill);
				for (int i = 0; i < n; ++i) {
					out[i] = 0.;
				}
				framesToFill -= n;
				n_ -= n;
				out += n;

				if (framesToFill == 0) break;
			}
			V b;
			if (_a.one(th, a) || _b.one(th, b)) {
				setDone();
				break;
			} else {
				if (b.isFunOrPrim()) {
					SaveStack ss(th);
					th.push(a);
					try {
						b.apply(th);
					} catch (...) {
						setDone();
						produce(framesToFill);
						throw;
					}
					b = th.pop();
				}
				n_ = b.asFloat();
                *out++ = a;
                --framesToFill;
			}
		}
		produce(framesToFill);
	}
};

struct Spreadz : Gen
{
	ZIn _a;
	BothIn _b;
	int n_;

	Spreadz(Thread& th, Arg a, Arg b) : Gen(th, itemTypeZ, mostFinite(a,b)), _a(a), _b(b), n_(0) {}

	virtual const char* TypeName() const override { return "Spreadz"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		Z a;
		while (framesToFill) {
			if (n_) {
				int n = std::min(n_, framesToFill);
				for (int i = 0; i < n; ++i) {
					out[i] = 0.;
				}
				framesToFill -= n;
				n_ -= n;
				out += n;

				if (framesToFill == 0) break;
			}
			V b;
			if (_a.onez(th, a) || _b.one(th, b)) {
				setDone();
				break;
			} else {
				if (b.isFunOrPrim()) {
					SaveStack ss(th);
					th.push(a);
					try {
						b.apply(th);
					} catch (...) {
						setDone();
						produce(framesToFill);
						throw;
					}
					b = th.pop();
				}
				n_ = b.asFloat();

                *out++ = a;
                --framesToFill;
			}
		}
		produce(framesToFill);
	}
};

static void spread_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();

	if (a.isVList()) {
		th.push(new List(new Spread(th, a, b)));
	} else {
		th.push(new List(new Spreadz(th, a, b)));
	}
}

static void spreadz_(Thread& th, Prim* prim)
{
	V b = th.popZIn("spreadz : b");
	V a = th.popZIn("spreadz : a");

    th.push(new List(new Spreadz(th, a, b)));
}

struct Expand : Gen
{
	VIn _a;
	BothIn _b;

	Expand(Thread& th, Arg a, Arg b) : Gen(th, itemTypeV, a.isFinite()), _a(a), _b(b) {}

	virtual const char* TypeName() const override { return "Expand"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
        for (int i = 0; framesToFill; ) {
			V b;
			if (_b.one(th, b)) {
				setDone();
				break;
			} else {
				if (b.isTrue()) {
					V a;
					if (_a.one(th, a)) {
						setDone();
						break;
					}
					out[i++] = a;
					--framesToFill;
				} else {
					out[i++] = 0.;
					--framesToFill;
				}
			}
		}
		produce(framesToFill);
	}
};

struct Expandz : Gen
{
	ZIn _a;
	BothIn _b;

	Expandz(Thread& th, Arg a, Arg b) : Gen(th, itemTypeZ, a.isFinite()), _a(a), _b(b) {}

	virtual const char* TypeName() const override { return "Expandz"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
        for (int i = 0; framesToFill; ) {
			V b;
			if (_b.one(th, b)) {
				setDone();
				break;
			} else {
				if (b.isTrue()) {
					Z a;
					if (_a.onez(th, a)) {
						setDone();
						break;
					}
					out[i++] = a;
					--framesToFill;
				} else {
					out[i++] = 0.;
					--framesToFill;
				}
			}
		}
		produce(framesToFill);
	}
};

static void expand_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();

	if (a.isVList()) {
		th.push(new List(new Expand(th, a, b)));
	} else {
		th.push(new List(new Expandz(th, a, b)));
	}
}

static void expandz_(Thread& th, Prim* prim)
{
	V b = th.popZIn("expandz : b");
	V a = th.popZIn("expandz : a");

    th.push(new List(new Expandz(th, a, b)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Clump : Gen
{
	VIn _a;
	BothIn _b;

	Clump(Thread& th, Arg a, Arg b) : Gen(th, itemTypeV, a.isFinite()), _a(a), _b(b) {}

	virtual const char* TypeName() const override { return "Clump"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			V b;
			if (_b.one(th, b)) {
				setDone();
				break;
			}
			int64_t n = b.asFloat();

			P<List> list = new List(itemTypeV, 1);
			for (int i = 0; i < n; ++i) {
				V a;
				if (_a.one(th, a)) {
					setDone();
					goto leave;
				}
				list->add(a);
			}

			*out++ = list;
			--framesToFill;
		}
leave:
		produce(framesToFill);
	}
};

struct Clumpz : Gen
{
	ZIn _a;
	BothIn _b;

	Clumpz(Thread& th, Arg a, Arg b) : Gen(th, itemTypeV, a.isFinite()), _a(a), _b(b) {}

	virtual const char* TypeName() const override { return "Clumpz"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			V b;
			if (_b.one(th, b)) {
				setDone();
				break;
			}
			int64_t n = b.asFloat();

			P<List> list = new List(itemTypeZ, 1);
			for (int i = 0; i < n; ++i) {
				Z a;
				if (_a.onez(th, a)) {
					setDone();
					goto leave;
				}
				list->addz(a);
			}

			*out++ = list;
			--framesToFill;
		}
leave:
		produce(framesToFill);
	}
};

static void clump_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();

	if (a.isVList()) {
		th.push(new List(new Clump(th, a, b)));
	} else {
		th.push(new List(new Clumpz(th, a, b)));
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

class ShortAs : public Gen
{
	VIn a_;
	VIn b_;
public:

	ShortAs(Thread& th, Arg a, Arg b) : Gen(th, itemTypeV, mostFinite(a, b)), a_(a), b_(b) {}

	virtual const char* TypeName() const override { return "ShortAs"; }

	void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int astride, bstride;
			V *a, *b;
			if (a_(th, n, astride, a) || b_(th, n, bstride, b)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				out[i] = *a;
				a += astride;
			}
			out += n;
			framesToFill -= n;
			a_.advance(n);
			b_.advance(n);
		}
		produce(framesToFill);
	}

};

class ShortAsZ : public Gen
{
	ZIn a_;
	ZIn b_;
public:

	ShortAsZ(Thread& th, Arg a, Arg b) : Gen(th, itemTypeZ, mostFinite(a, b)), a_(a), b_(b) {}

	virtual const char* TypeName() const override { return "ShortAsZ"; }

	void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int astride, bstride;
			Z *a, *b;
			if (a_(th, n, astride, a) || b_(th, n, bstride, b)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				out[i] = *a;
				a += astride;
			}
			out += n;
			framesToFill -= n;
			a_.advance(n);
			b_.advance(n);
		}
		produce(framesToFill);
	}

};

static void shortas_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();

	if (a.isVList() && b.isVList()) {
		th.push(new List(new ShortAs(th, a, b)));
	} else if (a.isZList() && b.isZList()) {
		th.push(new List(new ShortAsZ(th, a, b)));
	} else {
		wrongType("shortas : a, b must be same type", "two streams or two signals", a);
	}
}

class LongAs : public Gen
{
	VIn a_;
	VIn b_;
	V last;
public:

	LongAs(Thread& th, Arg a, Arg b) : Gen(th, itemTypeV, b.isFinite()), a_(a), b_(b) {}

	virtual const char* TypeName() const override { return "LongAs"; }

	void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int astride, bstride;
			V *a, *b;

			if (b_(th, n, bstride, b)) {
				setDone();
				break;
			}

			int n0 = n;
			if (a_(th, n, astride, a)) {
				n = n0;
				for (int i = 0; i < n; ++i) {
					out[i] = last;
				}
			} else {
				last = *(a + (n-1)*astride);
				for (int i = 0; i < n; ++i) {
					out[i] = *a;
					a += astride;
				}
				a_.advance(n);
			}
			out += n;
			framesToFill -= n;
			b_.advance(n);
		}
		produce(framesToFill);
	}

};

class LongAsZ : public Gen
{
	ZIn a_;
	ZIn b_;
	Z last;
public:

	LongAsZ(Thread& th, Arg a, Arg b) : Gen(th, itemTypeZ, b.isFinite()), a_(a), b_(b) {}

	virtual const char* TypeName() const override { return "LongAsZ"; }

	void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int astride, bstride;
			Z *a, *b;

			if (b_(th, n, bstride, b)) {
				setDone();
				break;
			}

			int n0 = n; // should ZIn::operator() return n = 0 if it is at end of stream??
			if (a_(th, n, astride, a)) {
				n = n0;
				for (int i = 0; i < n; ++i) {
					out[i] = last;
				}
			} else {
				last = *(a + (n-1)*astride);
				for (int i = 0; i < n; ++i) {
					out[i] = *a;
					a += astride;
				}
				a_.advance(n);
			}
			out += n;
			framesToFill -= n;
			b_.advance(n);
		}
		produce(framesToFill);
	}

};

static void longas_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();

	if (a.isVList() && b.isVList()) {
		th.push(new List(new LongAs(th, a, b)));
	} else if (a.isZList() && b.isZList()) {
		th.push(new List(new LongAsZ(th, a, b)));
	} else {
		wrongType("longas : a, b must be same type", "two streams or two signals", a);
	}
}

class LongAs0 : public Gen
{
	VIn a_;
	VIn b_;
public:

	LongAs0(Thread& th, Arg a, Arg b) : Gen(th, itemTypeV, b.isFinite()), a_(a), b_(b) {}

	virtual const char* TypeName() const override { return "LongAs0"; }

	void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int astride, bstride;
			V *a, *b;

			if (b_(th, n, bstride, b)) {
				setDone();
				break;
			}

			int n0 = n;
			if (a_(th, n, astride, a)) {
				n = n0;
				V zero(0.);
				for (int i = 0; i < n; ++i) {
					out[i] = zero;
				}
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = *a;
					a += astride;
				}
				a_.advance(n);
			}
			out += n;
			framesToFill -= n;
			b_.advance(n);
		}
		produce(framesToFill);
	}

};

class LongAs0Z : public Gen
{
	ZIn a_;
	ZIn b_;
public:

	LongAs0Z(Thread& th, Arg a, Arg b) : Gen(th, itemTypeZ, b.isFinite()), a_(a), b_(b) {}

	virtual const char* TypeName() const override { return "LongAs0Z"; }

	void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int astride, bstride;
			Z *a, *b;

			if (b_(th, n, bstride, b)) {
				setDone();
				break;
			}

			int n0 = n; // should ZIn::operator() return n = 0 if it is at end of stream??
			if (a_(th, n, astride, a)) {
				n = n0;
				for (int i = 0; i < n; ++i) {
					out[i] = 0.;
				}
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = *a;
					a += astride;
				}
				a_.advance(n);
			}
			out += n;
			framesToFill -= n;
			b_.advance(n);
		}
		produce(framesToFill);
	}

};

static void longas0_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();

	if (a.isVList() && b.isVList()) {
		th.push(new List(new LongAs0(th, a, b)));
	} else if (a.isZList() && b.isZList()) {
		th.push(new List(new LongAs0Z(th, a, b)));
	} else {
		wrongType("longas0 : a, b must be same type", "two streams or two signals", a);
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "Play.hpp"

static void play_(Thread& th, Prim* prim)
{
	V v = th.popList("play : list");
	playWithAudioUnit(th, v);
}

static void record_(Thread& th, Prim* prim)
{
	V filename = th.pop();
	V v = th.popList("record : list");
	recordWithAudioUnit(th, v, filename);
}

static void stop_(Thread& th, Prim* prim)
{
	stopPlaying();
}

static void stopDone_(Thread& th, Prim* prim)
{
	stopPlayingIfDone();
}

static void interleave(int stride, int numFrames, double* in, float* out)
{
	for (int f = 0, k = 0; f < numFrames; ++f, k += stride)
		out[k] = in[f];
}

static void deinterleave(int numChans, int numFrames, float* in, double** out)
{
	switch (numChans) {
		case 1 :
			for (int f = 0, k = 0; f < numFrames; ++f, ++k) {
				out[0][f] = in[k];
			}
			break;
		case 2 :
			for (int f = 0, k = 0; f < numFrames; ++f, k+=2) {
				out[0][f] = in[k  ];
				out[1][f] = in[k+1];
			}
			break;
		case 3 : // e.g. W X Y
			for (int f = 0, k = 0; f < numFrames; ++f, k+=3) {
				out[0][f] = in[k  ];
				out[1][f] = in[k+1];
				out[2][f] = in[k+2];
			}
			break;
		case 4 :
			for (int f = 0, k = 0; f < numFrames; ++f, k+=4) {
				out[0][f] = in[k  ];
				out[1][f] = in[k+1];
				out[2][f] = in[k+2];
				out[3][f] = in[k+3];
			}
			break;
		case 5 :
			for (int f = 0, k = 0; f < numFrames; ++f, k+=5) {
				out[0][f] = in[k  ];
				out[1][f] = in[k+1];
				out[2][f] = in[k+2];
				out[3][f] = in[k+3];
				out[4][f] = in[k+4];
			}
			break;
		case 6 :
			for (int f = 0, k = 0; f < numFrames; ++f, k+=6) {
				out[0][f] = in[k  ];
				out[1][f] = in[k+1];
				out[2][f] = in[k+2];
				out[3][f] = in[k+3];
				out[4][f] = in[k+4];
				out[5][f] = in[k+5];
			}
			break;
		default :
			for (int f = 0, k = 0; f < numFrames; ++f) {
				for (int c = 0; c < numChans; ++c, ++k) {
					out[c][f] = in[k];
				}
			}
			break;
	}
}

static const size_t gSessionTimeMaxLen = 256;
char gSessionTime[gSessionTimeMaxLen];

#include <time.h>

static void setSessionTime()
{
	time_t t;
	tm tt;
	time(&t);
	localtime_r(&t, &tt);
	snprintf(gSessionTime, gSessionTimeMaxLen, "%04d-%02d%02d-%02d%02d%02d",
		tt.tm_year+1900, tt.tm_mon+1, tt.tm_mday, tt.tm_hour, tt.tm_min, tt.tm_sec);
}

static void sfwrite_(Thread& th, Prim* prim)
{

	V filename = th.pop();

	V v = th.popList(">sf : channels");

	sfwrite(th, v, filename, false);
}

static void sfwriteopen_(Thread& th, Prim* prim)
{

	V filename = th.pop();

	V v = th.pop();

	sfwrite(th, v, filename, true);
}

static void sfread_(Thread& th, Prim* prim)
{

	V filename = th.popString("sf> : filename");

	sfread(th, filename, 0, -1);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
static void bench_(Thread& th, Prim* prim)
{
	ZIn in[kMaxSFChannels];

	int numChannels = 0;

	V v = th.popList("bench : channels");

	if (v.isZList()) {
		if (!v.isFinite()) indefiniteOp(">sf : s - indefinite number of frames", "");
		numChannels = 1;
		in[0].set(v);
	} else {
		if (!v.isFinite()) indefiniteOp(">sf : s - indefinite number of channels", "");
		P<List> s = (List*)v.o();
		s = s->pack(th);
		Array* a = s->mArray();
		numChannels = (int)a->size();
		if (numChannels > kMaxSFChannels)
			throw errOutOfRange;

		bool allIndefinite = true;
		for (int i = 0; i < numChannels; ++i) {
			V va = a->at(i);
			if (va.isFinite()) allIndefinite = false;
			in[i].set(va);
			va.o = nullptr;
		}

		s = nullptr;
		a = nullptr;

		if (allIndefinite) indefiniteOp(">sf : s - all channels have indefinite number of frames", "");
	}
	v.o = nullptr;

	double t0 = elapsedTime();
	bool done = false;
	int64_t framesFilled = 0;
	while (!done) {
		for (int i = 0; i < numChannels; ++i) {
			int n = kBufSize;
			bool imdone = in[i].bench(th, n);
			if (imdone) done = true;
			framesFilled += n;
		}
	}
	double t1 = elapsedTime();

	double secondsOfCPU = t1-t0;
	double secondsOfAudio = (double)framesFilled * th.rate.invSampleRate;
	double percentOfRealtime = 100. * secondsOfCPU / secondsOfAudio;

	post("bench:\n");
	post("  %f seconds of audio.\n", secondsOfAudio);
	post("  %f seconds of CPU.\n", secondsOfCPU);
	post("  %f %% of real time.\n", percentOfRealtime);

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include "Spectrogram.hpp"

std::atomic<int32_t> gSpectrogramFileCount = 0;

static void sgram_(Thread& th, Prim* prim)
{
	V filename = th.pop();
	Z dBfloor = fabs(th.popFloat("sgram : dBfloor"));
	P<List> list = th.popZList("sgram : signal");

	if (!list->isFinite()) {
		indefiniteOp("sgram : signal - indefinite number of frames", "");
	}

	char path[1024];
	if (filename.isString()) {
		const char* sgramDir = getenv("SAPF_SPECTROGRAMS");
		if (!sgramDir || strlen(sgramDir)==0) sgramDir = "/tmp";
		snprintf(path, 1024, "%s/%s-%d.jpg", sgramDir, ((String*)filename.o())->s, (int)floor(dBfloor + .5));
	} else {
		int32_t count = ++gSpectrogramFileCount;
		snprintf(path, 1024, "/tmp/sapf-%s-%04d.jpg", gSessionTime, count);
	}

	list = list->pack(th);
	P<Array> array = list->mArray;
	int64_t n = array->size();
	double* z = array->z();
	spectrogram((int)n, z, 3200, 11, path, -dBfloor);

	{
		char cmd[1100];
		snprintf(cmd, 1100, "open \"%s\"", path);
		system(cmd);
	}

}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static double bessi0(double x)
{
	//returns the modified Bessel function I_0(x) for any real x
	//from numerical recipes

	double ax, ans;
	double y;

	if((ax=fabs(x))<3.75){
		y=x/3.75;
		y *= y;
		ans =1.0+y*(3.5156229+y*(3.0899424+y*(1.2067492
			+y*(0.2659732+y*(0.360768e-1+y*0.45813e-2)))));
	}
	else{
		y=3.75/ax;
		ans = (exp(ax)/sqrt(ax))*(0.39894228+y*(0.1328592e-1
			+y*(0.225319e-2+y*(-0.157565e-2+y*(0.916281e-2
			+y*(-0.2057706e-1+y*(0.2635537e-1+y*(-0.1647633e-1
			+y*0.392377e-2))))))));
	}

	return ans;
}

static double kaiser_alpha(double atten)
{
	double alpha = 0.;
	if (atten > 50.)
		alpha = .1102 * (atten - 8.7);
	else if (atten >= 21.)
		alpha = .5842 * pow(atten - 21., .4) + .07886 * (atten - 21.);
	return alpha;
}

static void kaiser(size_t m, double *s, double alpha)
{
	if (m == 0) return;
	if (m == 1) {
		s[0] = 1.;
		return;
	}
	size_t n = m-1;
	double p = n / 2.;
	double rp = 1. / p;
	double rb = 1. / bessi0(alpha);

	for (size_t i = 0; i < m; ++i) {
		double x = (i-p) * rp;
		s[i] = rb * bessi0(alpha * sqrt(1. - x*x));
	}
}

static void kaiser_(Thread& th, Prim* prim)
{
	Z atten = fabs(th.popFloat("kaiser : stopband attenuation"));
	int64_t n = th.popInt("kaiser : n");

	P<List> out = new List(itemTypeZ, n);
	out->mArray->setSize(n);

	Z alpha = kaiser_alpha(atten);
	kaiser(n, out->mArray->z(), alpha);

	th.push(out);
}

static void hanning_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("hanning : n");

	P<List> out = new List(itemTypeZ, n);
	out->mArray->setSize(n);

	vDSP_hann_windowD(out->mArray->z(), n, 0);

	th.push(out);
}

static void hamming_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("hanning : n");

	P<List> out = new List(itemTypeZ, n);
	out->mArray->setSize(n);

	vDSP_hamm_windowD(out->mArray->z(), n, 0);

	th.push(out);
}

static void blackman_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("hanning : n");

	P<List> out = new List(itemTypeZ, n);
	out->mArray->setSize(n);

	vDSP_blkman_windowD(out->mArray->z(), n, 0);

	th.push(out);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Segment : public Gen
{
	ZIn in_;
	BothIn hop_;
	BothIn length_;
	int offset;
    Z fracsamp_;
    Z sr_;

	Segment(Thread& th, Arg in, Arg hop, Arg length)
        : Gen(th, itemTypeV, mostFinite(in, hop, length)), in_(in), hop_(hop), length_(length),
        fracsamp_(0.), sr_(th.rate.sampleRate)
	{
	}

	virtual const char* TypeName() const override { return "Segment"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		int framesFilled = 0;
		V* out = mOut->fulfill(framesToFill);
		for (int i = 0; i < framesToFill; ++i) {
			Z zlength, zhop;
			if (length_.onez(th, zlength)) {
				setDone();
				goto leave;
			}

			int length = (int)floor(sr_ * zlength + .5);
			P<List> segment = new List(itemTypeZ, length);
			segment->mArray->setSize(length);
			bool nomore = in_.fillSegment(th, length, segment->mArray->z());
			out[i] = segment;
			++framesFilled;
			if (nomore) {
				setDone();
				goto leave;
			}

			if (hop_.onez(th, zhop)) {
				setDone();
				goto leave;
			}

            Z fhop = sr_ * zhop + fracsamp_;
            Z ihop = floor(fhop);
            fracsamp_ = fhop - ihop;

			in_.hop(th, (int)ihop);
		}
	leave:
		produce(framesToFill - framesFilled);
	}

};

static void seg_(Thread& th, Prim* prim)
{
	V length = th.pop();
	V hop = th.pop();
	V in = th.popZIn("segment : in");

	th.push(new List(new Segment(th, in, hop, length)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

struct WinSegment : public Gen
{
	ZIn in_;
	BothIn hop_;
	P<Array> window_;
    int length_;
	int offset;
    Z fracsamp_;
    Z sr_;

	WinSegment(Thread& th, Arg in, Arg hop, P<Array> const& window)
        : Gen(th, itemTypeV, mostFinite(in, hop)), in_(in), hop_(hop), window_(window),
            length_((int)window_->size()),
        fracsamp_(0.), sr_(th.rate.sampleRate)
	{
	}

	virtual const char* TypeName() const override { return "WinSegment"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		int framesFilled = 0;
		V* out = mOut->fulfill(framesToFill);
		for (int i = 0; i < framesToFill; ++i) {
			Z zhop;

			P<List> segment = new List(itemTypeZ, length_);
			segment->mArray->setSize(length_);
            Z* segbuf = segment->mArray->z();
			bool nomore = in_.fillSegment(th, (int)length_, segbuf);
            vDSP_vmulD(segbuf, 1, window_->z(), 1, segbuf, 1, length_);
			out[i] = segment;
			++framesFilled;
			if (nomore) {
				setDone();
				goto leave;
			}

			if (hop_.onez(th, zhop)) {
				setDone();
				goto leave;
			}

            Z fhop = sr_ * zhop + fracsamp_;
            Z ihop = floor(fhop);
            fracsamp_ = fhop - ihop;

			in_.hop(th, (int)ihop);
		}
	leave:
		produce(framesToFill - framesFilled);
	}

};

static void wseg_(Thread& th, Prim* prim)
{
	P<List> window = th.popZList("wseg : window");
	V hop = th.pop();
	V in = th.popZIn("segment : in");

    window = window->pack(th);

	th.push(new List(new WinSegment(th, in, hop, window->mArray)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void fft_(Thread& th, Prim* prim)
{
	P<List> inImag = th.popZList("fft : imag");
	P<List> inReal = th.popZList("fft : real");

	if (!inReal->isFinite())
		indefiniteOp("fft : real", "");

	if (!inImag->isFinite())
		indefiniteOp("fft : imag", "");

	int n = (int)inReal->length(th);
	int m = (int)inImag->length(th);
	if (n != m) {
		post("fft : real and imag parts are different lengths.\n");
		throw errFailed;
	}
	if (!ISPOWEROFTWO64(n)) {
		post("fft : size is not a power of two.\n");
		throw errFailed;
	}

	inReal = inReal->pack(th);
	inImag = inImag->pack(th);

	P<List> outReal = new List(itemTypeZ, n);
	P<List> outImag = new List(itemTypeZ, n);
	outReal->mArray->setSize(n);
	outImag->mArray->setSize(n);

	fft(n, inReal->mArray->z(), inImag->mArray->z(), outReal->mArray->z(), outImag->mArray->z());

	th.push(outReal);
	th.push(outImag);
}

static void ifft_(Thread& th, Prim* prim)
{
	P<List> inImag = th.popZList("ifft : imag");
	P<List> inReal = th.popZList("ifft : real");

	if (!inReal->isFinite())
		indefiniteOp("ifft : real", "");

	if (!inImag->isFinite())
		indefiniteOp("ifft : imag", "");

	int n = (int)inReal->length(th);
	int m = (int)inImag->length(th);
	if (n != m) {
		post("ifft : real and imag parts are different lengths.\n");
		throw errFailed;
	}
	if (!ISPOWEROFTWO64(n)) {
		post("ifft : size is not a power of two.\n");
		throw errFailed;
	}

	inReal = inReal->pack(th);
	inImag = inImag->pack(th);

	P<List> outReal = new List(itemTypeZ, n);
	P<List> outImag = new List(itemTypeZ, n);
	outReal->mArray->setSize(n);
	outImag->mArray->setSize(n);

	ifft(n, inReal->mArray->z(), inImag->mArray->z(), outReal->mArray->z(), outImag->mArray->z());

	th.push(outReal);
	th.push(outImag);
}

struct Add : Gen
{
	P<List> _a;
	V _b;

	Add(Thread& th, P<List> const& a, Arg b) : Gen(th, itemTypeV, a->isFinite()), _a(a), _b(b) {}
	virtual const char* TypeName() const override { return "Add"; }

	virtual void pull(Thread& th) override
	{
		if (_a) {
			_a->force(th);
			if (_a->isEnd())
				goto ended;
			mOut->fulfill(_a->mArray);
			_a = _a->next();
		} else {
ended:
			V* out = mOut->fulfill(1);
			out[0] = _b;
			setDone();
		}
		mOut = mOut->nextp();
	}
};

struct Addz : Gen
{
	P<List> _a;
	Z _b;

	Addz(Thread& th, P<List> const& a, Z b) : Gen(th, itemTypeZ, a->isFinite()), _a(a), _b(b) {}
	virtual const char* TypeName() const override { return "Addz"; }

	virtual void pull(Thread& th) override
	{
		if (_a) {
			_a->force(th);
			if (_a->isEnd())
				goto ended;
			mOut->fulfillz(_a->mArray);
			_a = _a->next();
		} else {
ended:
			Z* out = mOut->fulfillz(1);
			out[0] = _b;
			setDone();
		}
		mOut = mOut->nextp();
	}
};

static void add_(Thread& th, Prim* prim)
{
	V item = th.pop();
	P<List> list = th.popList("add : list");
	if (list->isZ()) {
		th.push(new List(new Addz(th, list, item.asFloat())));
	} else {
		th.push(new List(new Add(th, list, item)));
	}
}

static void empty_(Thread& th, Prim* prim)
{
	P<List> list = th.popList("head : list");
	list->force(th);

	P<Array> array = list->mArray;
	int64_t size = array->size();
	th.pushBool(size==0);
}

static void nonempty_(Thread& th, Prim* prim)
{
	P<List> list = th.popList("head : list");
	list->force(th);

	P<Array> array = list->mArray;
	int64_t size = array->size();
	th.pushBool(size!=0);
}

static void head_(Thread& th, Prim* prim)
{
	P<List> list = th.popList("head : list");
	list->force(th);

	BothIn in(list);
	V v;
	if (in.one(th, v)) {
		throw errOutOfRange;
	}

	th.push(v);
}

static void tail_(Thread& th, Prim* prim)
{
	P<List> list = th.popList("tail : list");
	skip_positive_(th, list, 1);
	th.push(list);
}

static void uncons_(Thread& th, Prim* prim)
{
	P<List> list = th.popList("tail : list");
	list->force(th);

	BothIn in(list);
	V head;
	if (in.one(th, head)) {
		throw errOutOfRange;
	}

	skip_positive_(th, list, 1);

	th.push(list);
	th.push(head);
}

class Cons : public Gen
{
	V fun;
public:
	Cons(Thread& th, Arg inFun) : Gen(th, itemTypeV, false), fun(inFun) {}

	virtual const char* TypeName() const override { return "Cons"; }

	virtual void pull(Thread& th) override {
		SaveStack ss(th);
		fun.apply(th);
		V v = th.pop();
		if (v.isList()) {
			setDone();
			mOut->link(th, (List*)v.o());
		} else {
			end();
		}
	}
};

static V cons(Thread& th, Arg head, Arg tail)
{
	if (tail.isFunOrPrim()) {
		P<Array> array = new Array(itemTypeV, 1);
		array->add(head);
		return new List(array, new List(new Cons(th, tail)));
	} else if (tail.isList()) {

		P<List> list = (List*)tail.o();

		list->force(th);

		int64_t size = list->mArray->size();

		P<Array> array = list->mArray;
		P<Array> newArray = new Array(list->ItemType(), size+1);

		P<List> newList = new List(newArray, list->next());

		newArray->add(head);

		if (list->isZ()) {
			for (int i = 0; i < size; ++i) {
				Z z = array->atz(i);
				newArray->add(z);
			}
		} else {
			for (int i = 0; i < size; ++i) {
				V v = array->at(i);
				newArray->add(v);
			}
		}

		return newList;
	} else {
		wrongType("cons : list", "List or Fun", tail);
	}
}

static void cons_(Thread& th, Prim* prim)
{
	V head = th.pop();
	V tail = th.pop();

	th.push(cons(th, head, tail));
}

static void pack_(Thread& th, Prim* prim)
{
	P<List> list = th.popList("pack : list");
	th.push(list->pack(th));
}

static void packed_(Thread& th, Prim* prim)
{
	P<List> list = th.popList("packed : list");
	th.pushBool(list->isPacked());
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Scan : Gen
{
	VIn list_;
    V fun_;
    V val_;

	Scan(Thread& th, Arg list, Arg fun, Arg val) : Gen(th, itemTypeV, list.isFinite()), list_(list), fun_(fun), val_(val) {}

	virtual const char* TypeName() const override { return "Scan"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			V* in;
			int instride;
			int n = framesToFill;
			if (list_(th, n, instride, in)) {
				setDone();
				break;
			}
			for (int i = 0; i < n; ++i) {
				SaveStack ss(th);
				th.push(*in);
				th.push(val_);
				fun_.apply(th);
				val_ = th.pop();
				out[i] = val_;
				in += instride;
			}
			framesToFill -= n;
			out += n;
			list_.advance(n);
		}
		produce(framesToFill);
	}
};

static void scan_(Thread& th, Prim* prim)
{
	V fun = th.pop();
	V value = th.pop();
	V list = th.pop();
	th.push(new List(new Scan(th, list, fun, value)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Scan1 : Gen
{
	VIn list_;
    V fun_;
    V val_;
	bool once_;

	Scan1(Thread& th, Arg list, Arg fun) : Gen(th, itemTypeV, list.isFinite()), list_(list), fun_(fun), once_(true) {}

	virtual const char* TypeName() const override { return "Scan"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		if (once_) {
			once_ = false;
			if (list_.one(th, val_)) {
				setDone();
				produce(framesToFill);
				return;
			}
			*out++ = val_;
			--framesToFill;
		}
		while (framesToFill) {
			V* in;
			int instride;
			int n = framesToFill;
			if (list_(th, n, instride, in)) {
				setDone();
				break;
			}
			for (int i = 0; i < n; ++i) {
				SaveStack ss(th);
				th.push(*in);
				th.push(val_);
				fun_.apply(th);
				val_ = th.pop();
				out[i] = val_;
				in += instride;
			}
			framesToFill -= n;
			out += n;
			list_.advance(n);
		}
		produce(framesToFill);
	}
};

static void scan1_(Thread& th, Prim* prim)
{
	V fun = th.pop();
	V list = th.pop();
	th.push(new List(new Scan1(th, list, fun)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Iter : Gen
{
    V fun_;
    V val_;
    Z index = 0.;

	Iter(Thread& th, Arg fun, Arg val) : Gen(th, itemTypeV, true), fun_(fun), val_(val) {}

	virtual const char* TypeName() const override { return "Iter"; }

	virtual void pull(Thread& th) override {
		int n = mBlockSize;
		V* out = mOut->fulfill(n);
		for (int i = 0; i < n; ++i) {
			out[i] = val_;
			SaveStack ss(th);
			th.push(val_);
			if (fun_.takes() == 2) {
				th.push(index);
				index += 1.;
			}
			fun_.apply(th);
			val_ = th.pop();
		}
		mOut = mOut->nextp();
	}
};

struct NIter : Gen
{
    V fun_;
    V val_;
	int64_t n_;
    Z index = 0.;

	NIter(Thread& th, Arg fun, Arg val, int64_t n) : Gen(th, itemTypeV, false), fun_(fun), val_(val), n_(n) {}

	virtual const char* TypeName() const override { return "NIter"; }

	virtual void pull(Thread& th) override {
		if (n_ <= 0) {
			end();
			return;
		}

		int n = (int)std::min(n_, (int64_t)mBlockSize);
		V* out = mOut->fulfill(n);
		for (int i = 0; i < n; ++i) {
			out[i] = val_;
			SaveStack ss(th);
			th.push(val_);
			if (fun_.takes() == 2) {
				th.push(index);
				index += 1.;
			}
			fun_.apply(th);
			val_ = th.pop();
		}
		n_ -= n;
		mOut = mOut->nextp();
	}
};

static void iter_(Thread& th, Prim* prim)
{
	V fun = th.pop();
	V value = th.pop();

	th.push(new List(new Iter(th, fun, value)));
}

static void itern_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("itern : n");
	V fun = th.pop();
	V value = th.pop();

	th.push(new List(new NIter(th, fun, value, n)));
}

static void reduce_(Thread& th, Prim* prim)
{
	V fun = th.pop();
	V value = th.pop();
	P<List> list = th.popList("reduce : list");
	if (!list->isFinite()) {
		indefiniteOp("reduce : list", "");
	}

	BothIn in_(list);
	while (true) {
		V in;
		if (in_.one(th, in)) {
			th.push(value);
			return;
		}

		SaveStack ss(th);
		th.push(in);
		th.push(value);
		fun.apply(th);
		value = th.pop();
	}
}

static void reduce1_(Thread& th, Prim* prim)
{
	V fun = th.pop();

	P<List> list = th.popList("reduce : list");
	if (!list->isFinite()) {
		indefiniteOp("reduce : list", "");
	}

	BothIn in_(list);
	V value;
	if (in_.one(th, value)) {
		th.push(value);
		return;
	}

	while (true) {
		V in;
		if (in_.one(th, in)) {
			th.push(value);
			return;
		}

		SaveStack ss(th);
		th.push(in);
		th.push(value);
		fun.apply(th);
		value = th.pop();
	}
}

static void chain_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("chain : n");
	V fun = th.pop();
	V value = th.pop();

	bool pushIndex = fun.takes() == 2;

	for (int64_t i = 0; i < n; ++i) {
		SaveStack ss(th);
		th.push(value);
		if (pushIndex) th.push(i);
		fun.apply(th);
		value = th.pop();
	}
	th.push(value);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Merge : Gen
{
	VIn a_;
	VIn b_;
    V fun_;
	V aa, bb;
	bool flag = true;
	bool once = true;

	Merge(Thread& th, Arg a, Arg b, Arg fun) : Gen(th, itemTypeV, leastFinite(a, b)), a_(a), b_(b), fun_(fun) {}

	virtual const char* TypeName() const override { return "Merge"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		for (int i = 0; i < framesToFill; ++i) {
			SaveStack ss(th);
			if (once) {
				once = false;
				if (a_.one(th, aa)) {
					produce(framesToFill);
					b_.link(th, mOut);
					setDone();
					return;
				}
				if (b_.one(th, bb)) {
					out[i] = aa;
					produce(framesToFill - i - 1);
					a_.link(th, mOut);
					setDone();
					return;
				}
			} else if (flag) {
				// last produced was a.
				if (a_.one(th, aa)) {
					out[i] = bb;
					produce(framesToFill - i - 1);
					b_.link(th, mOut);
					setDone();
					return;
				}
			} else {
				// last produced was b.
				if (b_.one(th, bb)) {
					out[i] = aa;
					produce(framesToFill - i - 1);
					a_.link(th, mOut);
					setDone();
					return;
				}
			}
			th.push(aa);
			th.push(bb);
			fun_.apply(th);
			flag = th.pop().isTrue();
			if (flag) {
				out[i] = aa;
				aa = 0.;
			} else {
				out[i] = bb;
				bb = 0.;
			}
		}
		produce(0);
	}
};

struct MergeZ : Gen
{
	ZIn a_;
	ZIn b_;
    V fun_;
	Z aa, bb;
	bool flag = true;
	bool once = true;

	MergeZ(Thread& th, Arg a, Arg b, Arg fun) : Gen(th, itemTypeZ, leastFinite(a, b)), a_(a), b_(b), fun_(fun) {}

	virtual const char* TypeName() const override { return "MergeZ"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		for (int i = 0; i < framesToFill; ++i) {
			SaveStack ss(th);
			if (once) {
				once = false;
				if (a_.onez(th, aa)) {
					produce(framesToFill);
					b_.link(th, mOut);
					setDone();
					return;
				}
				if (b_.onez(th, bb)) {
					out[i] = aa;
					produce(framesToFill - i - 1);
					a_.link(th, mOut);
					setDone();
					return;
				}
			} else if (flag) {
				// last produced was a.
				if (a_.onez(th, aa)) {
					out[i] = bb;
					produce(framesToFill - i - 1);
					b_.link(th, mOut);
					setDone();
					return;
				}
			} else {
				// last produced was b.
				if (b_.onez(th, bb)) {
					out[i] = aa;
					produce(framesToFill - i - 1);
					a_.link(th, mOut);
					setDone();
					return;
				}
			}
			th.push(aa);
			th.push(bb);
			fun_.apply(th);
			flag = th.pop().isTrue();
			if (flag) {
				out[i] = aa;
				aa = 0.;
			} else {
				out[i] = bb;
				bb = 0.;
			}
		}
		produce(0);
	}
};

struct MergeByKey : Gen
{
	VIn a_;
	VIn b_;
    V key_;
	V aa, bb;
	bool flag = true;
	bool once = true;

	MergeByKey(Thread& th, Arg a, Arg b, Arg key) : Gen(th, itemTypeV, leastFinite(a, b)), a_(a), b_(b), key_(key) {}

	virtual const char* TypeName() const override { return "MergeByKey"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		for (int i = 0; i < framesToFill; ++i) {
			SaveStack ss(th);
			if (once) {
				once = false;
				if (a_.one(th, aa)) {
					produce(framesToFill);
					b_.link(th, mOut);
					setDone();
					return;
				}
				if (b_.one(th, bb)) {
					out[i] = aa;
					produce(framesToFill - i - 1);
					a_.link(th, mOut);
					setDone();
					return;
				}
			} else if (flag) {
				// last produced was a.
				if (a_.one(th, aa)) {
					out[i] = bb;
					produce(framesToFill - i - 1);
					b_.link(th, mOut);
					setDone();
					return;
				}
			} else {
				// last produced was b.
				if (b_.one(th, bb)) {
					out[i] = aa;
					produce(framesToFill - i - 1);
					a_.link(th, mOut);
					setDone();
					return;
				}
			}
			{
				V a, b;
				bool aok = aa.dot(th, key_, a);
				bool bok = bb.dot(th, key_, b);
				if (!aok || !bok) {
					setDone();
					return;
				}
				flag = ::Compare(th, a, b) < 0;
				if (flag) {
					out[i] = aa;
					aa = 0.;
				} else {
					out[i] = bb;
					bb = 0.;
				}
			}
		}
		produce(0);
	}
};

struct MergeCmp : Gen
{
	VIn a_;
	VIn b_;
    V fun_;
	V aa, bb;
	enum { left, right, both };
	int which = both;

	MergeCmp(Thread& th, Arg a, Arg b, Arg fun) : Gen(th, itemTypeV, leastFinite(a, b)), a_(a), b_(b), fun_(fun) {}

	virtual const char* TypeName() const override { return "MergeCmp"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		for (int i = 0; i < framesToFill; ++i) {
			SaveStack ss(th);
			if (which == both) {
				if (a_.one(th, aa)) {
					produce(framesToFill);
					b_.link(th, mOut);
					setDone();
					return;
				}
				if (b_.one(th, bb)) {
					out[i] = aa;
					produce(framesToFill - i - 1);
					a_.link(th, mOut);
					setDone();
					return;
				}
			} else if (which == left) {
				if (a_.one(th, aa)) {
					out[i] = bb;
					produce(framesToFill - i - 1);
					b_.link(th, mOut);
					setDone();
					return;
				}
			} else {
				if (b_.one(th, bb)) {
					out[i] = aa;
					produce(framesToFill - i - 1);
					a_.link(th, mOut);
					setDone();
					return;
				}
			}
			th.push(aa);
			th.push(bb);
			fun_.apply(th);
			Z compare = th.popFloat("mergec : compareValue");
			if (compare < 0.) {
				out[i] = aa;
				aa = 0.;
				which = left;
			} else if (compare == 0.) {
				out[i] = aa;
				aa = 0.;
				bb = 0.;
				which = both;
			} else {
				out[i] = bb;
				bb = 0.;
				which = right;
			}
		}
		produce(0);
	}
};

struct MergeCmpZ : Gen
{
	ZIn a_;
	ZIn b_;
    V fun_;
	Z aa, bb;
	enum { left, right, both };
	int which = both;

	MergeCmpZ(Thread& th, Arg a, Arg b, Arg fun) : Gen(th, itemTypeZ, leastFinite(a, b)), a_(a), b_(b), fun_(fun) {}

	virtual const char* TypeName() const override { return "MergeCmpZ"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		for (int i = 0; i < framesToFill; ++i) {
			SaveStack ss(th);
			if (which == both) {
				if (a_.onez(th, aa)) {
					produce(framesToFill);
					b_.link(th, mOut);
					setDone();
					return;
				}
				if (b_.onez(th, bb)) {
					out[i] = aa;
					produce(framesToFill - i - 1);
					a_.link(th, mOut);
					setDone();
					return;
				}
			} else if (which == left) {
				if (a_.onez(th, aa)) {
					out[i] = bb;
					produce(framesToFill - i - 1);
					b_.link(th, mOut);
					setDone();
					return;
				}
			} else {
				if (b_.onez(th, bb)) {
					out[i] = aa;
					produce(framesToFill - i - 1);
					a_.link(th, mOut);
					setDone();
					return;
				}
			}
			th.push(aa);
			th.push(bb);
			fun_.apply(th);
			Z compare = th.popFloat("mergec : compareValue");
			if (compare < 0.) {
				out[i] = aa;
				which = right;
			} else if (compare == 0.) {
				out[i] = aa;
				which = both;
			} else {
				out[i] = bb;
				which = right;
			}
		}
		produce(0);
	}
};

static void merge_(Thread& th, Prim* prim)
{
	V fun = th.pop();
	V b = th.popList("merge : b");
	V a = th.popList("merge : a");

	if (a.isZList() && b.isZList()) {
		th.push(new List(new MergeZ(th, a, b, fun)));
	} else if (a.isVList() && b.isVList()) {
		if (fun.isString()) {
			th.push(new List(new MergeByKey(th, a, b, fun)));
		} else {
			th.push(new List(new Merge(th, a, b, fun)));
		}
	} else {
		post("merge : lists not same type\n");
		throw errFailed;
	}
}

static void mergec_(Thread& th, Prim* prim)
{
	V fun = th.pop();
	V b = th.popList("mergec : b");
	V a = th.popList("mergec : a");

	if (a.isZList() && b.isZList()) {
		th.push(new List(new MergeCmpZ(th, a, b, fun)));
	} else if (a.isVList() && b.isVList()) {
		th.push(new List(new MergeCmp(th, a, b, fun)));
	} else {
		post("mergec : lists not same type\n");
		throw errFailed;
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//evmerge

extern P<String> s_dt;
extern P<String> s_out;
P<String> s_dur;

P<TableMap> dtTableMap;
P<TableMap> restTableMap;

P<Form> extendFormByOne(Thread& th, P<Form> const& parent, P<TableMap> const& tmap, Arg value);

static P<Form> makeRestEvent(Z dt)
{
	P<Table> table = new Table(restTableMap);
	table->put(0, V(0.));
	table->put(1, V(dt));
	table->put(2, V(dt));
	return new Form(table);
}

struct MergeEvents : Gen
{
	VIn a_;
	VIn b_;
	Z nextATime = 0.;
	Z nextBTime;

	MergeEvents(Thread& th, Arg a, Arg b, Z t) : Gen(th, itemTypeV, leastFinite(a, b)), a_(a), b_(b), nextBTime(t)
	{
	}

	virtual const char* TypeName() const override { return "MergeEvents"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		for (int i = 0; i < framesToFill; ++i) {
			SaveStack ss(th);
			{
				if (nextATime <= nextBTime) {
					V aa;
					if (a_.one(th, aa)) {
						if (nextATime < nextBTime) {
							out[i] = makeRestEvent(nextBTime - nextATime);
							produce(framesToFill - i - 1);
						}
						b_.link(th, mOut);
						setDone();
						return;
					}

					V a;
					bool aok = aa.dot(th, s_dt, a);
					if (!aok) {
						setDone();
						return;
					}
					Z dta = a.asFloat();
					Z dt = std::min(dta, nextBTime - nextATime);
					out[i] = extendFormByOne(th, asParent(th, aa), dtTableMap, dt);
					nextATime += dta;
					aa = 0.;
				} else {
					V bb;
					if (b_.one(th, bb)) {
						if (nextBTime < nextATime) {
							out[i] = makeRestEvent(nextATime - nextBTime);
							produce(framesToFill - i - 1);
						}
						a_.link(th, mOut);
						setDone();
						return;
					}
					V b;
					bool bok = bb.dot(th, s_dt, b);
					if (!bok) {
						setDone();
						return;
					}
					Z dtb = b.asFloat();
					Z dt = std::min(dtb, nextATime - nextBTime);
					out[i] = extendFormByOne(th, asParent(th, bb), dtTableMap, dt);
					nextBTime += dtb;
					bb = 0.;
				}
			}
		}
		produce(0);
	}
};

static void evmerge_(Thread& th, Prim* prim)
{
	Z t = th.popFloat("evmerge : t");
	V b = th.popVList("evmerge : b");
	V a = th.popVList("evmerge : a");
	th.push(new List(new MergeEvents(th, a, b, t)));
}

static void evrest_(Thread& th, Prim* prim)
{
	Z t = th.popFloat("evrest : t");
	th.push(makeRestEvent(t));
}

static void evdelay_(Thread& th, Prim* prim)
{
	Z t = th.popFloat("evdelay : t");
	V a = th.popVList("evdelay : a");
	th.push(cons(th, makeRestEvent(t), a));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

class CompareFun
{
public:
	CompareFun() {}
	virtual ~CompareFun() {}
	virtual bool operator()(Thread& th, Arg a, Arg b) = 0;
};

class VLess : public CompareFun
{
public:
	VLess() {}
	~VLess() {}
	virtual bool operator()(Thread& th, Arg a, Arg b) { return Compare(th, a, b) < 0; }
};

class VGreater : public CompareFun
{
public:
	VGreater() {}
	~VGreater() {}
	virtual bool operator()(Thread& th, Arg a, Arg b) { return Compare(th, a, b) > 0; }
};

class VCompareF : public CompareFun
{
	V fun;
public:
	VCompareF(V inFun) : fun(inFun) {}
	~VCompareF() {}
	virtual bool operator()(Thread& th, Arg a, Arg b) {
		SaveStack ss(th);
		th.push(a);
		th.push(b);
		fun.apply(th);
		return th.pop().isTrue();
	}
};

class ZCompareFun
{
public:
	ZCompareFun() {}
	virtual ~ZCompareFun() {}
	virtual bool operator()(Thread& th, Z a, Z b) = 0;
};

class ZLess : public ZCompareFun
{
public:
	ZLess() {}
	~ZLess() {}
	virtual bool operator()(Thread& th, Z a, Z b) { return a < b; }
};

class ZCompareF : public ZCompareFun
{
	V fun;
public:
	ZCompareF(V inFun) : fun(inFun) {}
	~ZCompareF() {}
	virtual bool operator()(Thread& th, Z a, Z b) {
		SaveStack ss(th);
		th.push(a);
		th.push(b);
		fun.apply(th);
		return th.pop().isTrue();
	}
};

class ZGreater : public ZCompareFun
{
public:
	ZGreater() {}
	~ZGreater() {}
	virtual bool operator()(Thread& th, Z a, Z b) { return a > b; }
};

static void merge(Thread& th, int64_t an, V* a, int64_t bn, V* b, V* c, CompareFun* compare)
{
	// merge a and b using scratch space c.
	// copy result back to a.
	// a and b are assumed to be contiguous.
	int64_t ai = 0;
	int64_t bi = 0;
	int64_t ci = 0;
	while (ai < an && bi < bn) {
		if ((*compare)(th, a[ai], b[bi])) {
			c[ci++] = a[ai++];
		} else {
			c[ci++] = b[bi++];
		}
	}
	while (ai < an) {
		c[ci++] = a[ai++];
	}
	while (bi < bn) {
		c[ci++] = b[bi++];
	}
	for (int64_t i = 0; i < ci; ++i) {
		a[i] = c[i];
	}
}

static void merge(Thread& th, int64_t an, Z* a, int64_t bn, Z* b, Z* c, ZCompareFun* compare)
{
	// merge a and b using scratch space c.
	// copy result back to a.
	// a and b are assumed to be contiguous.
	int64_t ai = 0;
	int64_t bi = 0;
	int64_t ci = 0;
	while (ai < an && bi < bn) {
		if ((*compare)(th, a[ai], b[bi])) {
			c[ci++] = a[ai++];
		} else {
			c[ci++] = b[bi++];
		}
	}
	while (ai < an) {
		c[ci++] = a[ai++];
	}
	while (bi < bn) {
		c[ci++] = b[bi++];
	}
	for (int64_t i = 0; i < ci; ++i) {
		a[i] = c[i];
	}
}

static void mergesort(Thread& th, int64_t n, V* a, V* tmp, CompareFun* compare)
{
	if (n == 1) return;
	int64_t an = n / 2;
	int64_t bn = n - an;
	V* b = a + an;
	mergesort(th, an, a, tmp, compare);
	mergesort(th, bn, b, tmp, compare);
	merge(th, an, a, bn, b, tmp, compare);
}

static void mergesort(Thread& th, int64_t n, Z* a, Z* tmp, ZCompareFun* compare)
{
	if (n == 1) return;
	int64_t an = n / 2;
	int64_t bn = n - an;
	Z* b = a + an;
	mergesort(th, an, a, tmp, compare);
	mergesort(th, bn, b, tmp, compare);
	merge(th, an, a, bn, b, tmp, compare);
}

static void sort(Thread& th, int64_t n, const V* in, V* out, CompareFun* compare)
{
	V* tmp = new V[n];
	ArrayDeleter<V> d(tmp);

	for (int64_t i = 0; i < n; ++i) out[i] = in[i];
	mergesort(th, n, out, tmp, compare);
}

static void sort(Thread& th, int64_t n, const Z* in, Z* out, ZCompareFun* compare)
{
	Z* tmp = new Z[n];
	ArrayDeleter<Z> d(tmp);

	for (int64_t i = 0; i < n; ++i) out[i] = in[i];
	mergesort(th, n, out, tmp, compare);
}

static void merge(Thread& th, int64_t an, V* a, Z* az, int64_t bn, V* b, Z* bz, V* c, Z* cz, CompareFun* compare)
{
	// merge a and b using scratch space c.
	// copy result back to a.
	// a and b are assumed to be contiguous.
	int64_t ai = 0;
	int64_t bi = 0;
	int64_t ci = 0;
	while (ai < an && bi < bn) {
		if ((*compare)(th, a[ai], b[bi])) {
			c[ci] = a[ai];
			cz[ci++] = az[ai++];
		} else {
			c[ci] = b[bi];
			cz[ci++] = bz[bi++];
		}
	}
	while (ai < an) {
		c[ci] = a[ai];
		cz[ci++] = az[ai++];
	}
	while (bi < bn) {
		c[ci] = b[bi];
		cz[ci++] = bz[bi++];
	}
	for (int64_t i = 0; i < ci; ++i) {
		a[i] = c[i];
		az[i] = cz[i];
	}
}

static void merge(Thread& th, int64_t an, Z* a, Z* az, int64_t bn, Z* b, Z* bz, Z* c, Z* cz, ZCompareFun* compare)
{
	// merge a and b using scratch space c.
	// copy result back to a.
	// a and b are assumed to be contiguous.
	int64_t ai = 0;
	int64_t bi = 0;
	int64_t ci = 0;
	while (ai < an && bi < bn) {
		if ((*compare)(th, a[ai], b[bi])) {
			c[ci] = a[ai];
			cz[ci++] = az[ai++];
		} else {
			c[ci] = b[bi];
			cz[ci++] = bz[bi++];
		}
	}
	while (ai < an) {
		c[ci] = a[ai];
		cz[ci++] = az[ai++];
	}
	while (bi < bn) {
		c[ci] = b[bi];
		cz[ci++] = bz[bi++];
	}
	for (int64_t i = 0; i < ci; ++i) {
		a[i] = c[i];
		az[i] = cz[i];
	}
}

static void mergesort(Thread& th, int64_t n, V* a, Z* az, V* c, Z* cz, CompareFun* compare)
{
	if (n == 1) return;
	int64_t an = n / 2;
	int64_t bn = n - an;
	V* b = a + an;
	Z* bz = az + an;
	mergesort(th, an, a, az, c, cz, compare);
	mergesort(th, bn, b, bz, c, cz, compare);
	merge(th, an, a, az, bn, b, bz, c, cz, compare);
}

static void mergesort(Thread& th, int64_t n, Z* a, Z* az, Z* c, Z* cz, ZCompareFun* compare)
{
	if (n == 1) return;
	int64_t an = n / 2;
	int64_t bn = n - an;
	Z* b = a + an;
	Z* bz = az + an;
	mergesort(th, an, a, az, c, cz, compare);
	mergesort(th, bn, b, bz, c, cz, compare);
	merge(th, an, a, az, bn, b, bz, c, cz, compare);
}

static void grade(Thread& th, int64_t n, const V* in, Z* zout, CompareFun* compare)
{
	V* out = new V[n];
	V* tmp = new V[n];
	Z* ztmp = new Z[n];
	ArrayDeleter<V> d1(out);
	ArrayDeleter<V> d2(tmp);
	ArrayDeleter<Z> d3(ztmp);

	for (int64_t i = 0; i < n; ++i) out[i] = in[i];
	double z = 0.;
	for (int64_t i = 0; i < n; ++i, z+=1.) zout[i] = z;
	mergesort(th, n, out, zout, tmp, ztmp, compare);
}

static void grade(Thread& th, int64_t n, const Z* in, Z* zout, ZCompareFun* compare)
{
	Z* out = new Z[n];
	Z* tmp = new Z[n];
	Z* ztmp = new Z[n];
	ArrayDeleter<Z> d1(out);
	ArrayDeleter<Z> d2(tmp);
	ArrayDeleter<Z> d3(ztmp);

	for (int64_t i = 0; i < n; ++i) out[i] = in[i];
	double z = 0.;
	for (int64_t i = 0; i < n; ++i, z+=1.) zout[i] = z;
	mergesort(th, n, out, zout, tmp, ztmp, compare);
}

static void sort_(Thread& th, Prim* prim)
{
	V a = th.popList("sort : a");

	if (!a.isFinite())
		indefiniteOp("sort : a", "");

	P<List> list = ((List*)a.o())->pack(th);

	P<Array> array = list->mArray;
	int64_t n = array->size();

	if (list->isVList()) {
		V* v = array->v();
		VLess cmp;

		P<List> out = new List(itemTypeV, n);
		out->mArray->setSize(n);
		V* vout = out->mArray->v();

		sort(th, n, v, vout, &cmp);
		th.push(out);
	} else {
		Z* z = array->z();
		ZLess cmp;
		P<List> out = new List(itemTypeZ, n);
		out->mArray->setSize(n);
		Z* zout = out->mArray->z();

		sort(th, n, z, zout, &cmp);
		th.push(out);
	}
}

static void sortf_(Thread& th, Prim* prim)
{
	V fun = th.popList("sort : fun");
	V a = th.popList("sort : a");

	if (!a.isFinite())
		indefiniteOp("sort : a", "");

	P<List> list = ((List*)a.o())->pack(th);

	P<Array> array = list->mArray;
	int64_t n = array->size();

	if (list->isVList()) {
		V* v = array->v();
		VCompareF cmp(fun);

		P<List> out = new List(list->ItemType(), n);
		out->mArray->setSize(n);
		V* vout = out->mArray->v();

		sort(th, n, v, vout, &cmp);
		th.push(out);
	} else {
		Z* z = array->z();
		ZCompareF cmp(fun);
		P<List> out = new List(itemTypeZ, n);
		out->mArray->setSize(n);
		Z* zout = out->mArray->z();

		sort(th, n, z, zout, &cmp);
		th.push(out);
	}
}

static void sort_gt_(Thread& th, Prim* prim)
{
	V a = th.popList("sort> : a");

	if (!a.isFinite())
		indefiniteOp("sort> : a", "");

	P<List> list = ((List*)a.o())->pack(th);

	P<Array> array = list->mArray;
	int64_t n = array->size();

	if (list->isVList()) {
		V* v = array->v();
		VGreater cmp;

		P<List> out = new List(itemTypeV, n);
		out->mArray->setSize(n);
		V* vout = out->mArray->v();

		sort(th, n, v, vout, &cmp);
		th.push(out);
	} else {
		Z* z = array->z();
		ZGreater cmp;
		P<List> out = new List(itemTypeZ, n);
		out->mArray->setSize(n);
		Z* zout = out->mArray->z();

		sort(th, n, z, zout, &cmp);
		th.push(out);
	}
}

static void grade_(Thread& th, Prim* prim)
{
	V a = th.popList("grade : a");

	if (!a.isFinite())
		indefiniteOp("grade : a", "");

	P<List> list = ((List*)a.o())->pack(th);

	P<Array> array = list->mArray;
	int64_t n = array->size();

	if (list->isVList()) {
		V* v = array->v();
		VLess cmp;

		P<List> out = new List(itemTypeZ, n);
		out->mArray->setSize(n);
		Z* zout = out->mArray->z();

		grade(th, n, v, zout, &cmp);
		th.push(out);
	} else {
		Z* z = array->z();
		ZLess cmp;
		P<List> out = new List(itemTypeZ, n);
		out->mArray->setSize(n);
		Z* zout = out->mArray->z();

		grade(th, n, z, zout, &cmp);
		th.push(out);
	}
}

static void gradef_(Thread& th, Prim* prim)
{
	V fun = th.popList("grade : fun");
	V a = th.popList("grade : a");

	if (!a.isFinite())
		indefiniteOp("grade : a", "");

	P<List> list = ((List*)a.o())->pack(th);

	P<Array> array = list->mArray;
	int64_t n = array->size();

	if (list->isVList()) {
		V* v = array->v();
		VCompareF cmp(fun);

		P<List> out = new List(itemTypeZ, n);
		out->mArray->setSize(n);
		Z* zout = out->mArray->z();

		grade(th, n, v, zout, &cmp);
		th.push(out);
	} else {
		Z* z = array->z();
		ZCompareF cmp(fun);
		P<List> out = new List(itemTypeZ, n);
		out->mArray->setSize(n);
		Z* zout = out->mArray->z();

		grade(th, n, z, zout, &cmp);
		th.push(out);
	}
}

static void grade_gt_(Thread& th, Prim* prim)
{
	V a = th.popList("grade> : a");

	if (!a.isFinite())
		indefiniteOp("grade> : a", "");

	P<List> list = ((List*)a.o())->pack(th);

	P<Array> array = list->mArray;
	int64_t n = array->size();

	if (list->isVList()) {
		V* v = array->v();
		VGreater cmp;

		P<List> out = new List(itemTypeZ, n);
		out->mArray->setSize(n);
		Z* zout = out->mArray->z();

		grade(th, n, v, zout, &cmp);
		th.push(out);
	} else {
		Z* z = array->z();
		ZGreater cmp;
		P<List> out = new List(itemTypeZ, n);
		out->mArray->setSize(n);
		Z* zout = out->mArray->z();

		grade(th, n, z, zout, &cmp);
		th.push(out);
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma mark ADD STREAM OPS

#define DEF(NAME, TAKES, LEAVES, HELP) 	vm.def(#NAME, TAKES, LEAVES, NAME##_, HELP);
#define DEFMCX(NAME, N, HELP) 	vm.defmcx(#NAME, N, NAME##_, HELP);
#define DEFAM(NAME, MASK, HELP) 	vm.defautomap(#NAME, #MASK, NAME##_, HELP);
#define DEFnoeach(NAME, TAKES, LEAVES, HELP) 	vm.def(#NAME, TAKES, LEAVES, NAME##_, HELP, V(0.), true);
#define DEFN(NAME, TAKES, LEAVES, FUN, HELP) 	vm.def(NAME, TAKES, LEAVES, FUN, HELP);
#define DEFNnoeach(NAME, TAKES, LEAVES, FUN, HELP) 	vm.def(NAME, TAKES, LEAVES, FUN, HELP, V(0.), true);

void AddStreamOps();
void AddStreamOps()
{
	initFFT();
	s_dt  = getsym("dt");
	s_out = getsym("out");
	s_dur = getsym("dur");
	dtTableMap = new TableMap(V(s_dt));
	restTableMap = new TableMap(3);
	restTableMap->put(0, s_out, s_out->Hash());
	restTableMap->put(1, s_dur, s_dur->Hash());
	restTableMap->put(2, s_dt,  s_dt->Hash());

	vm.addBifHelp("\n*** list conversion ***");
	DEF(V, 1, 1, "(signal --> stream) converts a signal or string to a stream.")
	DEF(Z, 1, 1, "(series --> signal) converts a stream or string to a signal.")
	DEF(L, 1, 1, "(anything --> stream) streams are returned as is. anything else is made into an infinite stream of itself.")
	DEF(L1, 1, 1, "(anything --> stream) streams are returned as is. anything else is wrapped in a one item list.")
	DEF(unspell, 1, 1, "(sequence --> string) converts a stream of numbers or a signal to a string.")

	vm.addBifHelp("\n*** basic list operations ***");

	DEF(size, 1, 1, "(seq --> num) Return the length of a sequence if it is finite. Returns inf if the sequence is of indefinite length (It may not actually be infinitely long).")
	DEF(rank, 1, 1, "(a --> n) Return the rank of an object. Makes the assumption that lists at all depths are homogenous.")
	DEF(shape, 1, 1, "(a --> [n..]) Return the shape of an object. Axes of indefinite length are represented by inf. Makes the assumption that lists at all depths are homogenous.")
	DEF(finite, 1, 1, "(seq --> bool) Returns 1 if the sequence is finite, 0 if indefinite.")

	DEF(empty, 1, 1, "(list --> bool) returns whether the list is empty.")
	DEF(nonempty, 1, 1, "(list --> bool) returns whether the list is nonempty.")
	DEF(head, 1, 1, "(list --> item) returns first item of list. fails if list is empty.")
	DEF(tail, 1, 1, "(list --> list) returns the rest of the list after the first item. fails if list is empty.")
	DEF(add, 2, 1, "(list item --> list) returns a new list with the item added to the end.")
	DEF(cons, 2, 1, "(list item --> list) returns a new list with the item added to the front.")
	DEF(uncons, 1, 2, "(list --> tail head) returns the tail and head of a list. fails if list is empty.")
	DEF(pack, 1, 1, "(list --> list) returns a packed version of the list.");
	DEF(packed, 1, 1, "(list --> bool) returns whether the list is packed.");

	vm.addBifHelp("\n*** list generation ***");

	DEFnoeach(ord,    0, 1, "(--> series) return an infinite series of integers ascending from 1.")
	DEFnoeach(nat,    0, 1, "(--> series) return an infinite series of integers ascending from 0.")
	DEFnoeach(invs,   0, 1, "(--> series) return an infinite series of reciprocals. equivalent to ord 1/")
	DEFnoeach(negs,   0, 1, "(--> series) return an infinite series of integers descending from -1.")
	DEFnoeach(evens,  0, 1, "(--> series) return an infinite series of ascending non-negative even integers.")
	DEFnoeach(odds,   0, 1, "(--> series) return an infinite series of ascending non-negative odd integers.")
	DEFnoeach(ints,   0, 1, "(--> series) return the infinite series [0 1 -1 2 -2 3 -3...]")
	DEFnoeach(primes, 0, 1, "(--> series) returns a finite series of prime numbers up to 1000039.")
	DEFAM(fib, kk, "(a b --> series) returns a fibonacci series starting with the two numbers given.")

	DEFnoeach(ordz,   0, 1, "(--> signal) return an infinite signal of integers ascending from 1.")
	DEFnoeach(natz,   0, 1, "(--> signal) return an infinite signal of integers ascending from 0.")
	DEFnoeach(invz,   0, 1, "(--> signal) return an infinite signal of reciprocals. equivalent to ordz 1/")
	DEFnoeach(negz,   0, 1, "(--> signal) return an infinite signal of integers descending from -1.")
	DEFnoeach(evenz,  0, 1, "(--> signal) return an infinite signal of ascending non-negative even integers.")
	DEFnoeach(oddz,   0, 1, "(--> signal) return an infinite signal of ascending non-negative odd integers.")
	DEFnoeach(intz,   0, 1, "(--> signal) return the infinite signal [0 1 -1 2 -2 3 -3...]")
	DEFnoeach(primez, 0, 1, "(--> signal) returns a finite signal of prime numbers up to 1000039.")
	DEFMCX(fibz, 2, "(a b --> signal) returns a fibonacci signal starting with the two numbers given.")

	DEFAM(ninvs, k, "(n --> stream) return a finite stream of n reciprocals. equivalent to n 1 1 nby 1/")
	DEFMCX(ninvz, 1, "(n --> signal) return a finite signal of n reciprocals. equivalent to n 1 1 nbyz 1/")

	DEF(ever, 1, 1, "(value --> series) return an infinite stream of value.")
	DEFAM(by, kk, "(start step --> series) return an infinite arithmetic series.")
	DEFAM(nby, kkk, "(n start step --> series) return a finite arithmetic series.")
	DEFAM(grow, kk, "(start step --> series) return an infinite geometric series.")
	DEFAM(ngrow, kkk, "(start step --> series) return a finite geometric series.")
	DEFAM(to, kk, "(a b --> series) return a finite series from a to b stepping by +1 if a < b, or -1 if a < b.")

	DEFMCX(everz, 1, "(value --> signal) return an infinite signal of value.")
	DEFMCX(byz, 2, "(start step --> series) return an infinite arithmetic series as a signal.")
	DEFMCX(nbyz, 3, "(start step --> series) return a finite arithmetic series as a signal.")
	DEFMCX(growz, 2, "(start step --> series) return an infinite geometric series as a signal.")
	DEFMCX(ngrowz, 3, "(start step --> series) return a finite geometric series as a signal.")
	DEFMCX(toz, 2, "(a b --> series) return a finite signal from a to b stepping by +1 if a < b, or -1 if a < b.")

	DEFAM(lindiv, kkk, "(n start end --> series) returns a series of n equal steps from start to end.")
	DEFAM(expdiv, kkk, "(n start end --> series) returns a series of n exponentially spaced steps from start to end.")
	DEFMCX(lindivz, 3, "(n start end --> series) returns a signal of n equal steps from start to end.")
	DEFMCX(expdivz, 3, "(n start end --> series)  returns a signal of n exponentially spaced steps from start to end.")

	DEFAM(lindiv1, kkk, "(n start end --> series) returns a series of n equal steps from start up to but not including end.")
	DEFAM(expdiv1, kkk, "(n start end --> series) returns a series of n exponentially spaced steps from start up to but not including end.")
	DEFMCX(lindiv1z, 3, "(n start end --> series) returns a signal of n equal steps from start up to but not including end.")
	DEFMCX(expdiv1z, 3, "(n start end --> series)  returns a signal of n exponentially spaced steps from start up to but not including end.")

	DEFMCX(line, 3, "(dur start end --> z) return a signal ramping linearly from start to end in dur seconds.") // mcx
	DEFMCX(xline, 3, "(dur start end --> z) return a signal ramping exponentially from start to end in dur seconds.") // mcx

	vm.addBifHelp("\n*** list reduction operations ***");
	DEFAM(reduce, aak, "(list value fun --> value) applies fun to each item in list and the current value to get a new value. returns the ending value.")
	DEFAM(reduce1, ak, "(list fun --> value) like reduce except that the initial value is the first item in the list.")

	DEFAM(scan, aak, "(list value fun --> list) applies fun to each item in list and the current value to get a new value, which is added to the output list.")
	DEFAM(scan1, ak, "(list fun --> list) like scan except that the initial value is the first item in the list.")
	DEFAM(iter, ak, "(value fun --> list) returns an infinite list of repeated applications of fun to value.")
	DEFAM(itern, akk, "(value fun n --> list) returns a list of n repeated applications of fun to value.")

	DEFAM(chain, akk, "(value fun n --> list) returns the result of n repeated applications of fun to value.")

	vm.addBifHelp("\n*** list ordering operations ***");
	DEF(cyc, 1, 1, "(list --> list) makes a finite list become cyclic.")
	DEFAM(ncyc, ak, "(n list --> list) concatenates n copies of a finite list.")
	DEF(rcyc, 1, 1, "(ref --> list) gets a new list from ref each time list is exhausted.")

	vm.defautomap("X", "ak", repeat_, "(value n --> stream) makes a list containing n copies of value. If value is a function, then the results of applying the function with an integer count argument is used as the contents of the output list.");
	vm.defmcx("XZ", 2, repeatz_, "(value n --> signal) returns a signal with value repeated n times.");
	vm.defmcx("mum", 1, mum_, "(t --> signal) returns a signal of t seconds of silence.");

	vm.def("$", 2, 1, append_, "(listA listB --> out) returns the concatenation of listA and listB.");
	vm.defmcx("$z", 2, append_, "(signalA signalB --> signal) returns the concatenation of signalA and signalB.");

	vm.def("$$", 2, 1, appendSubs_, "(listA listB --> out) return the concatenation of the sublists of listA and listB. equivalent to (listA @ listB @ $)");
	vm.def("$/", 1, 1, cat_, "(list --> out) returns the concatenation of the sub-lists of the input list.");
	DEF(flat, 1, 1, "(list --> list) flattens a list.")
	DEFAM(flatten, ak, "(list n --> list) makes a list n levels flatter.")
	vm.defautomap("keep", "ak", N_, "(list n --> list) returns a list of the first n items of the input list.");

	DEFAM(T, zk, "(signal t --> signal) returns a signal of the first t seconds of the input signal.");
	vm.defautomap("T>", "zk", skipT_, "(signal t --> signal) skips the first t seconds of the input signal.");
	vm.defautomap("N>", "ak", skip_, "(list n --> list) skips the first n items of the input list.");
	vm.def("N>>", 2, 1, hops_, "(list hops --> listOfLists) returns a list of tails of the input list. equivalent to (list (hops 0 | L 0 cons +\\) N>).");
	vm.defautomap("T>>", "za", hopTs_, "(signal hops --> listOfSignals) returns a list of tails of the input list. equivalent to (signal (hops 0 | L 0 cons +\\) T>).");
	DEFAM(N, ak, "(list n --> list) returns a list of the first n items of the input list.")
	DEFAM(NZ, zk, "(signal n --> signal) returns a signal of the first n items of the input signal. automaps over streams.")

	DEFAM(skip, ak, "(list n --> list) skips the first n items of the input list.")

	DEFAM(take, ak, "(list n --> list) returns a list of the first n items of the input list, or the last n items if n is negative and the list is finite.")
	DEFAM(drop, ak, "(list n --> list) skips the first n items of the input list, or the last n items if n is negative and the list is finite.")

	DEFAM(choff, akk, "(channel(s) c n --> out) takes a finite list of channels or a single signal and places it into an array of n channels beginning at offset c. Other channels are set to zero.");

	DEF(tog, 2, 1, "(a b --> series) return a series alternating between a and b.")
	DEFMCX(togz, 2, "(a b --> signal) return a signal alternating between a and b.")
	DEF(sel, 2, 1, "(a j --> out) select. a is a list of lists. out[i] is a[j][i]")
	DEF(sell, 2, 1, "(a j --> out) lazy select. a is a list of lists. out[i] is the next value from a[j].")

	vm.def("?", 2, 1, filter_, "(a b --> out) the output list contains a[i] repeated b[i] times. If b is a list of booleans (1 or 0) then this functions as a filter.");
	DEF(spread, 2, 1, "(a n --> out) inserts n[i] zeroes after a[i].")
	DEFMCX(spreadz, 2, "(a n --> signal) inserts n[i] zeroes after a[i]. automaps over stream inputs.")

	DEF(change, 1, 1, "(a --> b) eliminates sequential duplicates in a signal or stream.")
	DEFMCX(changez, 1, "(a --> b) eliminates sequential duplicates in a signal. automaps over streams.")
	DEF(expand, 2, 1, "(a b --> out) when b is true, a value from a is written to out, when b is false, zero is written to out.")
	DEFMCX(expandz, 2, "(a b --> out) when b is true, a value from a is written to out, when b is false, zero is written to out. automaps over stream inputs.")

	DEF(clump, 2, 1, "(a n --> out) groups elements from list a into sub-lists of size n.")
	DEF(hang, 1, 1, "(a --> out) repeats the last value of a finite list indefinitely.")
	DEFMCX(hangz, 1, "(a --> out) repeats the last value of a finite signal indefinitely. automaps over streams.")
	DEFAM(histo, ak, "(a n --> out) makes a histogram of the finite stream a.")
	vm.defautomap("histoz", "zk", histo_, "(a n --> out) makes a histogram of the finite signal a. automaps over streams.");

	DEF(keepWhile, 2, 1, "(a b --> out) return items from a while items from b are true.")
	DEF(skipWhile, 2, 1, "(a b --> out) skip items from a while items from b are true.")

	DEF(flop, 1, 1, "(a --> b) returns the transpose of the list of lists a. At least one of the dimensions must be finite.")
	DEF(flops, 1, 1, "(a --> b) like flop, but signals are treated as scalars and not flopped.")
	DEF(flop1, 1, 1, "(a --> b) like flop, but if list a is not a list of lists then it is wrapped in a list. compare: [[1 2 3][[4 5] 6 7]] @ flop $/ with: [[1 2 3][[4 5] 6 7]] @ flop1 $/")
	DEF(lace, 1, 1, "(a --> b) returns the concatenation of the transpose of the list of lists a.")
	DEFAM(merge, aak, "(a b fun --> c) merges two lists according to the function given. The function should work like <.")
	DEFAM(mergec, aak, "(a b fun --> c) merges two lists without duplicates according to the function given. The function should work like cmp.")

	DEF(perms, 1, 1, "(a --> b) returns a list of all permutations of the input list.")
	DEFMCX(permz, 1, "(a --> b) returns a list of all permutations of the input signal. automaps over streams.")

	DEF(permswr, 1, 1, "(a --> b) returns a list of all unique permutations of an input stream with repeated elements.")
	DEFMCX(permzwr, 1, "(a --> b) returns a returns a list of all unique permutations of an input signal with repeated elements. automaps over streams.")

	DEF(shortas, 2, 1, "(a b --> a') makes list a as short as list b.")
	DEF(longas, 2, 1, "(a b --> a') makes list a as long as list b by repeating the last item.")
	DEF(longas0, 2, 1, "(a b --> a') makes list a as long as list b by appending zeroes.")

	// array ops

	vm.addBifHelp("\n*** list ops ***");

	DEF(bub, 1, 1, "(a --> [a]) makes the top item on the stack into a one item list. i.e. puts a bubble around it.")
	DEF(nbub, 2, 1, "(a n --> [[..[a]..]]) embeds the top item in N one item lists.")

	vm.def("2ple", 2, 1, tupleN_<2>, "(a b --> [a b]) make a pair from the top two stack items.");
	vm.def("3ple", 3, 1, tupleN_<3>, "(a b c --> [a b c]) make a triple from the top three stack items.");
	vm.def("4ple", 4, 1, tupleN_<4>, "(a b c d --> [a b c d]) make a quadriple from the top four stack items.");
	vm.def("5ple", 5, 1, tupleN_<5>, "(a b c d e --> [a b c d e]) make a quintuple from the top five stack items.");
	vm.def("6ple", 6, 1, tupleN_<6>, "(a b c d e f --> [a b c d e f]) make a sextuple from the top six stack items.");
	vm.def("7ple", 7, 1, tupleN_<7>, "(a b c d e f g --> [a b c d e f g]) make a septuple from the top seven stack items.");
	vm.def("8ple", 8, 1, tupleN_<8>, "(a b c d e f g h --> [a b c d e f g h]) make an octuple from the top eight stack items.");

	vm.defautomap("2ples", "kk", tupleN_<2>, "(a b --> [[a0 b0][a1 b1]..[aN bN]]) make a sequence of pairs from the sequences a and b.");
	vm.defautomap("3ples", "kkk", tupleN_<3>, "(a b c --> [[a0 b0 c0][a1 b1 c1]..[aN bN cN]]) make a sequence of triples from the sequences a, b and c.");
	vm.defautomap("4ples", "kkkk", tupleN_<4>, "(a b c d --> seq) make a sequence of quadruples from the sequences a, b, c and d.");
	vm.defautomap("5ples", "kkkkk", tupleN_<5>, "(a b c d e --> seq) make a sequence of quintuples from the sequences a through e.");
	vm.defautomap("6ples", "kkkkkk", tupleN_<6>, "(a b c d e f--> seq) make a sequence of sextuples from the sequences a through f.");
	vm.defautomap("7ples", "kkkkkkk", tupleN_<7>, "(a b c d e f g--> seq) make a sequence of septuples from the sequences a through g.");
	vm.defautomap("8ples", "kkkkkkkk", tupleN_<8>, "(a b c d e f g h --> seq) make a sequence of octuples from the sequences a through h.");

	DEFNnoeach("un2", 1, 2, untupleN_<2>, "([a0 a1 .. aN-1] --> a0 a1) Push two items from a sequence onto the stack.")
	DEFNnoeach("un3", 1, 3, untupleN_<3>, "([a0 a1 .. aN-1] --> a0 a1 a2) Push three items from a sequence onto the stack.")
	DEFNnoeach("un4", 1, 4, untupleN_<4>, "([a0 a1 .. aN-1] --> a0 a1 a2 a3) Push four items from a sequence onto the stack.")
	DEFNnoeach("un5", 1, 5, untupleN_<5>, "([a0 a1 .. aN-1] --> a0 a1 a2 a3 a4) Push five items from a sequence onto the stack.")
	DEFNnoeach("un6", 1, 6, untupleN_<6>, "([a0 a1 .. aN-1] --> a0 a1 a2 .. a5) Push six items from a sequence onto the stack.")
	DEFNnoeach("un7", 1, 7, untupleN_<7>, "([a0 a1 .. aN-1] --> a0 a1 a2 .. a6) Push seven items from a sequence onto the stack.")
	DEFNnoeach("un8", 1, 8, untupleN_<8>, "([a0 a1 .. aN-1] --> a0 a1 a2 .. a7) Push eight items from a sequence onto the stack.")

	DEF(reverse, 1, 1, "(a --> b) reverses a finite sequence.")
	DEF(mirror0, 1, 1, "(a --> b) cyclic mirror of a sequence. [1 2 3 4] --> [1 2 3 4 3 2]")
	DEF(mirror1, 1, 1, "(a --> b) odd mirror of a sequence. [1 2 3 4] --> [1 2 3 4 3 2 1]")
	DEF(mirror2, 1, 1, "(a --> b) even mirror of a sequence. [1 2 3 4] --> [1 2 3 4 4 3 2 1]")
	DEFAM(rot, ak, "(seq M --> seq') rotation of a sequence by M places. M > 0 moves right.")
	DEFAM(shift, ak, "(seq M --> seq') shift of a sequence by M places. zeroes are shifted in to fill vacated positions.")
	DEFAM(clipShift, ak, "(seq M --> seq') shift of a sequence by M places. the end value is copied in to fill vacated positions.")
	DEFAM(foldShift, ak, "(seq M --> seq') shift of a sequence by M places. values from the cyclic mirrored sequence are copied in to fill vacated positions.")
	DEF(muss, 1, 1, "(a --> b) puts a finite sequence into a random order.")

	DEF(at, 2, 1, "(seq index(es) --> value(s)) looks up item(s) in sequence at index(es). out of range indexes return zero.")
	DEF(wrapAt, 2, 1, "(seq index(es) --> value(s)) looks up item(s) in sequence at index(es). out of range indexes return the value at the end point.")
	DEF(foldAt, 2, 1, "(seq index(es) --> value(s)) looks up item(s) in sequence at index(es). out of range indexes return the items from the cyclic sequence.")
	DEF(clipAt, 2, 1, "(seq index(es) --> value(s)) looks up item(s) in sequence at index(es). out of range indexes return items from the cyclic mirrored sequence.")
	DEF(degkey, 2, 1, "(degree scale --> converts scale degree(s) to keys, given a scale");
	DEF(keydeg, 2, 1, "(key scale --> converts key(s) to scale degree(s), given a scale");

	DEF(sort, 1, 1, "(in --> out) ascending order sort of the input list.");
	DEFAM(sortf, ak, "(in fun --> out) sort of the input list using a compare function.");
	DEFN("sort>", 1, 1, sort_gt_, "(in --> out) descending order sort of the input list.");

	DEF(grade, 1, 1, "(in --> out) ascending order sorted indices of the input list.");
	DEFAM(gradef, ak, "(in fun --> out) sorted indices of the input list using a compare function.");
	DEFN("grade>", 1, 1, grade_gt_, "(in --> out) descending order sorted indices of the input list.");

	vm.addBifHelp("\n*** event list operations ***");
	DEFAM(evmerge, aak, "(a b t --> c) merges event list 'b' with delay 't' with event list 'a' according to their delta times")
	DEFAM(evdelay, ak, "(a t --> c) delay an event list by adding a preceeding rest of duration 't'")
	DEFAM(evrest, aak, "(t --> c) returns a rest event for duration 't'.")

	vm.addBifHelp("\n*** dsp operations ***");

	DEFMCX(kaiser, 2, "(n stopBandAttenuation --> out) returns a signal filled with a kaiser window with the given stop band attenuation.")
	DEFMCX(hanning, 1, "(n --> out) returns a signal filled with a Hanning window.")
	DEFMCX(hamming, 1, "(n --> out) returns a signal filled with a Hamming window.")
	DEFMCX(blackman, 1, "(n --> out) returns a signal filled with a Blackman window.")
	DEFMCX(fft, 2, "(re im --> out) returns the complex FFT of two vectors (one real and one imaginary) which are a power of two length.")
	DEFMCX(ifft, 2, "(re im --> out) returns the complex IFFT of two vectors (one real and one imaginary) which are a power of two length.")

	DEFAM(seg, zaa, "(in hops durs --> out) divide input signal in to a stream of signal segments of given duration stepping by hop time.")
	DEFAM(wseg, zaz, "(in hops window --> out) divide input signal in to a stream of windowed signal segments of lengths equal to the window length, stepping by hop time.")

	vm.addBifHelp("\n*** audio I/O operations ***");
	DEF(play, 1, 0, "(channels -->) plays the audio to the hardware.")
	DEF(record, 2, 0, "(channels filename -->) plays the audio to the hardware and records it to a file.")
	DEFnoeach(stop, 0, 0, "(-->) stops any audio playing.")
	vm.def("sf>", 1, 0, sfread_, "(filename -->) read channels from an audio file. not real time.");
	vm.def(">sf", 2, 0, sfwrite_, "(channels filename -->) writes the audio to a file.");
	vm.def(">sfo", 2, 0, sfwriteopen_, "(channels filename -->) writes the audio to a file and opens it in the default application.");
	//vm.def("sf>", 2, sfread_);
	DEF(bench, 1, 0, "(channels -->) prints the amount of CPU required to compute a segment of audio. audio must be of finite duration.")
	vm.def("sgram", 3, 0, sgram_, "(signal dBfloor filename -->) writes a spectrogram to a file and opens it.");

	setSessionTime();

}

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "OscilUGens.hpp"
#include "UGen.hpp"

#include "VM.hpp"
#include "clz.hpp"
#include "dsp.hpp"
#include <cmath>
#include <float.h>
#include <vector>
#include <algorithm>
#include <Accelerate/Accelerate.h>

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

const int kNumTables = 30; // third octave tables.
const int kWaveTableSize = 16384;
const int kWaveTableMask = kWaveTableSize - 1;
//const int kWaveTableByteSize = kWaveTableSize * sizeof(Z);
const int kWaveTableTotalSize = kWaveTableSize * kNumTables;
//const Z kPhaseInc = kTwoPi / kWaveTableSize;
const Z kWaveTableSizeF = kWaveTableSize;

// the maximum number of harmonics is actually 1024, but 1290 is needed for extrapolation.
const int kMaxHarmonics = 1290;
const int gNumHarmonicsForTable[kNumTables+1] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 16, 20, 25, 32, 40, 50, 64, 80, 101, 128, 161, 203, 256, 322, 406, 512, 645, 812, 1024, 1290 };

int gWaveTableSize[kNumTables];

const double kMaxHarmonicsF = kMaxHarmonics;

Z gTableForNumHarmonics[kMaxHarmonics+1];
Z gHertzToHarmonics[kMaxHarmonics+1];

static void fillHarmonicsTable()
{
	double maxval = kNumTables - 1.0000001;

	int t = 0;
	for (int i = 1; i < kMaxHarmonics; ++i) {
		if (gNumHarmonicsForTable[t] < i && t < kNumTables) ++t;
		//int t1 = std::clamp(t-1, 0, kNumTables-1);

		double frac = (double)(i - gNumHarmonicsForTable[t]) / (double)(gNumHarmonicsForTable[t] - gNumHarmonicsForTable[t-1]);
		double ft = t - 1 + frac;
		gTableForNumHarmonics[i] = ft;
	}

	gTableForNumHarmonics[0] = 0.;
	gTableForNumHarmonics[kMaxHarmonics] = maxval;
}

static void zeroTable(size_t n, Z* table)
{
	memset(table, 0, n * sizeof(Z));
}

static void normalize(int n, Z* buf)
{
	Z maxabs = 0.;
	for (int i = 0; i < n; ++i) {
		maxabs = std::max(maxabs, fabs(buf[i]));
	}
	if (maxabs > 0.) {
		Z scale = 1. / maxabs;
		for (int i = 0; i < n; ++i) {
			buf[i] *= scale;
		}
	}
}

static void fillWaveTable(int n, Z* amps, int ampStride, Z* phases, int phaseStride, Z smooth, Z* table)
{
	const size_t kWaveTableSize2 = kWaveTableSize / 2;
	const Z two_pi = 2. * M_PI;

	Z real[kWaveTableSize2];
	Z imag[kWaveTableSize2];
	Z polar[kWaveTableSize];
	Z rect[kWaveTableSize];

	zeroTable(kWaveTableSize2, real);
	zeroTable(kWaveTableSize2, imag);

	Z w = M_PI_2 / n;
	for (int i = 0; i < n; ++i) {
		Z smoothAmp = smooth == 0. ? 1. : pow(cos(w*i), smooth);
		//fillHarmonic(i+1, *amps * smoothAmp, *phases, table);
		real[i+1] = *amps * smoothAmp;
		imag[i+1] = (*phases - .25) * two_pi;
		amps += ampStride;
		phases += phaseStride;
	}

	DSPDoubleSplitComplex in;
	in.realp = real;
	in.imagp = imag;

	vDSP_ztocD(&in, 1, (DSPDoubleComplex*)polar, 2, kWaveTableSize2);
	vDSP_rectD(polar, 2, rect, 2, kWaveTableSize2);
	vDSP_ctozD((DSPDoubleComplex*)rect, 2, &in, 1, kWaveTableSize2);
	rifft(kWaveTableSize, real, imag, table);
}

static void fill3rdOctaveTables(int n, Z* amps, int ampStride, Z* phases, int phaseStride, Z smooth, Z* tables)
{
	// tables is assumed to be allocated to kNumTables * kWaveTableSize samples
	for (int i = 0; i < kNumTables; ++i) {
		int numHarmonics = std::min(n, gNumHarmonicsForTable[i]);
		fillWaveTable(numHarmonics, amps, ampStride, phases, phaseStride, smooth, tables + i * kWaveTableSize);
	}

	normalize(kWaveTableTotalSize, tables);
}

static P<List> makeWavetable(int n, Z* amps, int ampStride, Z* phases, int phaseStride, Z smooth)
{
	P<List> list = new List(itemTypeZ, kWaveTableTotalSize);
	P<Array> array = list->mArray;

	Z* tables = array->z();

	fill3rdOctaveTables(n, amps, ampStride, phases, phaseStride, smooth, tables);
	array->setSize(kWaveTableTotalSize);

	return list;
}

static void wavefill_(Thread& th, Prim* prim)
{
	Z smooth = th.popFloat("wavefill : smooth");
	V phases = th.popZIn("wavefill : phases");
	V amps = th.popZIn("wavefill : amps");

	P<List> ampl;
	P<List> phasel;
	Z *phasez, *ampz;
	int phaseStride, ampStride;
	int64_t n = kMaxHarmonics;
	if (phases.isZList()) {
		phasel = ((List*)phases.o())->packSome(th, n);
		phasez = phasel->mArray->z();
		phaseStride = 1;
	} else {
		phasez = &phases.f;
		phaseStride = 0;
	}

	if (amps.isZList()) {
		ampl = ((List*)amps.o())->packSome(th, n);
		ampz = ampl->mArray->z();
		ampStride = 1;
	} else {
		ampz = &amps.f;
		ampStride = 0;
	}

	P<List> list = makeWavetable((int)n, ampz, ampStride, phasez, phaseStride, smooth);
	th.push(list);
}

P<List> gParabolicTable;
P<List> gTriangleTable;
P<List> gSquareTable;
P<List> gSawtoothTable;

static void makeClassicWavetables()
{
	//fprintf(stdout, "computing wave tables\n");
	Z amps[kMaxHarmonics+1];
	Z phases[kMaxHarmonics+1];
	Z smooth = 0.;

	for (int i = 1; i <= kMaxHarmonics; ) {
		amps[i] = 1. / (i*i);		++i;
	}
	phases[0] = .25;
	gParabolicTable = makeWavetable(kMaxHarmonics, amps+1, 1, phases, 0, smooth);

	for (int i = 1; i <= kMaxHarmonics; ) {
		amps[i] = 1. / (i*i);		++i; if (i > kMaxHarmonics) break;
		amps[i] = 0.;				++i; if (i > kMaxHarmonics) break;
		amps[i] = -1. / (i*i);		++i; if (i > kMaxHarmonics) break;
		amps[i] = 0.;				++i;
	}

	phases[0] = 0.;
	gTriangleTable = makeWavetable(kMaxHarmonics, amps+1, 1, phases, 0, smooth);

	for (int i = 1; i <= kMaxHarmonics; ) {
		amps[i] = 1. / i;		++i;  if (i > kMaxHarmonics) break;
		amps[i] = 0.;			++i;
	}
	phases[0] = 0.;
	gSquareTable = makeWavetable(kMaxHarmonics, amps+1, 1, phases, 0, smooth);

	for (int i = 1; i <= kMaxHarmonics; ) {
		amps[i] = 1. / i;		++i;
	}
	for (int i = 1; i <= kMaxHarmonics; ) {
		phases[i] = 0.;		++i;
		phases[i] = .5;		++i;
	}
	gSawtoothTable = makeWavetable(kMaxHarmonics, amps+1, 1, phases+1, 1, smooth);

	vm.addBifHelp("\n*** classic wave tables ***");
	vm.def("parTbl", gParabolicTable);		vm.addBifHelp("parTbl - parabolic wave table.");
	vm.def("triTbl", gTriangleTable);		vm.addBifHelp("triTbl - triangle wave table.");
	vm.def("sqrTbl", gSquareTable);			vm.addBifHelp("sqrTbl - square wave table.");
	vm.def("sawTbl", gSawtoothTable);		vm.addBifHelp("sawTbl - sawtooth wave table.");

	//fprintf(stdout, "done computing wave tables\n");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Osc : public ZeroInputUGen<Osc>
{
	P<Array> const array;
	Z phase;
	Z freq;
	Z* table;

	Osc(Thread& th, P<Array> const& inArray, Z ifreq, Z iphase) : ZeroInputUGen<Osc>(th, false),
		array(inArray),
		phase(sc_wrap(iphase, 0., 1.) * kWaveTableSizeF), freq(ifreq * kWaveTableSizeF * th.rate.invSampleRate)
	{
		Z numHarmonics = std::clamp(th.rate.freqLimit / fabs(ifreq), 0., kMaxHarmonicsF);
		Z inumHarmonics = floor(numHarmonics);
		int harmIndex = (int)inumHarmonics;
		Z tableF = lut(gTableForNumHarmonics, harmIndex, numHarmonics - inumHarmonics);
		Z tableI = floor(tableF);
		int tableNum = (int)tableI + 1;
		table = array->z() + kWaveTableSize * tableNum;
	}

	virtual const char* TypeName() const override { return "Osc"; }

	void calc(int n, Z* out)
	{
		const int mask = kWaveTableMask;

		for (int i = 0; i < n; ++i) {

			Z iphase = floor(phase);
			int index = (int)iphase;
			Z fracphase = phase - iphase;

			out[i] = oscilLUT(table, index, mask, fracphase);

			phase += freq;
			if (phase >= kWaveTableSizeF) phase -= kWaveTableSizeF;
			if (phase < 0.) phase += kWaveTableSizeF;
		}
	}
};

struct OscPM : public OneInputUGen<OscPM>
{
	P<Array> const array;
	Z phase;
	Z freq;
	Z* table;

	OscPM(Thread& th, P<Array> const& inArray, Z ifreq, Arg phasemod) : OneInputUGen<OscPM>(th, phasemod),
		array(inArray),
		phase(0.), freq(ifreq * kWaveTableSizeF * th.rate.invSampleRate)
	{
		Z numHarmonics = std::clamp(th.rate.freqLimit / fabs(ifreq), 0., kMaxHarmonicsF);
		Z inumHarmonics = floor(numHarmonics);
		int harmIndex = (int)inumHarmonics;
		Z tableF = lut(gTableForNumHarmonics, harmIndex, numHarmonics - inumHarmonics);
		Z tableI = floor(tableF);
		int tableNum = (int)tableI + 1;
		table = array->z() + kWaveTableSize * tableNum;
	}

	virtual const char* TypeName() const override { return "Osc"; }

	void calc(int n, Z* out, Z* phasemod, int phasemodStride)
	{
		const int mask = kWaveTableMask;

		for (int i = 0; i < n; ++i) {

			Z pphase = phase + *phasemod * kWaveTableSizeF;
			phasemod += phasemodStride;
			Z iphase = floor(pphase);
			int index = (int)iphase;
			Z fracphase = pphase - iphase;

			out[i] = oscilLUT(table, index, mask, fracphase);

			phase += freq;
			if (phase >= kWaveTableSizeF) phase -= kWaveTableSizeF;
			if (phase < 0.) phase += kWaveTableSizeF;
		}
	}
};

struct OscFM : public OneInputUGen<OscFM>
{
	P<Array> const array;
	Z phase;
	Z freqmul;
	Z freqLimit;
	Z* tables;

	OscFM(Thread& th, P<Array> const& inArray, Arg freq, Z iphase) : OneInputUGen<OscFM>(th, freq),
		array(inArray),
		phase(sc_wrap(iphase, 0., 1.) * kWaveTableSizeF), freqmul(kWaveTableSizeF * th.rate.invSampleRate),
		tables(array->z()),
		freqLimit(th.rate.freqLimit)
	{
	}

	virtual const char* TypeName() const override { return "OscFM"; }

	void calc(int n, Z* out, Z* freq, int freqStride)
	{
		const int mask = kWaveTableMask;
		for (int i = 0; i < n; ++i) {
			Z ffreq = *freq;
			freq += freqStride;
			Z numHarmonics = std::clamp(freqLimit / fabs(ffreq), 0., kMaxHarmonicsF);
			Z inumHarmonics = floor(numHarmonics);
			int harmIndex = (int)inumHarmonics;
			Z tableF = lut(gTableForNumHarmonics, harmIndex, numHarmonics - inumHarmonics);
			Z tableI = floor(tableF);

			int tableNum = (int)tableI;
			Z fractable = tableF - tableI;

			Z* tableA = tables + kWaveTableSize * tableNum;
			Z* tableB = tableA + kWaveTableSize;

			Z iphase = floor(phase);
			int index = (int)iphase;
			Z fracphase = phase - iphase;

			// optional: round off the attenuation of higher harmonics. this eliminates a broadband tick that happens when a straight line decays to zero.
			fractable = sc_scurve0(fractable);

			out[i] = oscilLUT2(tableA, tableB, index, mask, fracphase, fractable);

			phase += ffreq * freqmul;
			if (phase >= kWaveTableSizeF) phase -= kWaveTableSizeF;
			if (phase < 0.) phase += kWaveTableSizeF;
		}
	}
};

struct OscFMPM : public TwoInputUGen<OscFMPM>
{
	P<Array> const array;
	Z phase;
	Z freqmul;
	Z freqLimit;
	Z* tables;

	OscFMPM(Thread& th, P<Array> const& inArray, Arg freq, Arg phasemod) : TwoInputUGen<OscFMPM>(th, freq, phasemod),
		array(inArray),
		phase(0.), freqmul(kWaveTableSizeF * th.rate.invSampleRate),
		tables(array->z()),
		freqLimit(th.rate.freqLimit)
	{
	}

	virtual const char* TypeName() const override { return "OscFMPM"; }

	void calc(int n, Z* out, Z* freq, Z* phasemod, int freqStride, int phasemodStride)
	{
		const int mask = kWaveTableMask;
		for (int i = 0; i < n; ++i) {
			Z ffreq = *freq;
			freq += freqStride;
			//Z numHarmonics = std::min(freqLimit, cutoff) / ffreq;
			Z numHarmonics = std::clamp(freqLimit / fabs(ffreq), 0., kMaxHarmonicsF);
			Z inumHarmonics = floor(numHarmonics);
			int harmIndex = (int)inumHarmonics;
			Z tableF = lut(gTableForNumHarmonics, harmIndex, numHarmonics - inumHarmonics);
			Z tableI = floor(tableF);
			int tableNum = (int)tableI;
			Z fractable = tableF - tableI;

			Z* tableA = tables + kWaveTableSize * tableNum;
			Z* tableB = tableA + kWaveTableSize;

			Z pphase = phase + *phasemod * kWaveTableSizeF;
			phasemod += phasemodStride;
			Z iphase = floor(pphase);
			int index = (int)iphase;
			Z fracphase = pphase - iphase;

			// optional: round off the attenuation of higher harmonics. this eliminates a broadband tick that happens when a straight line decays to zero.
			fractable = sc_scurve0(fractable);

			out[i] = oscilLUT2(tableA, tableB, index, mask, fracphase, fractable);

			phase += ffreq * freqmul;
			if (phase >= kWaveTableSizeF) phase -= kWaveTableSizeF;
			if (phase < 0.) phase += kWaveTableSizeF;
		}
	}
};

static void newOsc(Thread& th, Arg freq, Arg phase, P<List> const& tables)
{
	if (freq.isZList()) {
		if (phase.isZList()) {
			th.push(new List(new OscFMPM(th, tables->mArray, freq, phase)));
		} else {
			th.push(new List(new OscFM(th, tables->mArray, freq, phase.asFloat())));
		}
	} else {
		if (phase.isZList()) {
			th.push(new List(new OscPM(th, tables->mArray, freq.asFloat(), phase)));
		} else {
			th.push(new List(new Osc(th, tables->mArray, freq.asFloat(), phase.asFloat())));
		}
	}
}

static void osc_(Thread& th, Prim* prim)
{
	P<List> tables = th.popZList("osc : tables");
	V phase = th.popZIn("osc : phase");
	V freq = th.popZIn("osc : freq");

	if (!tables->isPacked() || tables->length(th) != kWaveTableTotalSize) {
		post("osc : tables is not a wave table. must be a signal of %d x %d samples.", kNumTables, kWaveTableSize);
		throw errWrongType;
	}

	newOsc(th, freq, phase, tables);
}

static void par_(Thread& th, Prim* prim)
{
	V phase = th.popZIn("par : phase");
	V freq = th.popZIn("par : freq");

	newOsc(th, freq, phase, gParabolicTable);
}

static void tri_(Thread& th, Prim* prim)
{
	V phase = th.popZIn("tri : phase");
	V freq = th.popZIn("tri : freq");

	newOsc(th, freq, phase, gTriangleTable);
}

static void saw_(Thread& th, Prim* prim)
{
	V phase = th.popZIn("saw : phase");
	V freq = th.popZIn("saw : freq");

	newOsc(th, freq, phase, gSawtoothTable);
}

static void square_(Thread& th, Prim* prim)
{
	V phase = th.popZIn("square : phase");
	V freq = th.popZIn("square : freq");

	newOsc(th, freq, phase, gSquareTable);
}

struct OscPWM : public ThreeInputUGen<OscPWM>
{
	P<Array> const array;
	Z phase;
	Z freqmul;
	Z freqLimit;
	Z* tables;

	OscPWM(Thread& th, P<Array> const& inArray, Arg freq, Arg phasemod, Arg duty) : ThreeInputUGen<OscPWM>(th, freq, phasemod, duty),
		array(inArray),
		phase(0.), freqmul(kWaveTableSizeF * th.rate.invSampleRate),
		tables(array->z()),
		freqLimit(th.rate.freqLimit)
	{
	}

	virtual const char* TypeName() const override { return "OscPWM"; }

	void calc(int n, Z* out, Z* freq, Z* phasemod, Z* duty, int freqStride, int phasemodStride, int dutyStride)
	{
		const int mask = kWaveTableMask;
		for (int i = 0; i < n; ++i) {
			Z ffreq = *freq;
			freq += freqStride;
			//Z numHarmonics = std::min(freqLimit, cutoff) / ffreq;
			Z numHarmonics = std::clamp(freqLimit / fabs(ffreq), 0., kMaxHarmonicsF);
			Z inumHarmonics = floor(numHarmonics);
			int harmIndex = (int)inumHarmonics;
			Z tableF = lut(gTableForNumHarmonics, harmIndex, numHarmonics - inumHarmonics);
			Z tableI = floor(tableF);
			int tableNum = (int)tableI;
			Z fractable = tableF - tableI;

			Z* tableA = tables + kWaveTableSize * tableNum;
			Z* tableB = tableA + kWaveTableSize;

			Z pphase1 = phase + *phasemod * kWaveTableSizeF;
			Z iphase1 = floor(pphase1);
			int index1 = (int)iphase1;
			Z fracphase1 = pphase1 - iphase1;

			Z pphase2 = pphase1 + *duty * kWaveTableSizeF;
			Z iphase2 = floor(pphase2);
			int index2 = (int)iphase2;
			Z fracphase2 = pphase2 - iphase2;

			phasemod += phasemodStride;
			duty += dutyStride;

			// optional: round off the attenuation of higher harmonics. this eliminates a broadband tick that happens when a straight line decays to zero.
			fractable = sc_scurve0(fractable);

			Z a = oscilLUT2(tableA, tableB, index1, mask, fracphase1, fractable);
			Z b = oscilLUT2(tableA, tableB, index2, mask, fracphase2, fractable);
			out[i] = .5 * (a - b);

			phase += ffreq * freqmul;
			if (phase >= kWaveTableSizeF) phase -= kWaveTableSizeF;
			if (phase < 0.) phase += kWaveTableSizeF;
		}
	}
};

struct VarSaw : public ThreeInputUGen<VarSaw>
{
	P<Array> const array;
	Z phase;
	Z freqmul;
	Z freqLimit;
	Z* tables;

	VarSaw(Thread& th, P<Array> const& inArray, Arg freq, Arg phasemod, Arg duty) : ThreeInputUGen<VarSaw>(th, freq, phasemod, duty),
		array(inArray),
		phase(0.), freqmul(kWaveTableSizeF * th.rate.invSampleRate),
		tables(array->z()),
		freqLimit(th.rate.freqLimit)
	{
	}

	virtual const char* TypeName() const override { return "VarSaw"; }

	void calc(int n, Z* out, Z* freq, Z* phasemod, Z* duty, int freqStride, int phasemodStride, int dutyStride)
	{
		const int mask = kWaveTableMask;
		for (int i = 0; i < n; ++i) {
			Z ffreq = *freq;
			freq += freqStride;
			//Z numHarmonics = std::min(freqLimit, cutoff) / ffreq;
			Z numHarmonics = std::clamp(freqLimit / fabs(ffreq), 0., kMaxHarmonicsF);
			Z inumHarmonics = floor(numHarmonics);
			int harmIndex = (int)inumHarmonics;
			Z tableF = lut(gTableForNumHarmonics, harmIndex, numHarmonics - inumHarmonics);
			Z tableI = floor(tableF);
			int tableNum = (int)tableI;
			Z fractable = tableF - tableI;

			Z* tableA = tables + kWaveTableSize * tableNum;
			Z* tableB = tableA + kWaveTableSize;

			Z pphase1 = phase + *phasemod * kWaveTableSizeF;
			Z iphase1 = floor(pphase1);
			int index1 = (int)iphase1;
			Z fracphase1 = pphase1 - iphase1;

			Z zduty = std::clamp(*duty, .01, .99);
			Z pphase2 = pphase1 + zduty * kWaveTableSizeF;
			Z iphase2 = floor(pphase2);
			int index2 = (int)iphase2;
			Z fracphase2 = pphase2 - iphase2;

			phasemod += phasemodStride;
			duty += dutyStride;

			// optional: round off the attenuation of higher harmonics. this eliminates a broadband tick that happens when a straight line decays to zero.
			fractable = sc_scurve0(fractable);

			Z a = oscilLUT2(tableA, tableB, index1, mask, fracphase1, fractable);
			Z b = oscilLUT2(tableA, tableB, index2, mask, fracphase2, fractable);

			Z amp = .25 / (zduty - zduty * zduty);
			out[i] = amp * (a - b);

			phase += ffreq * freqmul;
			if (phase >= kWaveTableSizeF) phase -= kWaveTableSizeF;
			if (phase < 0.) phase += kWaveTableSizeF;
		}
	}
};

static void oscp_(Thread& th, Prim* prim)
{
	P<List> tables = th.popZList("oscp : tables");
	V duty = th.popZIn("oscp : phaseOffset");
	V phase = th.popZIn("oscp : phase");
	V freq = th.popZIn("oscp : freq");

	if (!tables->isPacked() || tables->length(th) != kWaveTableTotalSize) {
		post("oscp : tables is not a wave table. must be a signal of %d x %d samples.", kNumTables, kWaveTableSize);
		throw errWrongType;
	}

	th.push(new List(new OscPWM(th, tables->mArray, freq, phase, duty)));
}

static void pulse_(Thread& th, Prim* prim)
{
	V duty = th.popZIn("pulse : phaseOffset");
	V phase = th.popZIn("pulse : phase");
	V freq = th.popZIn("pulse : freq");

	P<List> tables = gSawtoothTable;

	th.push(new List(new OscPWM(th, tables->mArray, freq, phase, duty)));
}

static void vsaw_(Thread& th, Prim* prim)
{
	V duty = th.popZIn("vsaw : phaseOffset");
	V phase = th.popZIn("vsaw : phase");
	V freq = th.popZIn("vsaw : freq");

	P<List> tables = gParabolicTable;

	th.push(new List(new VarSaw(th, tables->mArray, freq, phase, duty)));
}

struct SyncOsc : public TwoInputUGen<SyncOsc>
{
	P<Array> const array;
	Z sinePhaseStart;
	Z sinePhaseReset;
	Z sinePhaseEnd;
    Z wavePhaseResetRatio;
	Z phase1;
	Z phase2a;
	Z phase2b;
	Z freqmul1;
	Z freqmul2;
	Z freqLimit;
	Z* tables;
    bool once = true;

	SyncOsc(Thread& th, P<Array> const& inArray, Arg freq1, Arg freq2) : TwoInputUGen<SyncOsc>(th, freq1, freq2),
		array(inArray),
        sinePhaseStart(kSineTableSize/4),
		sinePhaseReset(kSineTableSize/2),
		sinePhaseEnd(sinePhaseStart + sinePhaseReset),
        wavePhaseResetRatio(kWaveTableSizeF / sinePhaseReset),
		phase1(sinePhaseStart),
        phase2a(0.),
        phase2b(0.),
		freqmul1(.5 * th.rate.radiansPerSample * gInvSineTableOmega),
		freqmul2(kWaveTableSizeF * th.rate.invSampleRate),
		freqLimit(th.rate.freqLimit),
		tables(array->z())
	{
	}

	virtual const char* TypeName() const override { return "SyncOsc"; }

	void calc(int n, Z* out, Z* freq1, Z* freq2, int freq1Stride, int freq2Stride)
	{
        if (once) {
            once = false;
            phase2b = kWaveTableSizeF * (fabs(*freq2) / fabs(*freq1));
        }
		const int mask = kWaveTableMask;
		for (int i = 0; i < n; ++i) {
			Z ffreq1 = fabs(*freq1);
			Z ffreq2 = fabs(*freq2);
			freq1 += freq1Stride;
			freq2 += freq2Stride;

			Z numHarmonics = std::clamp(freqLimit / fabs(ffreq2), 0., kMaxHarmonicsF);
			Z inumHarmonics = floor(numHarmonics);
			int harmIndex = (int)inumHarmonics;
			Z tableF = lut(gTableForNumHarmonics, harmIndex, numHarmonics - inumHarmonics);
			Z tableI = floor(tableF);
			int tableNum = (int)tableI;
			Z fractable = tableF - tableI;

			Z* tableA = tables + kWaveTableSize * tableNum;
			Z* tableB = tableA + kWaveTableSize;

			Z iphase2a = floor(phase2a);
			int index2a = (int)iphase2a;
			Z fracphase2a = phase2a - iphase2a;

			Z iphase2b = floor(phase2b);
			int index2b = (int)iphase2b;
			Z fracphase2b = phase2b - iphase2b;

			// optional: round off the attenuation of higher harmonics. this eliminates an (extremely quiet) broadband tick that happens when a straight line decays to zero.
			fractable = sc_scurve0(fractable);

			Z sawA = oscilLUT2(tableA, tableB, index2a, mask, fracphase2a, fractable);
			Z sawB = oscilLUT2(tableA, tableB, index2b, mask, fracphase2b, fractable);

			Z window = .5 - .5 * tsinx(phase1);
			out[i] = sawB + window * (sawA - sawB);

			Z freq2inc = ffreq2 * freqmul2;

			phase2a += freq2inc;
			if (phase2a >= kWaveTableSizeF) phase2a -= kWaveTableSizeF;

			phase2b += freq2inc;
			if (phase2b >= kWaveTableSizeF) phase2b -= kWaveTableSizeF;

			phase1 += ffreq1 * freqmul1;
			if (phase1 >= sinePhaseEnd) {
				phase1 -= sinePhaseReset;

                // reset and swap phases
                phase2b = phase2a;
                phase2a = wavePhaseResetRatio * (phase1 - sinePhaseStart) * (ffreq2 / ffreq1); // reset to proper fractional position.
			}
		}
	}
};

static void ssaw_(Thread& th, Prim* prim)
{
	V freq2 = th.popZIn("ssaw : freq2");
	V freq1 = th.popZIn("ssaw : freq1");

	P<List> tables = gSawtoothTable;

	th.push(new List(new SyncOsc(th, tables->mArray, freq1, freq2)));
}

static void sosc_(Thread& th, Prim* prim)
{
	P<List> tables = th.popZList("sosc : tables");
	V freq2 = th.popZIn("sosc : freq2");
	V freq1 = th.popZIn("sosc : freq1");

	if (!tables->isPacked() || tables->length(th) != kWaveTableTotalSize) {
		post("sosc : tables is not a wave table. must be a signal of %d x %d samples.", kNumTables, kWaveTableSize);
		throw errWrongType;
	}

	th.push(new List(new SyncOsc(th, tables->mArray, freq1, freq2)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

struct LFSaw : public OneInputUGen<LFSaw>
{
	Z phase;
	Z freqmul;

	LFSaw(Thread& th, Arg freq, Z iphase) : OneInputUGen<LFSaw>(th, freq), phase(sc_wrap(2. * iphase, -1., 1.)), freqmul(th.rate.invNyquistRate)
	{
	}

	virtual const char* TypeName() const override { return "LFSaw"; }

	void calc(int n, Z* out, Z* freq, int freqStride)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = phase;
			phase += *freq * freqmul;
			freq += freqStride;
			if (phase >= 1.) phase -= 2.;
			else if (phase < -1.) phase += 2.;
		}
	}
};

struct LFSaw2 : public TwoInputUGen<LFSaw2>
{
	Z phase;
	Z freqmul;

	LFSaw2(Thread& th, Arg freq, Arg phasem) : TwoInputUGen<LFSaw2>(th, freq, phasem), phase(0.), freqmul(th.rate.invNyquistRate)
	{
	}

	virtual const char* TypeName() const override { return "LFSaw2"; }

	void calc(int n, Z* out, Z* freq, Z* phasem, int freqStride, int phasemStride)
	{
		for (int i = 0; i < n; ++i) {
			Z pphase = phase + 2. * *phasem - 1.;
			if (pphase >= 1.) do { pphase -= 2.; } while (pphase >= 1.);
			else if (pphase < -1.) do { pphase += 2.; } while (pphase < -1.);
			out[i] = pphase;
			phase += *freq * freqmul;
			freq += freqStride;
			if (phase >= 1.) phase -= 2.;
			else if (phase < -1.) phase += 2.;
		}
	}
};

struct LFTri : public OneInputUGen<LFTri>
{
	Z phase;
	Z freqmul;

	LFTri(Thread& th, Arg freq, Z iphase) : OneInputUGen<LFTri>(th, freq), phase(sc_wrap(2. * iphase, -1., 1.)), freqmul(2. * th.rate.invNyquistRate)
	{
	}

	virtual const char* TypeName() const override { return "LFTri"; }

	void calc(int n, Z* out, Z* freq, int freqStride)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = phase <= 1. ? phase : 2. - phase;
			phase += *freq * freqmul;
			freq += freqStride;
			if (phase >= 3.) phase -= 4.;
			else if (phase < -1.) phase += 4.;
		}
	}
};

static void lfsaw_(Thread& th, Prim* prim)
{
	Z phase = th.popFloat("lfsaw : phase");
	V freq = th.popZIn("lfsaw : freq");

	th.push(new List(new LFSaw(th, freq, phase)));
}

static void lftri_(Thread& th, Prim* prim)
{
	Z phase = th.popFloat("lftri : phase");
	V freq = th.popZIn("lftri : freq");

	th.push(new List(new LFTri(th, freq, phase)));
}

struct LFPulse : public TwoInputUGen<LFPulse>
{
	Z phase;
	Z freqmul;

	LFPulse(Thread& th, Arg freq, Z iphase, Arg duty) : TwoInputUGen<LFPulse>(th, freq, duty), phase(sc_wrap(iphase, 0., 1.)), freqmul(th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "LFPulse"; }

	void calc(int n, Z* out, Z* freq, Z* duty, int freqStride, int dutyStride)
	{
		for (int i = 0; i < n; ++i) {
			if (phase >= 1.) {
				phase -= 1.;
				// output at least one sample from the opposite polarity
				out[i] = *duty < 0.5 ? 1. : 0.;
			} else {
				out[i] = phase < *duty ? 1.f : 0.f;
			}

			phase += *freq * freqmul;
			duty += dutyStride;
			freq += freqStride;
		}
	}
};

struct LFPulseBipolar : public TwoInputUGen<LFPulseBipolar>
{
	Z phase;
	Z freqmul;

	LFPulseBipolar(Thread& th, Arg freq, Z iphase, Arg duty) : TwoInputUGen<LFPulseBipolar>(th, freq, duty), phase(sc_wrap(iphase, 0., 1.)), freqmul(th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "LFPulseBipolar"; }

	void calc(int n, Z* out, Z* freq, Z* duty, int freqStride, int dutyStride)
	{
		for (int i = 0; i < n; ++i) {
			if (phase >= 1.) {
				phase -= 1.;
				// output at least one sample from the opposite polarity
				out[i] = *duty < 0.5 ? *duty : *duty - 1. ;
			} else {
				out[i] = phase < *duty ?  *duty : *duty - 1.;
			}

			phase += *freq * freqmul;
			duty += dutyStride;
			freq += freqStride;
		}
	}
};

struct LFSquare : public OneInputUGen<LFSquare>
{
	Z phase;
	Z freqmul;

	LFSquare(Thread& th, Arg freq, Z iphase) : OneInputUGen<LFSquare>(th, freq), phase(sc_wrap(iphase, 0., 1.)), freqmul(th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "LFSquare"; }

	void calc(int n, Z* out, Z* freq, int freqStride)
	{
		for (int i = 0; i < n; ++i) {
			if (phase >= 1.) phase -= 1.;
			out[i] = phase < .5 ? 1. : -1.;
			phase += *freq * freqmul;
			freq += freqStride;
		}
	}
};

////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Vosim : public TwoInputUGen<Vosim>
{
	Z phase;
	Z freqmul;
	Z freqLimit;

	Vosim(Thread& th, Arg freq, Z iphase, Arg nth) : TwoInputUGen<Vosim>(th, freq, nth), phase(sc_wrap(2. * iphase, -1., 1.)), freqmul(th.rate.invSampleRate), freqLimit(.5*th.rate.freqLimit)
	{
	}

	virtual const char* TypeName() const override { return "SmoothSaw"; }

	void calc(int n, Z* out, Z* freq, Z* nth, int freqStride, int nthStride)
	{
		for (int i = 0; i < n; ++i) {
			Z maxnth = (freqLimit / *freq);
			out[i] = sc_squared(std::sin(M_PI*std::min(maxnth, *nth)*phase)) * sc_squared(1.-phase);
			phase += *freq * freqmul;
			freq += freqStride;
			nth += nthStride;
			if (phase >= 1.) phase -= 1.;
			else if (phase < 0.) phase += 1.;
		}
	}
};

static void vosim_(Thread& th, Prim* prim)
{
	V n = th.popZIn("vosim : n");
	Z phase = th.popFloat("vosim : phase");
	V freq = th.popZIn("vosim : freq");

	th.push(new List(new Vosim(th, freq, phase, n)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

struct SmoothSaw : public TwoInputUGen<SmoothSaw>
{
	Z phase;
	Z freqmul;
	Z freqLimit;

	SmoothSaw(Thread& th, Arg freq, Z iphase, Arg nth) : TwoInputUGen<SmoothSaw>(th, freq, nth), phase(sc_wrap(2. * iphase, -1., 1.)), freqmul(th.rate.invNyquistRate), freqLimit(th.rate.freqLimit)
	{
	}

	virtual const char* TypeName() const override { return "SmoothSaw"; }

	void calc(int n, Z* out, Z* freq, Z* nth, int freqStride, int nthStride)
	{
		for (int i = 0; i < n; ++i) {
			Z maxnth = freqLimit / *freq;
			out[i] = phase-phase*std::pow(std::abs(phase),std::min(maxnth, *nth));
			phase += *freq * freqmul;
			freq += freqStride;
			nth += nthStride;
			if (phase >= 1.) phase -= 2.;
			else if (phase < -1.) phase += 2.;
		}
	}
};

struct SmoothSawPWM : public ThreeInputUGen<SmoothSawPWM>
{
	Z phase;
	Z freqmul;
	Z freqLimit;

	SmoothSawPWM(Thread& th, Arg freq, Z iphase, Arg nth, Arg duty) : ThreeInputUGen<SmoothSawPWM>(th, freq, nth, duty), phase(sc_wrap(2. * iphase, -1., 1.)), freqmul(th.rate.invNyquistRate), freqLimit(th.rate.freqLimit)
	{
	}

	virtual const char* TypeName() const override { return "SmoothSaw"; }

	void calc(int n, Z* out, Z* freq, Z* nth, Z* duty, int freqStride, int nthStride, int dutyStride)
	{
		for (int i = 0; i < n; ++i) {
			Z maxnth = freqLimit / *freq;
			Z w = *duty;
			Z u = .5*phase - .5;
			Z wphase = (w+u)/(w*phase-u);
			out[i] = wphase*(1.-std::pow(std::abs(phase),std::min(maxnth, *nth)));
			phase += *freq * freqmul;
			freq += freqStride;
			nth += nthStride;
			duty += dutyStride;
			if (phase >= 1.) phase -= 2.;
			else if (phase < -1.) phase += 2.;
		}
	}
};

static void smoothsawpwm_(Thread& th, Prim* prim)
{
	V duty = th.popZIn("smoothsawpwm : duty");
	V n = th.popZIn("smoothsawpwm : n");
	Z phase = th.popFloat("smoothsawpwm : phase");
	V freq = th.popZIn("smoothsawpwm : freq");

	th.push(new List(new SmoothSawPWM(th, freq, phase, n, duty)));
}

static void smoothsaw_(Thread& th, Prim* prim)
{
	V n = th.popZIn("smoothsaw : n");
	Z phase = th.popFloat("smoothsaw : phase");
	V freq = th.popZIn("smoothsaw : freq");

	th.push(new List(new SmoothSaw(th, freq, phase, n)));
}

static void lfpulse_(Thread& th, Prim* prim)
{
	V duty = th.popZIn("lfpulse : duty");
	Z phase = th.popFloat("lfpulse : phase");
	V freq = th.popZIn("lfpulse : freq");

	th.push(new List(new LFPulse(th, freq, phase, duty)));
}

static void lfpulseb_(Thread& th, Prim* prim)
{
	V duty = th.popZIn("lfpulseb : duty");
	Z phase = th.popFloat("lfpulseb : phase");
	V freq = th.popZIn("lfpulseb : freq");

	th.push(new List(new LFPulseBipolar(th, freq, phase, duty)));
}

static void lfsquare_(Thread& th, Prim* prim)
{
	Z phase = th.popFloat("lfsquare : phase");
	V freq = th.popZIn("lfsquare : freq");

	th.push(new List(new LFSquare(th, freq, phase)));
}

struct Impulse : public OneInputUGen<Impulse>
{
	Z phase;
	Z freqmul;

	Impulse(Thread& th, Arg freq, Z iphase) : OneInputUGen<Impulse>(th, freq), phase(sc_wrap(iphase, 0., 1.)), freqmul(th.rate.invSampleRate)
	{
		if (phase == 0.) phase = 1.; // force an initial impulse.
	}

	virtual const char* TypeName() const override { return "Impulse"; }

	void calc(int n, Z* out, Z* freq, int freqStride)
	{
		for (int i = 0; i < n; ++i) {
			if (phase >= 1.) {
				phase -= 1.;
				out[i] = 1.;
			} else {
				out[i] = 0.;
			}
			phase += *freq * freqmul;
			freq += freqStride;
		}
	}
};

static void impulse_(Thread& th, Prim* prim)
{
	Z phase = th.popFloat("impulse : phase");
	V freq = th.popZIn("impulse : freq");

	th.push(new List(new Impulse(th, freq, phase)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

struct SinOsc : public OneInputUGen<SinOsc>
{
	Z phase;
	Z freqmul;

	SinOsc(Thread& th, Arg freq, Z iphase) : OneInputUGen<SinOsc>(th, freq),
		phase(sc_wrap(iphase, 0., 1.) * kTwoPi), freqmul(th.rate.radiansPerSample)
	{
	}

	virtual const char* TypeName() const override { return "SinOsc"; }

	void calc(int n, Z* out, Z* freq, int freqStride)
	{
#if 1
		for (int i = 0; i < n; ++i) {
			out[i] = phase;
			phase += *freq * freqmul;
			freq += freqStride;
			if (phase >= kTwoPi) phase -= kTwoPi;
			else if (phase < 0.) phase += kTwoPi;
		}
		vvsin(out, out, &n);
#else
		for (int i = 0; i < n; ++i) {
			out[i] = sin(phase);
			phase += *freq * freqmul;
			freq += freqStride;
			if (phase >= kTwoPi) phase -= kTwoPi;
			else if (phase < 0.) phase += kTwoPi;
		}
#endif
	}
};

struct SinOsc2 : public OneInputUGen<SinOsc2>
{
	Z phase;
	Z freqmul;

	SinOsc2(Thread& th, Arg freq, Z iphase) : OneInputUGen<SinOsc2>(th, freq),
		phase(sc_wrap(iphase, 0., 1.) * kTwoPi), freqmul(th.rate.radiansPerSample)
	{
	}

	virtual const char* TypeName() const override { return "SinOsc2"; }

	void calc(int n, Z* out, Z* freq, int freqStride)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = tsin(phase);
			phase += *freq * freqmul;
			freq += freqStride;
			if (phase >= kTwoPi) phase -= kTwoPi;
			else if (phase < 0.) phase += kTwoPi;
		}
	}
};

struct TSinOsc : public OneInputUGen<TSinOsc>
{
	Z phase;
	Z freqmul;

	TSinOsc(Thread& th, Arg freq, Z iphase) : OneInputUGen<TSinOsc>(th, freq), phase(sc_wrap(iphase, 0., 1.) * kTwoPi), freqmul(th.rate.radiansPerSample)
	{
	}

	virtual const char* TypeName() const override { return "TSinOsc"; }

	void calc(int n, Z* out, Z* freq, int freqStride)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = tsin(phase);
			phase += *freq * freqmul;
			freq += freqStride;
			if (phase >= kTwoPi) phase -= kTwoPi;
			else if (phase < 0.) phase += kTwoPi;
		}
	}
};

struct FSinOsc : public ZeroInputUGen<FSinOsc>
{
	Z freq;
	Z b1, y1, y2;

	FSinOsc(Thread& th, Z ifreq, Z iphase) : ZeroInputUGen<FSinOsc>(th, false),
		freq(ifreq * th.rate.radiansPerSample),
		b1(2. * cos(freq))
	{
		iphase = sc_wrap(iphase, 0., 1.) * kTwoPi;
		y1 = sin(iphase-freq);
		y2 = sin(iphase-2.*freq);
	}

	virtual const char* TypeName() const override { return "FSinOsc"; }

	void calc(int n, Z* out)
	{
		Z zy1 = y1;
		Z zy2 = y2;
		Z zb1 = b1;
		for (int i = 0; i < n; ++i) {
			Z y0 = zb1 * zy1 - zy2;
			out[i] = y0;
			zy2 = zy1;
			zy1 = y0;
		}
		y1 = zy1;
		y2 = zy2;
	}
};

struct SinOscPMFB : public TwoInputUGen<SinOscPMFB>
{
	Z phase;
	Z freqmul;
	Z y1;

	SinOscPMFB(Thread& th, Arg freq, Z iphase, Arg phasefb) : TwoInputUGen<SinOscPMFB>(th, freq, phasefb), phase(sc_wrap(iphase, 0., 1.) * kTwoPi), freqmul(th.rate.radiansPerSample)
	{
		freqmul = th.rate.radiansPerSample;
	}

	virtual const char* TypeName() const override { return "SinOscPMFB"; }

	void calc(int n, Z* out, Z* freq, Z* phasefb, int freqStride, int phasefbStride)
	{
		for (int i = 0; i < n; ++i) {
			Z y0 = sin(phase + *phasefb * y1);
			out[i] = y0;
			y1 = y0;
			phase += *freq * freqmul;
			freq += freqStride;
			phasefb += phasefbStride;
			if (phase >= kTwoPi) phase -= kTwoPi;
			else if (phase < 0.) phase += kTwoPi;
		}
	}
};

struct SinOscPM : public TwoInputUGen<SinOscPM>
{
	Z phase;
	Z freqmul;

	SinOscPM(Thread& th, Arg freq, Arg phasemod) : TwoInputUGen<SinOscPM>(th, freq, phasemod), phase(0.), freqmul(th.rate.radiansPerSample)
	{
		freqmul = th.rate.radiansPerSample;
	}

	virtual const char* TypeName() const override { return "SinOscPM"; }

	void calc(int n, Z* out, Z* freq, Z* phasemod, int freqStride, int phasemodStride)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = phase + *phasemod * kTwoPi;
			phase += *freq * freqmul;
			freq += freqStride;
			phasemod += phasemodStride;
			if (phase >= kTwoPi) phase -= kTwoPi;
			else if (phase < 0.) phase += kTwoPi;
		}
		vvsin(out, out, &n);
	}
};

struct SinOscM : public ThreeInputUGen<SinOscM>
{
	Z phase;
	Z freqmul;

	SinOscM(Thread& th, Arg freq, Z iphase, Arg mul, Arg add) : ThreeInputUGen<SinOscM>(th, freq, mul, add), phase(sc_wrap(iphase, 0., 1.) * kTwoPi), freqmul(th.rate.radiansPerSample)
	{
	}

	virtual const char* TypeName() const override { return "SinOscM"; }

	void calc(int n, Z* out, Z* freq, Z* mul, Z* add, int freqStride, int mulStride, int addStride)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = sin(phase) * *mul + *add;
			phase += *freq * freqmul;
			freq += freqStride;
			mul += mulStride;
			add += addStride;
			if (phase >= kTwoPi) phase -= kTwoPi;
			else if (phase < 0.) phase += kTwoPi;
		}
	}
};

struct SinOscPMM : public FourInputUGen<SinOscPMM>
{
	Z phase;
	Z freqmul;

	SinOscPMM(Thread& th, Arg freq, Arg phasemod, Arg mul, Arg add)
		: FourInputUGen<SinOscPMM>(th, freq, phasemod, mul, add), phase(0.), freqmul(th.rate.radiansPerSample)
	{
	}

	virtual const char* TypeName() const override { return "SinOscPMM"; }

	void calc(int n, Z* out, Z* freq, Z* phasemod, Z* mul, Z* add, int freqStride, int phasemodStride, int mulStride, int addStride)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = sin(phase + *phasemod) * *mul + *add;
			phase += *freq * freqmul;
			freq += freqStride;
			phasemod += phasemodStride;
			mul += mulStride;
			add += addStride;
			if (phase >= kTwoPi) phase -= kTwoPi;
			else if (phase < 0.) phase += kTwoPi;
		}
	}
};

static void tsinosc_(Thread& th, Prim* prim)
{
	Z phase = th.popFloat("tsinosc : iphase");
	V freq = th.popZIn("tsinosc : freq");

    th.push(new List(new SinOsc2(th, freq, phase)));
}

static void sinosc_(Thread& th, Prim* prim)
{
	V phase = th.popZIn("sinosc : phase");
	V freq = th.popZIn("sinosc : freq");

	if (phase.isZList()) {
		th.push(new List(new SinOscPM(th, freq, phase)));
	} else if (freq.isZList()) {
		th.push(new List(new SinOsc(th, freq, phase.f)));
	} else {
		th.push(new List(new FSinOsc(th, freq.f, phase.f)));
	}
}

static void sinoscm_(Thread& th, Prim* prim)
{
	V add = th.popZIn("sinoscm : mul");
	V mul = th.popZIn("sinoscm : add");
	V phase = th.popZIn("sinoscm : phase");
	V freq = th.popZIn("sinoscm : freq");

	if (phase.isZList()) {
		th.push(new List(new SinOscPMM(th, freq, phase, mul, add)));
	} else {
		th.push(new List(new SinOscM(th, freq, phase.f, mul, add)));
	}
}

static void sinoscfb_(Thread& th, Prim* prim)
{
	V fb = th.popZIn("sinoscfb : fb");
	Z iphase = th.popFloat("sinoscfb : phase");
	V freq = th.popZIn("sinoscfb : freq");

	th.push(new List(new SinOscPMFB(th, freq, iphase, fb)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Blip : public TwoInputUGen<Blip>
{
	Z phase;
	Z freqmul;
	Z nyq_;
	Blip(Thread& th, Arg freq, Z iphase, Arg numharms)
		: TwoInputUGen<Blip>(th, freq, numharms), phase(sc_wrap(2. * iphase - 1., -1., 1.)), freqmul(th.rate.radiansPerSample),
		nyq_(th.rate.sampleRate * .5)
	{
	}

	virtual const char* TypeName() const override { return "Blip"; }

	void calc(int n, Z* out, Z* freq, Z* numharms, int freqStride, int numharmsStride)
	{
		Z nyq = nyq_;

		for (int i = 0; i < n; ++i) {

			//f(x)=x-x*sqrt(c^2+1)/sqrt(c^2*x^2+1)
			Z ffreq = *freq * freqmul;

			Z maxN = floor(nyq / ffreq);
			Z N = *numharms;

			if (N > maxN) N = maxN;
			else if (N < 1.) N = 1.;

			Z Na = floor(N);
			Z Nb = Na + 1.;

			Z frac = N - Na;
			Z Na_scale = .5 / Na;
			Z Nb_scale = .5 / Nb;

			Z Na2 = 2. * Na + 1.;
			Z Nb2 = Na2 + 2.;

			Z d = 1. / sin(phase);
			Z a = Na_scale * (sin(Na2 * phase) * d - 1.);
			Z b = Nb_scale * (sin(Nb2 * phase) * d - 1.);

			frac = sc_scurve0(frac); // this eliminates out a broadband tick in the spectrum.

			out[i] = a + frac * (b - a);

			phase += ffreq;
			freq += freqStride;
			numharms += numharmsStride;
			if (phase >= kTwoPi) phase -= kTwoPi;
			else if (phase < -kTwoPi) phase += kTwoPi;
		}
	}
};

static void blip_(Thread& th, Prim* prim)
{
	V numharms = th.popZIn("blip : numharms");
	Z phase = th.popFloat("blip : phase");
	V freq = th.popZIn("blip : freq");

	th.push(new List(new Blip(th, freq, phase, numharms)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

struct DSF1 : public FourInputUGen<DSF1>
{
	Z phase1;
	Z phase2;
	Z freqmul;
	Z N, N1;
	DSF1(Thread& th, Arg freq, Arg carRatio, Arg modRatio, Arg coef, Z numharms)
		: FourInputUGen<DSF1>(th, freq, carRatio, modRatio, coef), phase1(0.), phase2(0.), freqmul(th.rate.radiansPerSample)
	{
		N = numharms < 1. ? 1. : floor(numharms);
		N1 = N + 1.;
	}

	virtual const char* TypeName() const override { return "DSF1"; }

	void calc(int n, Z* out, Z* freq, Z* carRatio, Z* modRatio, Z* coef, int freqStride, int carStride, int modStride, int coefStride)
	{
		Z p1 = phase1;
		Z p2 = phase2;
		for (int i = 0; i < n; ++i) {

			//f(x)=x-x*sqrt(c^2+1)/sqrt(c^2*x^2+1)

			Z a = *coef;
			Z a2 = a*a;
			Z an1 = pow(a, N1);
			Z scale = (a - 1.)/(an1 - 1.);
			out[i] = scale * (sin(p1) - a * sin(p1-p2) - an1 * (sin(p1 + N1*p2) - a * sin(p1 + N*p2)))/(1. + a2 - 2. * a * cos(p2));
printf("%d %f\n", i, out[i]);
			Z ffreq = *freq * freqmul;
			Z f1 = ffreq * *carRatio;
			Z f2 = ffreq * *modRatio;
			p1 += f1;
			p2 += f2;
			freq += freqStride;
			carRatio += carStride;
			modRatio += modStride;
			coef += coefStride;
			if (p1 >= kTwoPi) p1 -= kTwoPi;
			else if (p1 < -kTwoPi) p1 += kTwoPi;
			if (p2 >= kTwoPi) p2 -= kTwoPi;
			else if (p2 < -kTwoPi) p2 += kTwoPi;
		}
		phase1 = p1;
		phase2 = p2;
	}
};

static void dsf1_(Thread& th, Prim* prim)
{
	Z numharms = th.popFloat("dsf1 : numharms");
	V coef = th.popZIn("dsf1 : coef");
	V modRatio = th.popZIn("dsf1 : modRatio");
	V carRatio = th.popZIn("dsf1 : carRatio");
	V freq = th.popZIn("dsf1 : freq");

	th.push(new List(new DSF1(th, freq, carRatio, modRatio, coef, numharms)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

struct DSF3 : public FourInputUGen<DSF3>
{
	Z phase1;
	Z phase2;
	Z freqmul;
	Z N, N1;
	DSF3(Thread& th, Arg freq, Arg carRatio, Arg modRatio, Arg coef, Z numharms)
		: FourInputUGen<DSF3>(th, freq, carRatio, modRatio, coef), phase1(0.), phase2(0.), freqmul(th.rate.radiansPerSample)
	{
		N = numharms < 1. ? 1. : floor(numharms);
		N1 = N + 1.;
	}

	virtual const char* TypeName() const override { return "DSF3"; }

	void calc(int n, Z* out, Z* freq, Z* carRatio, Z* modRatio, Z* coef, int freqStride, int carStride, int modStride, int coefStride)
	{
		Z p1 = phase1;
		Z p2 = phase2;
		for (int i = 0; i < n; ++i) {

			//f(x)=x-x*sqrt(c^2+1)/sqrt(c^2*x^2+1)

			Z a = std::clamp(*coef, -.9999, .9999);
			Z a2 = a*a;
			Z an1 = pow(a, N1);
			Z scalePeak = (a - 1.)/(2.*an1 - a - 1.);
			Z scale = scalePeak;
			Z denom = (1. + a2 - 2. * a * cos(p2));
			out[i] = scale * sin(p1) * (1. - a2 - 2. * an1 * (cos(N1*p2) - a * cos(N*p2)))/denom;

			Z ffreq = *freq * freqmul;
			Z f1 = ffreq * *carRatio;
			Z f2 = ffreq * *modRatio;
			p1 += f1;
			p2 += f2;
			freq += freqStride;
			carRatio += carStride;
			modRatio += modStride;
			coef += coefStride;
			if (p1 >= kTwoPi) p1 -= kTwoPi;
			else if (p1 < -kTwoPi) p1 += kTwoPi;
			if (p2 >= kTwoPi) p2 -= kTwoPi;
			else if (p2 < -kTwoPi) p2 += kTwoPi;
		}
		phase1 = p1;
		phase2 = p2;
	}
};

static void dsf3_(Thread& th, Prim* prim)
{
	Z numharms = th.popFloat("dsf3 : numharms");
	V coef = th.popZIn("dsf3 : coef");
	V modRatio = th.popZIn("dsf3 : modRatio");
	V carRatio = th.popZIn("dsf3 : carRatio");
	V freq = th.popZIn("dsf3 : freq");

	th.push(new List(new DSF3(th, freq, carRatio, modRatio, coef, numharms)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct KlangOsc
{
	KlangOsc(Arg f, Arg a, Z p) :
		freq(f), amp(a), phase(p) {}

	ZIn freq;
	ZIn amp;
	Z phase;
};

struct Klang : public Gen
{
	std::vector<KlangOsc> _oscs;
	Z _freqmul, _K;
	Z _nyq, _cutoff, _slope;

	Klang(Thread& th, V freqs, V amps, V phases)
		: Gen(th, itemTypeZ, false),
			_freqmul(th.rate.radiansPerSample),
			_K(log001 / th.rate.sampleRate),
			_nyq(th.rate.sampleRate * .5),
			_cutoff(_nyq * .8),
			_slope(1. / (_nyq - _cutoff))
	{
		int64_t numOscs = LONG_MAX;
		if (freqs.isVList()) {
			freqs = ((List*)freqs.o())->pack(th);
			numOscs = std::min(numOscs, freqs.length(th));
		}
		if (amps.isVList()) {
			amps = ((List*)amps.o())->pack(th);
			numOscs = std::min(numOscs, amps.length(th));
		}
		if (phases.isList()) {
			phases = ((List*)phases.o())->pack(th);
			numOscs = std::min(numOscs, phases.length(th));
		}

		if (numOscs == LONG_MAX) numOscs = 1;

		for (int64_t i = 0; i < numOscs; ++i) {
			KlangOsc kf(freqs.at(i), amps.at(i), phases.atz(i));
			_oscs.push_back(kf);
		}

	}

	virtual const char* TypeName() const override { return "Klang"; }

	virtual void pull(Thread& th) override
	{
		Z* out0 = mOut->fulfillz(mBlockSize);
		memset(out0, 0, mBlockSize * sizeof(Z));
		int maxToFill = 0;

		Z freqmul = _freqmul;
		Z nyq = _nyq;
		Z cutoff = _cutoff;
		Z slope = _slope;
		for (size_t osc = 0; osc < _oscs.size(); ++osc) {
			int framesToFill = mBlockSize;
			KlangOsc& ko = _oscs[osc];
			Z phase = ko.phase;

			Z* out = out0;
			while (framesToFill) {
				Z *freq, *amp;
				int n, freqStride, ampStride;
				n = framesToFill;
				if (ko.freq(th, n, freqStride, freq) || ko.amp(th, n, ampStride, amp)) {
					setDone();
					maxToFill = std::max(maxToFill, framesToFill);
					break;
				}

				for (int i = 0; i < n; ++i) {
					Z ffreq = *freq;
					if (ffreq > cutoff) {
						if (ffreq < nyq) {
							out[i] +=  (cutoff - ffreq) * slope * *amp * tsin(phase);
						}
					} else {
						out[i] += *amp * tsin(phase);
					}
					phase += ffreq * freqmul;
					if (phase >= kTwoPi) phase -= kTwoPi;
					else if (phase < 0.) phase += kTwoPi;
					freq += freqStride;
					amp += ampStride;
				}
				framesToFill -= n;
				out += n;
				ko.freq.advance(n);
				ko.amp.advance(n);
			}
			ko.phase = phase;
		}
		produce(maxToFill);
	}
};

static void klang_(Thread& th, Prim* prim)
{
	V phases	= th.popZInList("klang : phases");
	V amps		= th.popZInList("klang : amps");
	V freqs     = th.popZInList("klang : freqs");

	if (freqs.isVList() && !freqs.isFinite())
		indefiniteOp("klank : freqs", "");

	if (amps.isVList() && !amps.isFinite())
		indefiniteOp("klank : amps", "");

	if (phases.isVList() && !phases.isFinite())
		indefiniteOp("klank : phases", "");

	th.push(new List(new Klang(th, freqs, amps, phases)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

#define DEF(NAME, TAKES, LEAVES, HELP) 	vm.def(#NAME, TAKES, LEAVES, NAME##_, HELP);
#define DEFMCX(NAME, N, HELP) 	vm.defmcx(#NAME, N, NAME##_, HELP);
#define DEFAM(NAME, MASK, HELP) 	vm.defautomap(#NAME, #MASK, NAME##_, HELP);

void AddOscilUGenOps()
{
	fillHarmonicsTable();

	vm.addBifHelp("\n*** wavetable generation ***");
	DEFAM(wavefill, aak, "(amps phases smooth -> wavetable) generates a set 1/3 octave wavetables for table lookup oscillators. sin(i*theta + phases[i])*amps[i]*pow(cos(pi*i/n), smooth). smoothing reduces Gibb's phenomenon. zero is no smoothing")

	makeClassicWavetables();

	vm.addBifHelp("\n*** oscillator unit generators ***");

	DEFMCX(osc, 3, "(freq phase wavetable --> out) band limited wave table oscillator. wavetable is a table created with wavefill.")
	DEFMCX(oscp, 4, "(freq phase phaseOffset wavetable --> out) band limited wave table oscillator pair with phase offset.")
	DEFMCX(sosc, 2, "(freq1 freq2 wavetable --> out) band limited hard sync wave table oscillator. freq1 is the fundamental. freq2 is the slave oscil frequency.")

	DEFMCX(par, 2, "(freq phase --> out) band limited parabolic wave oscillator.")
	DEFMCX(tri, 2, "(freq phase --> out) band limited triangle wave oscillator.")
	DEFMCX(square, 2, "(freq phase --> out) band limited square wave oscillator.")
	DEFMCX(saw, 2, "(freq phase --> out) band limited sawtooth wave oscillator.")
	DEFMCX(pulse, 3, "(freq phase duty --> out) band limited pulse wave oscillator.")
	DEFMCX(vsaw, 3, "(freq phase duty --> out) band limited variable sawtooth oscillator.")
	DEFMCX(ssaw, 2, "(freq1 freq2 --> out) band limited hard sync sawtooth oscillator. freq1 is the fundamental. freq2 is the slave oscil frequency.")

	DEFMCX(blip, 3, "(freq phase numharms --> out) band limited impulse oscillator.")
	DEFMCX(dsf1, 5, "(freq carrierRatio modulatorRatio ampCoef numharms --> out) bandlimited partials with geometric series amplitudes. J.A.Moorer's equation 1")
	DEFMCX(dsf3, 5, "(freq carrierRatio modulatorRatio ampCoef numharms --> out) two sided bandlimited partials with geometric series amplitudes. J.A.Moorer's equation 3")

	DEFMCX(lftri, 2, "(freq phase --> out) non band limited triangle wave oscillator.")
	DEFMCX(lfsaw, 2, "(freq phase --> out) non band limited sawtooth wave oscillator.")
	DEFMCX(lfpulse, 3, "(freq phase duty --> out) non band limited unipolar pulse wave oscillator.")
	DEFMCX(lfpulseb, 3, "(freq phase duty --> out) non band limited bipolar pulse wave oscillator.")
	DEFMCX(lfsquare, 2, "(freq phase --> out) non band limited square wave oscillator.")
	DEFMCX(impulse, 2, "(freq phase --> out) non band limited single sample impulse train oscillator.")
	DEFMCX(smoothsaw, 3, "(freq phase nth --> out) smoothed sawtooth.")
	DEFMCX(smoothsawpwm, 4, "(freq phase nth duty --> out) smoothed sawtooth.")
	DEFMCX(vosim, 3, "(freq phase nth --> out) vosim sim.")
	DEFMCX(sinosc, 2, "(freq phase --> out) sine wave oscillator.")
	DEFMCX(tsinosc, 2, "(freq iphase --> out) sine wave oscillator.")
	DEFMCX(sinoscfb, 3, "(freq phase feedback --> out) sine wave oscillator with self feedback phase modulation")
	DEFMCX(sinoscm, 4, "(freq phase mul add --> out) sine wave oscillator with multiply and add.")

	DEF(klang, 3, 1, "(freqs amps iphases --> out) a sine oscillator bank. freqs amps and iphases are arrays.")
}

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "UGen.hpp"

#include "VM.hpp"
#include "MultichannelExpansion.hpp"
#include "clz.hpp"
#include <cmath>
#include <float.h>
#include <vector>
#include <algorithm>
#include <Accelerate/Accelerate.h>

struct MulAdd : public ThreeInputUGen<MulAdd>
{
	MulAdd(Thread& th, Arg a, Arg b, Arg c) : ThreeInputUGen<MulAdd>(th, a, b, c)
	{
	}

	virtual const char* TypeName() const override { return "MulAdd"; }

	void calc(int n, Z* out, Z* a, Z* b, Z* c, int aStride, int bStride, int cStride)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = *a * *b + *c;
			a += aStride;
			b += bStride;
			c += cStride;
		}
	}
};

extern UnaryOp* gUnaryOpPtr_neg;
extern BinaryOp* gBinaryOpPtr_plus;
extern BinaryOp* gBinaryOpPtr_minus;
extern BinaryOp* gBinaryOpPtr_mul;

static void madd_(Thread& th, Prim* prim)
{
	V c = th.popZIn("*+ : c");
	V b = th.popZIn("*+ : b");
	V a = th.popZIn("*+ : a");

	if (a.isReal() && b.isReal() && c.isReal()) {
		th.push(a.f * b.f + c.f);
	} else {
		if (c.isReal()) {
			if (c.f == 0.) {
				if (a.isReal() && a.f == 1.) { th.push(b); return; }
				if (b.isReal() && b.f == 1.) { th.push(a); return; }
				if (a.isReal() && a.f == -1.) { th.push(b.unaryOp(th, gUnaryOpPtr_neg)); return; }
				if (b.isReal() && b.f == -1.) { th.push(a.unaryOp(th, gUnaryOpPtr_neg)); return; }
				th.push(a.binaryOp(th, gBinaryOpPtr_mul, b)); return;
			}
		}
		if (a.isReal()) {
			if (a.f == 0.) { th.push(c); return; }
			if (a.f == 1.) { th.push(b.binaryOp(th, gBinaryOpPtr_plus, c)); return; }
			if (a.f == -1.) { th.push(b.binaryOp(th, gBinaryOpPtr_minus, c)); return; }
		}
		if (b.isReal()) {
			if (b.f == 0.) { th.push(c); return; }
			if (b.f == 1.) { th.push(a.binaryOp(th, gBinaryOpPtr_plus, c)); return; }
			if (b.f == -1.) { th.push(a.binaryOp(th, gBinaryOpPtr_minus, c)); return; }
		}
		th.push(new List(new MulAdd(th, a, b, c)));
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Fadeout : Gen
{
	ZIn _a;
	int64_t _sustainTime;
	int64_t _fadeTime;
	Z _amp, _fade;

	Fadeout(Thread& th, Arg a, Z sustainTime, Z fadeTime) : Gen(th, itemTypeZ, true), _a(a)
	{
		_sustainTime = (int64_t)floor(th.rate.sampleRate * sustainTime + .5);
		_fadeTime = (int64_t)floor(th.rate.sampleRate * fadeTime + .5);
		_sustainTime = std::max(1LL, _sustainTime);
		_fadeTime = std::max(1LL, _fadeTime);
		_amp = 1.001;
		_fade = pow(.001, 1. / _fadeTime);
	}
	virtual const char* TypeName() const override { return "Fadeout"; }

	virtual void pull(Thread& th) override {
		if (_sustainTime <= 0) {
			if (_fadeTime <= 0) {
				end();
			} else {
				int framesToFill = (int)std::min(_fadeTime, (int64_t)mBlockSize);
				Z* out = mOut->fulfillz(framesToFill);
				_fadeTime -= framesToFill;
				while (framesToFill) {
					int n = framesToFill;
					int astride;
					Z *a;
					if (_a(th, n,astride, a)) {
					  setDone();
						break;
					} else {
						Z amp = _amp;
						Z fade = _fade;
						for (int i = 0; i < n; ++i) {
							out[i] = *a * (amp - .001);
							amp *= fade;
							a += astride;
						}
						_amp = amp;
						_a.advance(n);
						framesToFill -= n;
						out += n;
					}
				}
			   produce(framesToFill);
			}
		} else {
            int framesToFill = (int)std::min(_sustainTime, (int64_t)mBlockSize);
            Z* out = mOut->fulfillz(framesToFill);
            _sustainTime -= framesToFill;
            while (framesToFill) {
                int n = framesToFill;
                int astride;
                Z *a;
                if (_a(th, n,astride, a)) {
                  setDone();
                    break;
                } else {
                    for (int i = 0; i < n; ++i) {
                        out[i] = *a;
						a += astride;
                    }
                    _a.advance(n);
                    framesToFill -= n;
                    out += n;
                }
            }
           produce(framesToFill);
        }
	}
};

struct Fadein : Gen
{
	ZIn _a;
	int64_t _fadeTime;
	Z _amp, _fade;

	Fadein(Thread& th, Arg a, Z fadeTime) : Gen(th, itemTypeZ, true), _a(a)
	{
		_fadeTime = (int64_t)floor(th.rate.sampleRate * fadeTime + .5);
		_fadeTime = std::max(1LL, _fadeTime);
		_amp = .001;
		_fade = pow(1000., 1. / _fadeTime);
	}
	virtual const char* TypeName() const override { return "Fadein"; }

	virtual void pull(Thread& th) override {
		if (_fadeTime <= 0) {
			_a.link(th, mOut);
			setDone();
		} else {
			int framesToFill = (int)std::min(_fadeTime, (int64_t)mBlockSize);
			Z* out = mOut->fulfillz(framesToFill);
			_fadeTime -= framesToFill;
			while (framesToFill) {
				int n = framesToFill;
				int astride;
				Z *a;
				if (_a(th, n,astride, a)) {
				  setDone();
					break;
				} else {
					Z amp = _amp;
					Z fade = _fade;
					for (int i = 0; i < n; ++i) {
						out[i] = *a * (amp - .001);
						amp *= fade;
						a += astride;
					}
					_amp = amp;
					_a.advance(n);
					framesToFill -= n;
					out += n;
				}
			}
		   produce(framesToFill);
		}
	}
};

static void fadeout_(Thread& th, Prim* prim)
{
	Z fade = th.popFloat("fadeout : fadeTime");
	Z sustain = th.popFloat("fadeout : sustainTime");
	V in = th.popZIn("fadeout : in");

	th.push(new List(new Fadeout(th, in, sustain, fade)));
}

static void fadein_(Thread& th, Prim* prim)
{
	Z fade = th.popFloat("fadein : fadeTime");
	V in = th.popZIn("fadein : in");

	th.push(new List(new Fadein(th, in, fade)));
}

struct Endfade : Gen
{
	ZIn _a;
	int64_t _startupTime;
	int64_t _holdTime;
	int64_t _holdTimeRemaining;
	int64_t _fadeTime;
	Z _amp, _fade, _threshold;

	Endfade(Thread& th, Arg a, Z startupTime, Z holdTime, Z fadeTime, Z threshold) : Gen(th, itemTypeZ, true), _a(a)
	{
		_startupTime = (int64_t)floor(th.rate.sampleRate * startupTime + .5);
		_holdTime = (int64_t)floor(th.rate.sampleRate * holdTime + .5);
		_fadeTime = (int64_t)floor(th.rate.sampleRate * fadeTime + .5);
		_startupTime = std::max(0LL, _startupTime);
		_holdTime = std::max(1LL, _holdTime);
		_holdTimeRemaining = _holdTime;
		_fadeTime = std::max(1LL, _fadeTime);
		_threshold = threshold;
		_amp = 1.001;
		_fade = pow(.001, 1. / _fadeTime);
	}
	virtual const char* TypeName() const override { return "Endfade"; }

	virtual void pull(Thread& th) override {
		Z* out = mOut->fulfillz(mBlockSize);
		int framesToFill = mBlockSize;

		while (framesToFill) {
			if (_startupTime > 0) {
				int n = (int)std::min((int64_t)framesToFill, _startupTime);
				int astride;
				Z *a;
				if (_a(th, n, astride, a)) {
					setDone();
					break;
				} else {
					for (int i = 0; i < n; ++i) {
						out[i] = *a;
						a += astride;
					}
					_a.advance(n);
					framesToFill -= n;
					out += n;
					_startupTime -= n;
				}
			} else if (_holdTimeRemaining > 0) {
 				int n = framesToFill;
				int astride;
				Z *a;
				if (_a(th, n, astride, a)) {
					setDone();
					break;
				} else {
					int framesFilled = 0;
					for (int i = 0; i < n && _holdTimeRemaining > 0; ++i) {
						Z z = *a;
						if (std::abs(z) >= _threshold) {
							_holdTimeRemaining = _holdTime;
						} else {
							--_holdTimeRemaining;
						}
						out[i] = z;
						a += astride;
						++framesFilled;
					}
					_a.advance(framesFilled);
					framesToFill -= framesFilled;
					out += framesFilled;
				}
			} else if (_fadeTime > 0) {
				int n = (int)std::min((int64_t)framesToFill, _fadeTime);
				int astride;
				Z *a;
				if (_a(th, n,astride, a)) {
					setDone();
					break;
				} else {
					Z amp = _amp;
					Z fade = _fade;
					for (int i = 0; i < n; ++i) {
						out[i] = *a * (amp - .001);
						amp *= fade;
						a += astride;
					}
					_amp = amp;
					_a.advance(n);
					framesToFill -= n;
					out += n;
					_fadeTime -= n;
				}
			} else {
				setDone();
				break;
			}
		}
		produce(framesToFill);
	}
};

static void endfade_(Thread& th, Prim* prim)
{
	Z threshold = th.popFloat("endfade : threshold");
	Z fade = th.popFloat("endfade : fadeTime");
	Z hold = th.popFloat("endfade : holdTime");
	Z startup = th.popFloat("endfade : startupTime");
	V in = th.popZIn("endfade : in");

	th.push(new List(new Endfade(th, in, startup, hold, fade, threshold)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Imps : public Gen
{
	BothIn durs_;
	BothIn vals_;
	ZIn rate_;
	Z val_;
	Z phase_, dur_, invdur_;
	Z freqmul_;
	bool once;

	Imps(Thread& th, Arg durs, Arg vals, Arg rate) : Gen(th, itemTypeZ, mostFinite(vals, durs, rate)), durs_(durs), vals_(vals), rate_(rate),
		phase_(0.), dur_(0.), freqmul_(th.rate.invSampleRate), once(false)
	{
	}

	virtual const char* TypeName() const override { return "Imps"; }

	virtual void pull(Thread& th) override
	{
		Z* out = mOut->fulfillz(mBlockSize);
		int framesToFill = mBlockSize;

		while (framesToFill) {
			Z* rate;
			int n = framesToFill;
			int rateStride;
			if (rate_(th, n, rateStride, rate)) {
				setDone();
				break;
			}
			for (int i = 0; i < n; ++i) {
				while (phase_ >= dur_) {
					phase_ -= dur_;
					do {
						if (vals_.onez(th, val_) || durs_.onez(th, dur_)) {
							setDone();
							goto leave;
						}
					} while (dur_ <= 0.);
					once = true;
				}

				if (once) {
					out[i] = val_;
					once = false;
				} else {
					out[i] = 0.;
				}
				phase_ += *rate * freqmul_;
				rate += rateStride;
				--framesToFill;
			}
			out += n;

		}
leave:
		produce(framesToFill);
	}
};

struct Steps : public Gen
{
	BothIn durs_;
	BothIn vals_;
	ZIn rate_;
	Z val_;
	Z phase_, dur_;
	Z freqmul_;

	Steps(Thread& th, Arg durs, Arg vals, Arg rate) : Gen(th, itemTypeZ, mostFinite(vals, durs, rate)), durs_(durs), vals_(vals), rate_(rate),
		phase_(0.), dur_(0.), freqmul_(th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "Steps"; }

	virtual void pull(Thread& th) override
	{
		Z* out = mOut->fulfillz(mBlockSize);
		int framesToFill = mBlockSize;
		while (framesToFill) {
			Z* rate;
			int n = framesToFill;
			int rateStride;
			if (rate_(th, n, rateStride, rate)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				while (phase_ >= dur_) {
					phase_ -= dur_;
					do {
						if (vals_.onez(th, val_) || durs_.onez(th, dur_)) {
							setDone();
							goto leave;
						}
					} while (dur_ <= 0.);
				}

				out[i] = val_;

				phase_ += *rate * freqmul_;
				rate += rateStride;
				--framesToFill;
			}
			out += n;
			rate_.advance(n);
		}
leave:
		produce(framesToFill);
	}
};

struct Gates : public Gen
{
	BothIn durs_;
	BothIn vals_;
	BothIn hold_;
	ZIn rate_;
	Z val_;
	Z phase_, dur_, hdur_;
	Z freqmul_;

	Gates(Thread& th, Arg durs, Arg vals, Arg hold, Arg rate) : Gen(th, itemTypeZ, mostFinite(vals, durs, rate, hold)), durs_(durs), vals_(vals), hold_(hold), rate_(rate),
		phase_(0.), dur_(0.), freqmul_(th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "Gates"; }

	virtual void pull(Thread& th) override
	{
		Z* out = mOut->fulfillz(mBlockSize);
		int framesToFill = mBlockSize;
		while (framesToFill) {
			Z* rate;
			int n = framesToFill;
			int rateStride;
			if (rate_(th, n, rateStride, rate)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				while (phase_ >= dur_) {
					phase_ -= dur_;
					do {
						if (vals_.onez(th, val_) || durs_.onez(th, dur_) || hold_.onez(th, hdur_)) {
							setDone();
							goto leave;
						}
					} while (dur_ <= 0.);
				}

				out[i] = phase_ < hdur_ ? val_ : 0.;

				phase_ += *rate * freqmul_;
				rate += rateStride;
				--framesToFill;
			}
			out += n;
			rate_.advance(n);
		}
leave:
		produce(framesToFill);
	}
};

struct Lines : public Gen
{
	BothIn durs_;
	BothIn vals_;
	ZIn rate_;
	Z oldval_, newval_, slope_;
	Z phase_, dur_;
	Z freqmul_;
	bool once = true;

	Lines(Thread& th, Arg durs, Arg vals, Arg rate) : Gen(th, itemTypeZ, mostFinite(vals, durs, rate)), durs_(durs), vals_(vals), rate_(rate),
		phase_(0.), dur_(0.), freqmul_(th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "Lines"; }

	virtual void pull(Thread& th) override
	{
		if (once) {
			once = false;
			vals_.onez(th, newval_);
		}
		Z* out = mOut->fulfillz(mBlockSize);
		int framesToFill = mBlockSize;
		while (framesToFill) {
			Z* rate;
			int n = framesToFill;
			int rateStride;
			if (rate_(th, n, rateStride, rate)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				while (phase_ >= dur_) {
					phase_ -= dur_;
					do {
						oldval_ = newval_;
						if (vals_.onez(th, newval_) || durs_.onez(th, dur_)) {
							setDone();
							goto leave;
						}
					} while (dur_ <= 0.);
					slope_ = (newval_ - oldval_) / dur_;
				}

				out[i] = oldval_ + slope_ * phase_;

				phase_ += *rate * freqmul_;
				rate += rateStride;
				--framesToFill;
			}
			out += n;
			rate_.advance(n);
		}
leave:
		produce(framesToFill);
	}
};

struct XLines : public Gen
{
	BothIn durs_;
	BothIn vals_;
	ZIn rate_;
	Z oldval_, newval_, ratio_, step_;
	Z phase_, dur_, invdur_, freq_;
	Z freqmul_;
	bool once = true;
	//bool cheat;

	XLines(Thread& th, Arg durs, Arg vals, Arg rate) : Gen(th, itemTypeZ, mostFinite(vals, durs, rate)), durs_(durs), vals_(vals), rate_(rate),
		phase_(0.), dur_(0.), freqmul_(th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "XLines"; }

	virtual void pull(Thread& th) override
	{
		if (once) {
			once = false;
			vals_.onez(th, newval_);
		}
		Z* out = mOut->fulfillz(mBlockSize);
		int framesToFill = mBlockSize;
		while (framesToFill) {
			Z* rate;
			int n = framesToFill;
			int rateStride;
			if (rate_(th, n, rateStride, rate)) {
				setDone();
				break;
			}

			if (rateStride == 0) {
				for (int i = 0; i < n; ++i) {
					while (phase_ >= dur_) {
						phase_ -= dur_;
						do {
							oldval_ = newval_;
							if (vals_.onez(th, newval_) || durs_.onez(th, dur_)) {
								setDone();
								goto leave;
							}
						} while (dur_ <= 0.);
						invdur_ = 1. / dur_;
						ratio_ = newval_ / oldval_;
						//oldval_ = oldval_ * pow(ratio_, phase_ * invdur_);
						freq_ = *rate * freqmul_;
						step_ = pow(ratio_, freq_ * invdur_);
					}

					out[i] = oldval_;
					oldval_ *= step_;

					phase_ += freq_;
					--framesToFill;
				}
			} else {
				for (int i = 0; i < n; ++i) {
					while (phase_ >= dur_) {
						phase_ -= dur_;
						do {
							oldval_ = newval_;
							if (vals_.onez(th, newval_) || durs_.onez(th, dur_)) {
								setDone();
								goto leave;
							}
						} while (dur_ <= 0.);
						invdur_ = 1. / dur_;
						ratio_ = newval_ / oldval_;
					}

					out[i] = oldval_ * pow(ratio_, phase_ * invdur_);

					phase_ += *rate * freqmul_;
					rate += rateStride;
					--framesToFill;
				}
			}
			out += n;
			rate_.advance(n);
		}
leave:
		produce(framesToFill);
	}
};

struct Curves : public Gen
{
	BothIn durs_;
	BothIn vals_;
	BothIn curves_;
	ZIn rate_;
	Z oldval_, newval_, step_;
	Z phase_, dur_, curve_, invdur_, freq_;
	Z b1_, a2_;
	Z freqmul_;
	bool once = true;
	//bool cheat;

	Curves(Thread& th, Arg durs, Arg vals, Arg curves, Arg rate) : Gen(th, itemTypeZ, mostFinite(vals, durs, rate, curves)), durs_(durs), vals_(vals), curves_(curves), rate_(rate),
		phase_(0.), dur_(0.), freqmul_(th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "Curves"; }

	virtual void pull(Thread& th) override
	{
		if (once) {
			once = false;
			vals_.onez(th, newval_);
		}
		Z* out = mOut->fulfillz(mBlockSize);
		int framesToFill = mBlockSize;
		while (framesToFill) {
			Z* rate;
			int n = framesToFill;
			int rateStride;
			if (rate_(th, n, rateStride, rate)) {
				setDone();
				break;
			}

			if (rateStride == 0) {
				for (int i = 0; i < n; ++i) {
					while (phase_ >= dur_) {
						phase_ -= dur_;
						do {
							oldval_ = newval_;
							if (vals_.onez(th, newval_) || curves_.onez(th, curve_) || durs_.onez(th, dur_)) {
								setDone();
								goto leave;
							}
						} while (dur_ <= 0.);
						dur_ = std::max(dur_, 1e-4);
						invdur_ = 1. / dur_;
						Z a1 = (newval_ - oldval_) / (1. - exp(curve_));
						a2_ = oldval_ + a1;
						b1_ = a1;
						freq_ = *rate * freqmul_;
						step_ = exp(curve_ * freq_ * invdur_);
					}

					out[i] = oldval_;
					b1_ *= step_;
					oldval_ = a2_ - b1_;

					phase_ += freq_;
					--framesToFill;
				}
			} else {
                //!! not correct
				for (int i = 0; i < n; ++i) {
					while (phase_ >= dur_) {
						phase_ -= dur_;
						do {
							oldval_ = newval_;
							if (vals_.onez(th, newval_) || curves_.onez(th, curve_) || durs_.onez(th, dur_)) {
								setDone();
								goto leave;
							}
						} while (dur_ <= 0.);
						invdur_ = 1. / dur_;
						Z a1 = (newval_ - oldval_) / (1. - exp(curve_));
						a2_ = oldval_ + a1;
						b1_ = a1;
						freq_ = freqmul_;
						step_ = exp(curve_ * freq_ * invdur_);
					}

					out[i] = oldval_;
					b1_ *= step_;
					oldval_ = a2_ - b1_;

					phase_ += *rate * freqmul_;
					--framesToFill;
				}
			}
			out += n;
			rate_.advance(n);
		}
leave:
		produce(framesToFill);
	}
};

struct Cubics : public Gen
{
	BothIn vals_;
	ZIn rate_;
	Z y0, y1, y2, y3;
	Z c0, c1, c2, c3;
	Z phase_;
	Z freqmul_;
	bool once = true;

	Cubics(Thread& th, Arg vals, Arg rate) : Gen(th, itemTypeZ, mostFinite(vals, rate)), vals_(vals), rate_(rate),
		phase_(1.), freqmul_(th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "Cubics"; }

	virtual void pull(Thread& th) override
	{
		if (once) {
			once = false;
			y0 = 0.;
			y1 = 0.;
			vals_.onez(th, y2);
			vals_.onez(th, y3);
		}
		Z* out = mOut->fulfillz(mBlockSize);
		int framesToFill = mBlockSize;
		Z x = phase_;
		Z freqmul = freqmul_;
		while (framesToFill) {
			Z* rate;
			int n = framesToFill;
			int rateStride;
			if (rate_(th, n, rateStride, rate)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				while (x >= 1.) {
					x -= 1.;
					y0 = y1;
					y1 = y2;
					y2 = y3;
					if (vals_.onez(th, y3)) {
						setDone();
						goto leave;
					} else {
						c0 = y1;
						c1 = .5 * (y2 - y0);
						c2 = y0 - 2.5 * y1 + 2. * y2 - .5 * y3;
						c3 = 1.5 * (y1 - y2) + .5 * (y3 - y0);
					}
				}

				out[i] = ((c3 * x + c2) * x + c1) * x + c0;

				x += *rate * freqmul;
				rate += rateStride;
				--framesToFill;
			}
			out += n;
			rate_.advance(n);
		}
leave:
		produce(framesToFill);
		phase_ = x;
	}
};

/*
d = duration of accelerando in beats
a = duration of accelerando in seconds

r0 = starting tempo in beats per second
r1 = ending tempo in beats per second

c = (r1 - r0) / d

duration of accelerando in seconds
a = log(r1/r0) / c

tempo for next sample
r(0) = r0
r(n) = r(n-1) * exp(c / sr)

beat for next sample
b(n) = r(n)/c - r0/c

b(0) = 0
b(n) = b(n-1) + r(n)/sr

*/

struct Tempo : public Gen
{
	BothIn vals_;
	ZIn rate_;
	Z beat_ = 0.;
	Z dur_ = 0.;
	Z lastTime_ = 0.;
	Z nextTime_ = 0.;
	Z invsr_;
	Z c_, r0_, r1_;
	bool once = true;

	Tempo(Thread& th, Arg vals, Arg rate) : Gen(th, itemTypeZ, mostFinite(vals, rate)), vals_(vals), rate_(rate),
		invsr_(th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "Tempo"; }

	virtual void pull(Thread& th) override
	{
		if (once) {
			once = false;
			if (vals_.onez(th, r1_)) {
				setDone();
				return;
			}
		}
		Z* out = mOut->fulfillz(mBlockSize);
		int framesToFill = mBlockSize;
		while (framesToFill) {
			Z* rate;
			int n = framesToFill;
			int rateStride;
			if (rate_(th, n, rateStride, rate)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				// numerically it would be better to subtract off dur each time, but we need to recreate the same loss of precision over time
				// as will be experienced from an integration of tempo occuring outside of this generator. otherwise there would be a drift
				// between when tempo changes occur and the beat time as integrated from the tempo.
				while (beat_ >= nextTime_) {
					do {
						r0_ = r1_;
						if (vals_.onez(th, dur_)) {
							setDone();
							goto leave;
						}
						if (vals_.onez(th, r1_)) {
							setDone();
							goto leave;
						}
					} while (dur_ <= 0.);
					c_ = (r1_ - r0_) / dur_;
					lastTime_ = nextTime_;
					nextTime_ += dur_;
				}

				Z tempo = *rate * (r0_ + (beat_ - lastTime_) * c_);
				out[i] = tempo;
				beat_ += tempo * invsr_;

				rate += rateStride;
				--framesToFill;
			}

			out += n;
			rate_.advance(n);
		}
leave:
		produce(framesToFill);
	}
};

struct Beats : public Gen
{
	ZIn tempo_;
	Z beat_ = 0.;
	Z invsr_;

	Beats(Thread& th, Arg tempo) : Gen(th, itemTypeZ, tempo.isFinite()), tempo_(tempo),
		invsr_(th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "Beats"; }

	virtual void pull(Thread& th) override
	{

		Z* out = mOut->fulfillz(mBlockSize);
		int framesToFill = mBlockSize;
		Z beat = beat_;
		while (framesToFill) {
			Z* tempo;
			int n = framesToFill;
			int tempoStride;
			if (tempo_(th, n, tempoStride, tempo)) {
				setDone();
				break;
			}

			Z invsr = invsr_;
			for (int i = 0; i < n; ++i) {
				out[i] = beat;
				beat += *tempo * invsr;
				tempo += tempoStride;
			}
			framesToFill -= n;
			out += n;
			tempo_.advance(n);
		}
		beat_ = beat;
		produce(framesToFill);
	}
};

template <int NumStages>
struct ADSR : public Gen
{
	Z levels_[NumStages+1];
	Z durs_[NumStages];
	Z curves_[NumStages];
	ZIn rate_;
	int stage_ = 0;
	Z oldval_ = 0.;
	Z newval_ = 0.;
	Z step_;
	Z phase_ = 0.;
	Z dur_ = 0.;
	Z noteOff_;
	Z curve_;
	Z b1_, a2_;
	Z freqmul_;
	Z beat_ = 0.;
	bool once = true;
	int sustainStage_;

	ADSR(Thread& th, Z* levels, Z* durs, Z* curves, Arg rate, int sustainStage) : Gen(th, itemTypeZ, true),
		rate_(rate), sustainStage_(sustainStage),
		freqmul_(th.rate.invSampleRate)
	{
		memcpy(levels_, levels, (NumStages+1)*sizeof(Z));
		memcpy(durs_,   durs,   NumStages*sizeof(Z));
		memcpy(curves_, curves, NumStages*sizeof(Z));
		noteOff_ = durs_[sustainStage_];

		oldval_ = levels[0];
		newval_ = levels_[1];
		curve_ = curves_[0];
		dur_ = durs_[0];

		calcStep();
	}

	virtual const char* TypeName() const override { return "ADSR"; }

	void calcStep()
	{
		if (fabs(curve_) < .01) {
			a2_ = oldval_;
			b1_ = 0.;
			step_ = 1.;
		} else {

			dur_ = std::max(dur_, 1e-5);
			Z invdur = 1. / dur_;
			Z a1 = (newval_ - oldval_) / (1. - exp(curve_));
			a2_ = oldval_ + a1;
			b1_ = a1;
			step_ = exp(curve_ * freqmul_ * invdur);
		}
	}

	virtual void pull(Thread& th) override
	{
		Z* out = mOut->fulfillz(mBlockSize);
		int framesToFill = mBlockSize;
		while (framesToFill) {
			Z* rate;
			int n = framesToFill;
			int rateStride;
			if (rate_(th, n, rateStride, rate)) {
				setDone();
				break;
			}

			// rework this loop!!!
			//adsr
			//	if (stage < releaseStage) {
			//		integrate tempo
			//		search from end. break when < noteoff.
			//		remember note off sample index.
			//	}
			//
			//	when there is a new stage and it is not the sustainStage
			//	compute the number of samples in the stage
			//
			//	limit n to the min(blockSize, stageSamplesRemaining, [noteOffSample])
			//	after a stage

			for (int i = 0; i < n; ++i) {
				while (1) {
					if (stage_ < sustainStage_) {
						if (phase_ >= dur_) {
							phase_ -= dur_;
							++stage_;
						} else if (beat_ >= noteOff_) {
							phase_ = 0.;
							stage_ = sustainStage_+1; // go into release mode
						} else break;
					} else if (stage_ == sustainStage_) {
						if (beat_ >= noteOff_) {
							phase_ = 0.;
							stage_ = sustainStage_+1;
						} else break;
					} else if (stage_ < NumStages){
						if (phase_ >= dur_) {
							phase_ -= dur_;
							++stage_;
						} else break;
					} else {
						setDone();
						goto leave;
					}

					newval_ = levels_[stage_+1];
					curve_ = curves_[stage_];
					dur_ = durs_[stage_];

					calcStep();
				}

				out[i] = oldval_;
				b1_ *= step_;
				oldval_ = a2_ - b1_;

				beat_ += *rate * freqmul_;
				rate += rateStride;
				phase_ += freqmul_;
				--framesToFill;
			}
			out += n;
			rate_.advance(n);
		}
leave:
		produce(framesToFill);
	}
};

template <int NumStages>
struct GatedADSR : public Gen
{
	Z levels_[NumStages+1];
	Z durs_[NumStages];
	Z curves_[NumStages];
	ZIn gate_;
	int stage_ = NumStages;
	Z oldval_ = 0.;
	Z newval_ = 0.;
	Z step_;
	Z phase_ = 0.;
	Z dur_ = 0.;
	Z curve_;
	Z b1_, a2_;
	Z freqmul_;
	bool once = true;
	int sustainStage_;

	GatedADSR(Thread& th, Z* levels, Z* durs, Z* curves, Arg gate, int sustainStage) : Gen(th, itemTypeZ, true),
		gate_(gate), sustainStage_(sustainStage),
		freqmul_(th.rate.invSampleRate)
	{
		memcpy(levels_, levels, (NumStages+1)*sizeof(Z));
		memcpy(durs_,   durs,   NumStages*sizeof(Z));
		memcpy(curves_, curves, NumStages*sizeof(Z));

		oldval_ = levels[0];
		newval_ = levels_[1];
		curve_ = curves_[0];
		dur_ = durs_[0];

		calcStep();
	}

	virtual const char* TypeName() const override { return "GatedADSR"; }

	void calcStep()
	{
		if (fabs(curve_) < .01) {
			a2_ = oldval_;
			b1_ = 0.;
			step_ = 1.;
		} else {

			dur_ = std::max(dur_, 1e-5);
			Z invdur = 1. / dur_;
			Z a1 = (newval_ - oldval_) / (1. - exp(curve_));
			a2_ = oldval_ + a1;
			b1_ = a1;
			step_ = exp(curve_ * freqmul_ * invdur);
		}
	}

	virtual void pull(Thread& th) override
	{
		Z* out = mOut->fulfillz(mBlockSize);
		int framesToFill = mBlockSize;
		while (framesToFill) {
			Z* gate;
			int n = framesToFill;
			int gateStride;
			if (gate_(th, n, gateStride, gate)) {
				setDone();
				break;
			}

			// rework this loop!!!
			//adsr
			//	if (stage < releaseStage) {
			//		integrate tempo
			//		search from end. break when < noteoff.
			//		remember note off sample index.
			//	}
			//
			//	when there is a new stage and it is not the sustainStage
			//	compute the number of samples in the stage
			//
			//	limit n to the min(blockSize, stageSamplesRemaining, [noteOffSample])
			//	after a stage

			for (int i = 0; i < n; ++i) {
                Z g = *gate;
                gate += gateStride;
                if (stage_ >= NumStages) {
                    // waiting for trigger
                    if (*gate > 0.) {
                        stage_ = 0;
                    } else {
                        out[i] = 0.;
                        --framesToFill;
                       continue;
                    }
                }
				while (1) {
                    if (stage_ < sustainStage_) {
						if (g <= 0.) {
							phase_ = 0.;
							stage_ = sustainStage_+1; // go into release mode
						} else if (phase_ >= dur_) {
							phase_ -= dur_;
							++stage_;
						} else break;
					} else if (stage_ == sustainStage_) {
						if (g <= 0.) {
							phase_ = 0.;
							stage_ = sustainStage_+1;
						} else break;
					} else {
						if (phase_ >= dur_) {
							phase_ -= dur_;
							++stage_;
						} else break;
					}

					newval_ = levels_[stage_+1];
					curve_ = curves_[stage_];
					dur_ = durs_[stage_];

					calcStep();
				}

				out[i] = oldval_;
				b1_ *= step_;
				oldval_ = a2_ - b1_;

				phase_ += freqmul_;
				--framesToFill;
			}
			out += n;
			gate_.advance(n);
		}
		produce(framesToFill);
	}
};

static void imps_(Thread& th, Prim* prim)
{
	V rate = th.popZIn("imps : rate");
	V durs = th.pop();
	V vals = th.pop();

	th.push(new List(new Imps(th, durs, vals, rate)));
}

static void steps_(Thread& th, Prim* prim)
{
	V rate = th.popZIn("steps : rate");
	V durs = th.pop();
	V vals = th.pop();

	th.push(new List(new Steps(th, durs, vals, rate)));
}

static void gates_(Thread& th, Prim* prim)
{
	V rate = th.popZIn("gates : rate");
	V hold = th.pop();
	V durs = th.pop();
	V vals = th.pop();

	th.push(new List(new Gates(th, durs, vals, hold, rate)));
}

static void lines_(Thread& th, Prim* prim)
{
	V rate = th.popZIn("lines : rate");
	V durs = th.pop();
	V vals = th.pop();

	th.push(new List(new Lines(th, durs, vals, rate)));
}

static void xlines_(Thread& th, Prim* prim)
{
	V rate = th.popZIn("xlines : rate");
	V durs = th.pop();
	V vals = th.pop();

	th.push(new List(new XLines(th, durs, vals, rate)));
}

static void cubics_(Thread& th, Prim* prim)
{
	V rate = th.popZIn("cubics : rate");
	V vals = th.pop();

	th.push(new List(new Cubics(th, vals, rate)));
}

static void curves_(Thread& th, Prim* prim)
{
	V rate = th.popZIn("curves : rate");
	V durs = th.pop();
	V param = th.pop();
	V vals = th.pop();

	th.push(new List(new Curves(th, durs, vals, param, rate)));
}

static void tempo_(Thread& th, Prim* prim)
{
	V rate = th.popZIn("tempo : rate");
	V vals = th.pop();

	th.push(new List(new Tempo(th, vals, rate)));
}

static void beats_(Thread& th, Prim* prim)
{
	V tempo = th.popZIn("beats : tempo");

	th.push(new List(new Beats(th, tempo)));
}

static void adsr_(Thread& th, Prim* prim)
{

	V rate    = th.popZIn("adsr :  tempo");
	Z noteDur = th.popFloat("adsr : noteDur");
	Z amp     = th.popFloat("adsr : amp");

	P<List> list   = th.popList("adsr : [attack decay sustain release]");
	const int kNumADSRStages = 4;
	Z env[kNumADSRStages];
	if (list->fillz(th, kNumADSRStages, env) != kNumADSRStages) {
		post("adsr : [attack decay sustain release] list should have 4 elements.");
	}

	Z relTime = env[3];
	Z susLvl  = env[2];
	Z dcyTime = env[1];
	Z atkTime = env[0];

	Z levels[kNumADSRStages+1] = { 0., amp, amp*susLvl, amp*susLvl, 0. };
	Z durs[kNumADSRStages] = { atkTime, dcyTime, noteDur, relTime };
	Z curves[kNumADSRStages] = { -1., -5., 0., -5. };

	th.push(new List(new ADSR<kNumADSRStages>(th, levels, durs, curves, rate, 2)));
}

static void dadsr_(Thread& th, Prim* prim)
{

	V rate    = th.popZIn("dadsr :  tempo");
	Z noteDur = th.popFloat("dadsr : noteDur");
	Z amp     = th.popFloat("dadsr : amp");

	P<List> list   = th.popList("dadsr : [delay attack decay sustain release]");

	const int kNumADSRStages = 5;
	Z env[kNumADSRStages];
	if (list->fillz(th, kNumADSRStages, env) != kNumADSRStages) {
		post("dahdsr : [delay attack decay sustain release] list should have 5 elements.");
	}

	Z relTime = env[4];
	Z susLvl  = env[3];
	Z dcyTime = env[2];
	Z atkTime = env[1];
	Z dlyTime = env[0];

	Z levels[kNumADSRStages+1] = { 0., 0., amp, amp*susLvl, amp*susLvl, 0. };
	Z durs[kNumADSRStages] = { dlyTime, atkTime, dcyTime, noteDur, relTime };
	Z curves[kNumADSRStages] = { 0., -1., -5., 0., -5. };

	th.push(new List(new ADSR<kNumADSRStages>(th, levels, durs, curves, rate, 3)));
}

static void dahdsr_(Thread& th, Prim* prim)
{

	V rate    = th.popZIn("dahdsr :  tempo");
	Z noteDur = th.popFloat("dahdsr : noteDur");
	Z amp     = th.popFloat("dahdsr : amp");

	P<List> list   = th.popList("dahdsr : [delay attack hold decay sustain release]");

	const int kNumADSRStages = 6;
	Z env[kNumADSRStages];
	if (list->fillz(th, kNumADSRStages, env) != kNumADSRStages) {
		post("dahdsr : [delay attack hold decay sustain release] list should have 6 elements.");
	}

	Z relTime = env[5];
	Z susLvl  = env[4];
	Z dcyTime = env[3];
	Z hldTime = env[2];
	Z atkTime = env[1];
	Z dlyTime = env[0];

	Z levels[kNumADSRStages+1] = { 0., 0., amp, amp, amp*susLvl, amp*susLvl, 0. };
	Z durs[kNumADSRStages] = { dlyTime, atkTime, hldTime, dcyTime, noteDur, relTime };
	Z curves[kNumADSRStages] = { 0., -1., 0., -5., 0., -5. };

	th.push(new List(new ADSR<kNumADSRStages>(th, levels, durs, curves, rate, 4)));
}

struct K2A : public Gen
{
	int n_;
	int remain_;
	Z slopeFactor_;
	BothIn vals_;
	Z oldval_, newval_, slope_;
	bool once = true;

	K2A(Thread& th, int n, Arg vals) : Gen(th, itemTypeZ, vals.isFinite()), vals_(vals),
		n_(n), remain_(0), slopeFactor_(1./n)
	{
	}

	virtual const char* TypeName() const override { return "K2A"; }

	virtual void pull(Thread& th) override
	{
		if (once) {
			once = false;
			vals_.onez(th, oldval_);
		}
		Z* out = mOut->fulfillz(mBlockSize);
		int framesToFill = mBlockSize;

		while (framesToFill) {
			if (remain_ == 0) {
				if (vals_.onez(th, newval_) ) {
					setDone();
					goto leave;
				}
				slope_ = slopeFactor_ * (newval_ - oldval_);
				remain_ = n_;
			}
			int n = std::min(remain_, framesToFill);
			for (int i = 0; i < n; ++i) {
				out[i] = oldval_;
				oldval_ += slope_;
			}
			framesToFill -= n;
			remain_ -= n;
			out += n;
		}
leave:
		produce(framesToFill);
	}
};

struct K2AC : public Gen
{
	BothIn vals_;
	int n_;
	int remain_;
	Z y0, y1, y2, y3;
	Z c0, c1, c2, c3;
	Z phase_;
	Z slope_;
	bool once = true;

	K2AC(Thread& th, int n, Arg vals)
		: Gen(th, itemTypeZ, vals.isFinite()), vals_(vals), n_(n), remain_(0),
		phase_(0), slope_(1./n)
	{
	}

	virtual const char* TypeName() const override { return "K2AC"; }

	virtual void pull(Thread& th) override
	{
		if (once) {
			once = false;
			y0 = 0.;
			y1 = 0.;
			vals_.onez(th, y2);
			vals_.onez(th, y3);
		}
		Z* out = mOut->fulfillz(mBlockSize);
		int framesToFill = mBlockSize;
		Z x = phase_;

		while (framesToFill) {
			if (remain_ == 0) {
				x = 0.;
				y0 = y1;
				y1 = y2;
				y2 = y3;
				if (vals_.onez(th, y3)) {
					setDone();
					goto leave;
				}
				c0 = y1;
				c1 = .5 * (y2 - y0);
				c2 = y0 - 2.5 * y1 + 2. * y2 - .5 * y3;
				c3 = 1.5 * (y1 - y2) + .5 * (y3 - y0);
				remain_ = n_;
			}
			int n = std::min(remain_, framesToFill);
			for (int i = 0; i < n; ++i) {
				out[i] = ((c3 * x + c2) * x + c1) * x + c0;
				x += slope_;
			}
			framesToFill -= n;
			remain_ -= n;
			out += n;
		}
leave:
		produce(framesToFill);
		phase_ = x;
	}
};

static void k2a_(Thread& th, Prim* prim)
{
	int n = (int)th.popInt("kr : n");
	V a = th.popZIn("kr : signal");

	th.push(new List(new K2A(th, n, a)));
}

static void k2ac_(Thread& th, Prim* prim)
{
	int n = (int)th.popInt("krc : n");
	V a = th.popZIn("krc : signal");

	th.push(new List(new K2AC(th, n, a)));
}

P<Prim> gK2A;
P<Prim> gK2AC;

static void kr_(Thread& th, Prim* prim)
{
	int n = (int)th.popInt("kr : n");
	V fun = th.pop();

	if (n <= 0) {
		post("krc : n <= 0\n");
		throw errOutOfRange;
	}
	if (n > th.rate.blockSize) {
		post("krc : n > block size\n");
		throw errOutOfRange;
	}
	if (th.rate.blockSize % n != 0) {
		post("kr : %d is not a divisor of the current signal block size %d\n", n, th.rate.blockSize);
		throw errFailed;
	}

	V result;
	{
		SaveStack ss(th);
		Rate subRate(th.rate, n);
		{
			UseRate ur(th, subRate);
			fun.apply(th);
		}
		result = th.pop();
		{
			SaveStack ss2(th);
			th.push(result);
			th.push(n);
			gK2A->apply_n(th, 2);
			result = th.pop();
		}
	}
	th.push(result);
}

static void krc_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("kr : n");
	V fun = th.pop();

	if (n <= 0) {
		post("krc : n <= 0\n");
		throw errOutOfRange;
	}
	if (n > th.rate.blockSize) {
		post("krc : n > block size\n");
		throw errOutOfRange;
	}
	if (th.rate.blockSize % n != 0) {
		post("krc : %d is not a divisor of the current signal block size %d\n", n, th.rate.blockSize);
		throw errFailed;
	}

	V result;
	{
		SaveStack ss(th);
		Rate subRate(th.rate, (int)n);
		{
			UseRate ur(th, subRate);
			fun.apply(th);
		}
		result = th.pop();
		{
			SaveStack ss2(th);
			th.push(result);
			th.push(n);
			gK2AC->apply_n(th, 2);
			result = th.pop();
		}
	}
	th.push(result);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

struct LFNoise0 : public Gen
{
	ZIn rate_;
	Z val_;
	Z phase_;
	Z freqmul_;

	LFNoise0(Thread& th, Arg rate) : Gen(th, itemTypeZ, true), rate_(rate),
		phase_(1.), freqmul_(th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "LFNoise0"; }

	virtual void pull(Thread& th) override
	{
		RGen& r = th.rgen;
		Z* out = mOut->fulfillz(mBlockSize);
		int framesToFill = mBlockSize;
		Z x = phase_;
		Z freqmul = freqmul_;
		while (framesToFill) {
			Z* rate;
			int n = framesToFill;
			int rateStride;
			if (rate_(th, n, rateStride, rate)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				while (x >= 1.) {
					x -= 1.;
					val_ = r.drand2();
				}
				out[i] = val_;

				x += *rate * freqmul;
				rate += rateStride;
			}
			framesToFill -= n;
			out += n;
			rate_.advance(n);
		}
		produce(framesToFill);
		phase_ = x;
	}
};

struct LFNoise1 : public Gen
{
	ZIn rate_;
	Z oldval_, newval_;
	Z slope_;
	Z phase_;
	Z freqmul_;

	LFNoise1(Thread& th, Arg rate) : Gen(th, itemTypeZ, true), rate_(rate),
		phase_(1.), freqmul_(th.rate.invSampleRate)
	{
		RGen& r = th.rgen;
		newval_ = oldval_ = r.drand2();
	}

	virtual const char* TypeName() const override { return "LFNoise1"; }

	virtual void pull(Thread& th) override
	{
		RGen& r = th.rgen;
		Z* out = mOut->fulfillz(mBlockSize);
		int framesToFill = mBlockSize;
		Z x = phase_;
		Z freqmul = freqmul_;
		while (framesToFill) {
			Z* rate;
			int n = framesToFill;
			int rateStride;
			if (rate_(th, n, rateStride, rate)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				while (x >= 1.) {
					x -= 1.;
					oldval_ = newval_;
					newval_ = r.drand2();
					slope_ = newval_ - oldval_;
				}
				out[i] = oldval_ + slope_ * x;

				x += *rate * freqmul;
				rate += rateStride;
			}
			framesToFill -= n;
			out += n;
			rate_.advance(n);
		}
		produce(framesToFill);
		phase_ = x;
	}
};

struct LFNoise3 : public Gen
{
	ZIn rate_;
	Z y0, y1, y2, y3;
	Z c0, c1, c2, c3;
	Z phase_;
	Z freqmul_;

	LFNoise3(Thread& th, Arg rate) : Gen(th, itemTypeZ, true), rate_(rate),
		phase_(1.), freqmul_(th.rate.invSampleRate)
	{
		RGen& r = th.rgen;
		y1 = r.drand2();
		y2 = r.drand2();
		y3 = r.drand2();
	}

	virtual const char* TypeName() const override { return "LFNoise3"; }

	virtual void pull(Thread& th) override
	{
		RGen& r = th.rgen;
		Z* out = mOut->fulfillz(mBlockSize);
		int framesToFill = mBlockSize;
		Z x = phase_;
		Z freqmul = freqmul_;
		while (framesToFill) {
			Z* rate;
			int n = framesToFill;
			int rateStride;
			if (rate_(th, n, rateStride, rate)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				while (x >= 1.) {
					x -= 1.;
					y0 = y1;
					y1 = y2;
					y2 = y3;
					y3 = r.drand2() * 0.8; // 0.8 because cubic interpolation can overshoot up to 1.25 if inputs are -1,1,1,-1.
					c0 = y1;
					c1 = .5 * (y2 - y0);
					c2 = y0 - 2.5 * y1 + 2. * y2 - .5 * y3;
					c3 = 1.5 * (y1 - y2) + .5 * (y3 - y0);
				}

				out[i] = ((c3 * x + c2) * x + c1) * x + c0;

				x += *rate * freqmul;
				rate += rateStride;
			}
			framesToFill -= n;
			out += n;
			rate_.advance(n);
		}
		produce(framesToFill);
		phase_ = x;
	}
};

static void lfnoise0_(Thread& th, Prim* prim)
{
	V rate = th.popZIn("lfnoise0 : freq");

	th.push(new List(new LFNoise0(th, rate)));
}

static void lfnoise1_(Thread& th, Prim* prim)
{
	V rate = th.popZIn("lfnoise1 : freq");

	th.push(new List(new LFNoise1(th, rate)));
}

static void lfnoise3_(Thread& th, Prim* prim)
{
	V rate = th.popZIn("lfnoise3 : freq");

	th.push(new List(new LFNoise3(th, rate)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

template <class F>
struct SymmetricEnv : public Gen
{
	Z xinc;
	Z x;
	int64_t n_;

	SymmetricEnv<F>(Thread& th, Z dur, Z scale) : Gen(th, itemTypeZ, true), x(-scale)
	{
		Z n = std::max(1., floor(dur * th.rate.sampleRate + .5));
		n_ = (int64_t)n;
		xinc = 2. * scale / n;
	}

	virtual void pull(Thread& th) override
	{
		int n = (int)std::min(n_, (int64_t)mBlockSize);
		Z* out = mOut->fulfillz(n);
		static_cast<F*>(this)->F::calc(n, out);
		mOut = mOut->nextp();
		n_ -= n;
		if (n_ == 0) {
			end();
		}
	}
};

template <class F>
struct TriggeredSymmetricEnv : public Gen
{
	ZIn trig_;
	BothIn dur_;
	BothIn amp_;
	Z xinc;
	Z x;
	Z scale_;
	Z ampval_;
	int64_t n_ = 0;

	TriggeredSymmetricEnv<F>(Thread& th, Arg trig, Arg dur, Arg amp, Z scale)
		: Gen(th, itemTypeZ, true), trig_(trig), dur_(dur), amp_(amp), scale_(scale)
	{
	}

	virtual void pull(Thread& th) override
	{
		Z* out = mOut->fulfillz(mBlockSize);
		int framesToFill = mBlockSize;
		while (framesToFill) {
			Z* trig;
			int n = framesToFill;
			int trigStride;
			if (trig_(th, n, trigStride, trig)) {
				setDone();
				break;
			}
			if (n_) {
				n = (int)std::min((int64_t)n, n_);
				static_cast<F*>(this)->F::calc(n, ampval_, out);
				n_ -= n;
				trig += n * trigStride;
			} else {
				for (int i = 0; i < n;) {
					if (*trig > 0.) {
						Z dur;
						if (dur_.onez(th, dur)) {
							setDone();
							produce(framesToFill - i);
							return;
						}
						if (amp_.onez(th, ampval_)) {
							setDone();
							produce(framesToFill - i);
							return;
						}
						x = -scale_;
						Z zn = std::max(1., floor(dur * th.rate.sampleRate + .5));
						n_ = (int64_t)zn;
						xinc = 2. * scale_ / zn;
						int n2 = (int)std::min((int64_t)(n-i), n_);
						static_cast<F*>(this)->F::calc(n2, ampval_, out+i);
						n_ -= n2;
						trig += n2 * trigStride;
						i += n2;
					} else {
						out[i] = 0.;
						++i;
						trig += trigStride;
					}
				}
			}
			framesToFill -= n;
			out += n;
			trig_.advance(n);
		}
	//ended:
		produce(framesToFill);
	}
};

struct TriggeredSignal : public Gen
{
	ZIn trig_;
	ZIn list_;
	BothIn amp_;
	V in_;
	Z ampval_;
	bool waiting_ = true;
	Z counter_ = 0.;

	TriggeredSignal(Thread& th, Arg trig, Arg in, Arg amp)
		: Gen(th, itemTypeZ, true), trig_(trig), amp_(amp), in_(in)
	{
	}

	virtual const char* TypeName() const override { return "TriggeredSignal"; }

	virtual void pull(Thread& th) override
	{
		Z* out = mOut->fulfillz(mBlockSize);
		int framesToFill = mBlockSize;
		while (framesToFill) {
			Z* trig;
			int n = framesToFill;
			int trigStride;
			if (trig_(th, n, trigStride, trig)) {
				setDone();
				break;
			}
			if (!waiting_) {
				Z* list;
				int listStride;
				if (list_(th, n, listStride, list)) {
					waiting_ = true;
					goto waiting;
				}
				Z amp = ampval_;
				for (int i = 0; i < n; ++i) {
					out[i] = amp * *list;
					list += listStride;
				}
				list_.advance(n);
			} else {
		waiting:
				for (int i = 0; i < n;) {
					if (*trig > 0.) {
						if (amp_.onez(th, ampval_)) {
							setDone();
							produce(framesToFill - i);
							return;
						}
						V in = in_;
						if (in.isFunOrPrim()) {
							SaveStack ss(th);
							try {
								th.push(counter_);
								in.apply(th);
							} catch (...) {
								setDone();
								produce(framesToFill);
								throw;
							}
							in = th.pop();
						}
						counter_ += 1.;
						list_.set(in);
						Z* list;
						int listStride;
						int n2 = n-i;
						if (list_(th, n2, listStride, list)) {
							out[i] = 0.;
							++i;
							trig += trigStride;
						} else {
							Z amp = ampval_;
							for (int j = i; j < i+n2; ++j) {
								out[j] = amp * *list;
								list += listStride;
							}
							trig += n2 * trigStride;
							i += n2;
							list_.advance(n2);
							waiting_ = i < n;
						}
					} else {
						out[i] = 0.;
						++i;
						trig += trigStride;
					}
				}
			}
			framesToFill -= n;
			out += n;
			trig_.advance(n);
		}
	//ended:
		produce(framesToFill);
	}
};

static void tsig_(Thread& th, Prim* prim)
{
	V amp = th.pop();
	V in = th.pop();
	V trig = th.popZIn("tsig : trig");

	th.push(new List(new TriggeredSignal(th, trig, in, amp)));
}

struct ParEnv : public SymmetricEnv<ParEnv>
{
	ParEnv(Thread& th, Z dur) : SymmetricEnv<ParEnv>(th, dur, 1.) {}

	virtual const char* TypeName() const override { return "ParEnv"; }

	virtual void calc(int n, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			Z x2 = x*x;
			out[i] = 1. - x2;
			x += xinc;
		}
	}
};

static void parenv_(Thread& th, Prim* prim)
{
	Z dur = th.popFloat("parenv : dur");

	th.push(new List(new ParEnv(th, dur)));
}

struct TParEnv : public TriggeredSymmetricEnv<TParEnv>
{
	TParEnv(Thread& th, Arg trig, Arg dur, Arg amp) : TriggeredSymmetricEnv<TParEnv>(th, trig, dur, amp, 1.) {}

	virtual const char* TypeName() const override { return "TParEnv"; }

	virtual void calc(int n, Z amp, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			Z x2 = x*x;
			out[i] = amp * (1. - x2);
			x += xinc;
		}
	}
};

static void tparenv_(Thread& th, Prim* prim)
{
	V amp = th.popZIn("tparenv : amp");
	V dur = th.popZIn("tparenv : dur");
	V trig = th.popZIn("tparenv : trig");

	th.push(new List(new TParEnv(th, trig, dur, amp)));
}

struct QuadEnv : public SymmetricEnv<QuadEnv>
{
	QuadEnv(Thread& th, Z dur) : SymmetricEnv<QuadEnv>(th, dur, 1.) {}

	virtual const char* TypeName() const override { return "QuadEnv"; }

	virtual void calc(int n, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			Z x2 = x*x;
			out[i] = 1. - x2*x2;
			x += xinc;
		}
	}
};

static void quadenv_(Thread& th, Prim* prim)
{
	Z dur = th.popFloat("quadenv : dur");

	th.push(new List(new QuadEnv(th, dur)));
}

struct TQuadEnv : public TriggeredSymmetricEnv<TQuadEnv>
{
	TQuadEnv(Thread& th, Arg trig, Arg dur, Arg amp) : TriggeredSymmetricEnv<TQuadEnv>(th, trig, dur, amp, 1.) {}

	virtual const char* TypeName() const override { return "TQuadEnv"; }

	virtual void calc(int n, Z amp, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			Z x2 = x*x;
			out[i] = amp * (1. - x2*x2);
			x += xinc;
		}
	}
};

static void tquadenv_(Thread& th, Prim* prim)
{
	V amp = th.popZIn("tquadenv : amp");
	V dur = th.popZIn("tquadenv : dur");
	V trig = th.popZIn("tquadenv : trig");

	th.push(new List(new TQuadEnv(th, trig, dur, amp)));
}

struct OctEnv : public SymmetricEnv<OctEnv>
{
	OctEnv(Thread& th, Z dur) : SymmetricEnv<OctEnv>(th, dur, 1.) {}

	virtual const char* TypeName() const override { return "OctEnv"; }

	virtual void calc(int n, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			Z x2 = x*x;
			Z x4 = x2*x2;

			out[i] = 1. - x4*x4;

			x += xinc;
		}
	}
};

static void octenv_(Thread& th, Prim* prim)
{
	Z dur = th.popFloat("octenv : dur");

	th.push(new List(new OctEnv(th, dur)));
}

struct TOctEnv : public TriggeredSymmetricEnv<TOctEnv>
{
	TOctEnv(Thread& th, Arg trig, Arg dur, Arg amp) : TriggeredSymmetricEnv<TOctEnv>(th, trig, dur, amp, 1.) {}

	virtual const char* TypeName() const override { return "TOctEnv"; }

	virtual void calc(int n, Z amp, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			Z x2 = x*x;
			Z x4 = x2*x2;
			out[i] = amp * (1. - x4*x4);
			x += xinc;
		}
	}
};

static void toctenv_(Thread& th, Prim* prim)
{
	V amp = th.popZIn("toctenv : amp");
	V dur = th.popZIn("toctenv : dur");
	V trig = th.popZIn("toctenv : trig");

	th.push(new List(new TOctEnv(th, trig, dur, amp)));
}

struct TriEnv : public SymmetricEnv<TriEnv>
{
	TriEnv(Thread& th, Z dur) : SymmetricEnv<TriEnv>(th, dur, 1.) {}

	virtual const char* TypeName() const override { return "TriEnv"; }

	virtual void calc(int n, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = 1. - fabs(x);
			x += xinc;
		}
	}
};

static void trienv_(Thread& th, Prim* prim)
{
	Z dur = th.popFloat("trienv : dur");

	th.push(new List(new TriEnv(th, dur)));
}

struct TTriEnv : public TriggeredSymmetricEnv<TTriEnv>
{
	TTriEnv(Thread& th, Arg trig, Arg dur, Arg amp) : TriggeredSymmetricEnv<TTriEnv>(th, trig, dur, amp, 1.) {}

	virtual const char* TypeName() const override { return "TTriEnv"; }

	virtual void calc(int n, Z amp, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = amp * (1. - fabs(x));
			x += xinc;
		}
	}
};

static void ttrienv_(Thread& th, Prim* prim)
{
	V amp = th.popZIn("ttrienv : amp");
	V dur = th.popZIn("ttrienv : dur");
	V trig = th.popZIn("ttrienv : trig");

	th.push(new List(new TTriEnv(th, trig, dur, amp)));
}

struct Tri2Env : public SymmetricEnv<Tri2Env>
{
	Tri2Env(Thread& th, Z dur) : SymmetricEnv<Tri2Env>(th, dur, 1.) {}

	virtual const char* TypeName() const override { return "Tri2Env"; }

	virtual void calc(int n, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			Z y = 1. - fabs(x);
			out[i] = y*y;
			x += xinc;
		}
	}
};

static void tri2env_(Thread& th, Prim* prim)
{
	Z dur = th.popFloat("tri2env : dur");

	th.push(new List(new Tri2Env(th, dur)));
}

struct TTri2Env : public TriggeredSymmetricEnv<TTri2Env>
{
	TTri2Env(Thread& th, Arg trig, Arg dur, Arg amp) : TriggeredSymmetricEnv<TTri2Env>(th, trig, dur, amp, 1.) {}

	virtual const char* TypeName() const override { return "TTri2Env"; }

	virtual void calc(int n, Z amp, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			Z y = 1. - fabs(x);
			out[i] = amp * y*y;
			x += xinc;
		}
	}
};

static void ttri2env_(Thread& th, Prim* prim)
{
	V amp = th.popZIn("ttri2env : amp");
	V dur = th.popZIn("ttri2env : dur");
	V trig = th.popZIn("ttri2env : trig");

	th.push(new List(new TTri2Env(th, trig, dur, amp)));
}

struct TrapezEnv : public SymmetricEnv<TrapezEnv>
{
	TrapezEnv(Thread& th, Z dur) : SymmetricEnv<TrapezEnv>(th, dur, 1.) {}

	virtual const char* TypeName() const override { return "TrapezEnv"; }

	virtual void calc(int n, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = 2. - fabs(x-.5) - fabs(x+.5);
			x += xinc;
		}
	}
};

static void trapezenv_(Thread& th, Prim* prim)
{
	Z dur = th.popFloat("trapezenv : dur");

	th.push(new List(new TrapezEnv(th, dur)));
}

struct TTrapezEnv : public TriggeredSymmetricEnv<TTrapezEnv>
{
	TTrapezEnv(Thread& th, Arg trig, Arg dur, Arg amp) : TriggeredSymmetricEnv<TTrapezEnv>(th, trig, dur, amp, 1.) {}

	virtual const char* TypeName() const override { return "TTrapezEnv"; }

	virtual void calc(int n, Z amp, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			Z y = 2. - fabs(x-.5) - fabs(x+.5);
			out[i] = amp * y;
			x += xinc;
		}
	}
};

static void ttrapezenv_(Thread& th, Prim* prim)
{
	V amp = th.popZIn("ttrapezenv : amp");
	V dur = th.popZIn("ttrapezenv : dur");
	V trig = th.popZIn("ttrapezenv : trig");

	th.push(new List(new TTrapezEnv(th, trig, dur, amp)));
}

struct Trapez2Env : public SymmetricEnv<Trapez2Env>
{
	Trapez2Env(Thread& th, Z dur) : SymmetricEnv<Trapez2Env>(th, dur, 1.) {}

	virtual const char* TypeName() const override { return "Trapez2Env"; }

	virtual void calc(int n, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			Z y = 2. - fabs(x-.5) - fabs(x+.5);
			out[i] = y*y;
			x += xinc;
		}
	}
};

static void trapez2env_(Thread& th, Prim* prim)
{
	Z dur = th.popFloat("trapez2env : dur");

	th.push(new List(new Trapez2Env(th, dur)));
}

struct TTrapez2Env : public TriggeredSymmetricEnv<TTrapez2Env>
{
	TTrapez2Env(Thread& th, Arg trig, Arg dur, Arg amp) : TriggeredSymmetricEnv<TTrapez2Env>(th, trig, dur, amp, 1.) {}

	virtual const char* TypeName() const override { return "TTrapez2Env"; }

	virtual void calc(int n, Z amp, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			Z y = 2. - fabs(x-.5) - fabs(x+.5);
			out[i] = amp * y*y;
			x += xinc;
		}
	}
};

static void ttrapez2env_(Thread& th, Prim* prim)
{
	V amp = th.popZIn("ttrapez2env : amp");
	V dur = th.popZIn("ttrapez2env : dur");
	V trig = th.popZIn("ttrapez2env : trig");

	th.push(new List(new TTrapez2Env(th, trig, dur, amp)));
}

struct CosEnv : public SymmetricEnv<CosEnv>
{
	CosEnv(Thread& th, Z dur) : SymmetricEnv<CosEnv>(th, dur, M_PI_2) {}

	virtual const char* TypeName() const override { return "CosEnv"; }

	virtual void calc(int n, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = cos(x);
			x += xinc;
		}
	}
};

static void cosenv_(Thread& th, Prim* prim)
{
	Z dur = th.popFloat("cosenv : dur");

	th.push(new List(new CosEnv(th, dur)));
}

struct TCosEnv : public TriggeredSymmetricEnv<TCosEnv>
{
	TCosEnv(Thread& th, Arg trig, Arg dur, Arg amp) : TriggeredSymmetricEnv<TCosEnv>(th, trig, dur, amp, 1.) {}

	virtual const char* TypeName() const override { return "TCosEnv"; }

	virtual void calc(int n, Z amp, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = amp * cos(x);
			x += xinc;
		}
	}
};

static void tcosenv_(Thread& th, Prim* prim)
{
	V amp = th.popZIn("tcosenv : amp");
	V dur = th.popZIn("tcosenv : dur");
	V trig = th.popZIn("tcosenv : trig");

	th.push(new List(new TCosEnv(th, trig, dur, amp)));
}

struct HanEnv : public SymmetricEnv<HanEnv>
{
	HanEnv(Thread& th, Z dur) : SymmetricEnv<HanEnv>(th, dur, M_PI_2) {}

	virtual const char* TypeName() const override { return "HanEnv"; }

	virtual void calc(int n, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			Z y = cos(x);
			out[i] = y*y;
			x += xinc;
		}
	}
};

static void hanenv_(Thread& th, Prim* prim)
{
	Z dur = th.popFloat("hanenv : dur");

	th.push(new List(new HanEnv(th, dur)));
}

struct THanEnv : public TriggeredSymmetricEnv<THanEnv>
{
	THanEnv(Thread& th, Arg trig, Arg dur, Arg amp) : TriggeredSymmetricEnv<THanEnv>(th, trig, dur, amp, 1.) {}

	virtual const char* TypeName() const override { return "THanEnv"; }

	virtual void calc(int n, Z amp, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			Z y = cos(x);
			out[i] = amp * y*y;
			x += xinc;
		}
	}
};

static void thanenv_(Thread& th, Prim* prim)
{
	V amp = th.popZIn("thanenv : amp");
	V dur = th.popZIn("thanenv : dur");
	V trig = th.popZIn("thanenv : trig");

	th.push(new List(new THanEnv(th, trig, dur, amp)));
}

struct Han2Env : public SymmetricEnv<Han2Env>
{
	Han2Env(Thread& th, Z dur) : SymmetricEnv<Han2Env>(th, dur, M_PI_2) {}

	virtual const char* TypeName() const override { return "Han2Env"; }

	virtual void calc(int n, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			Z y = cos(x);
			Z y2 = y*y;
			out[i] = y2*y2;
			x += xinc;
		}
	}
};

static void han2env_(Thread& th, Prim* prim)
{
	Z dur = th.popFloat("han2env : dur");

	th.push(new List(new Han2Env(th, dur)));
}

struct THan2Env : public TriggeredSymmetricEnv<THan2Env>
{
	THan2Env(Thread& th, Arg trig, Arg dur, Arg amp) : TriggeredSymmetricEnv<THan2Env>(th, trig, dur, amp, 1.) {}

	virtual const char* TypeName() const override { return "THan2Env"; }

	virtual void calc(int n, Z amp, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			Z y = cos(x);
			Z y2 = y*y;
			out[i] = amp * y2*y2;
			x += xinc;
		}
	}
};

static void than2env_(Thread& th, Prim* prim)
{
	V amp = th.popZIn("than2env : amp");
	V dur = th.popZIn("than2env : dur");
	V trig = th.popZIn("than2env : trig");

	th.push(new List(new THan2Env(th, trig, dur, amp)));
}

struct GaussEnv : public SymmetricEnv<GaussEnv>
{
	Z widthFactor;
	GaussEnv(Thread& th, Z dur, Z width) : SymmetricEnv<GaussEnv>(th, dur, 1.), widthFactor(-1. / (2. * width * width)) {}

	virtual const char* TypeName() const override { return "GaussEnv"; }

	virtual void calc(int n, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = exp(x * x * widthFactor);
			x += xinc;
		}
	}
};

static void gaussenv_(Thread& th, Prim* prim)
{
	Z width = th.popFloat("gaussenv : width");
	Z dur = th.popFloat("gaussenv : dur");

	th.push(new List(new GaussEnv(th, dur, width)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Pause : public Gen
{
	ZIn _in;
	ZIn _amp;

	Pause(Thread& th, Arg in, Arg amp)
		: Gen(th, itemTypeZ, amp.isFinite()), _in(in), _amp(amp)
	{
	}

	virtual const char* TypeName() const override { return "Pause"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			Z *amp;
			int n, ampStride;
			n = framesToFill;
			if (_amp(th, n, ampStride, amp) ) {
				setDone();
				goto leave;
			}

			int framesThisTime = n;
			while (framesThisTime) {
				int zerolen = 0;
				for (int i = 0; i < framesThisTime && *amp <= 0.; ++i) {
					*out++ = 0.;
					amp += ampStride;
					++zerolen;
				}
				framesThisTime -= zerolen;

				int seglen = 0;
				for (int i = 0; i < framesThisTime && *amp > 0.; ++i) {
					++seglen;
					amp += ampStride;
				}
				amp -= seglen * ampStride;

				int seglenRemain = seglen;
				while (seglenRemain) {
					Z *in;
					int n2, inStride;
					n2 = seglenRemain;
					if (_in(th, n2, inStride, in) ) {
						setDone();
						goto leave;
					}
					for (int i = 0; i < n2; ++i) {
						out[i] = *amp * *in;
						in += inStride;
						amp += ampStride;
					}
					out += n2;
					_in.advance(n2);
					seglenRemain -= n2;
				}
				framesThisTime -= seglen;

			}
			framesToFill -= n;
			_amp.advance(n);
		}
leave:
		produce(framesToFill);
	}

};

static void pause_(Thread& th, Prim* prim)
{
	V amp   = th.popZIn("pause : amp");
	V in   = th.popZIn("pause : in");

	th.push(new List(new Pause(th, in, amp)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

P<String> s_tempo;
P<String> s_dt;
P<String> s_out;

class OverlapAddInputSource;
class OverlapAddOutputChannel;

class OverlapAddBase : public Object
{
protected:
	OverlapAddOutputChannel* mOutputs = nullptr;
	P<OverlapAddInputSource> mActiveSources;
	bool mFinished = false;
	bool mNoMoreSources = false;
	int mNumChannels;
public:
    OverlapAddBase(int numChannels);
    virtual ~OverlapAddBase();

	virtual const char* TypeName() const override { return "OverlapAddBase"; }

	virtual bool pull(Thread& th);
	virtual void addNewSources(Thread& th, int blockSize) = 0;

	P<List> createOutputs(Thread& th);
    void fulfillOutputs(int blockSize);
    void produceOutputs(int shrinkBy);
    int renderActiveSources(Thread& th, int blockSize, bool& anyDone);
    void removeInactiveSources();
};

class OverlapAdd : public OverlapAddBase
{
protected:
	VIn mSounds;
	BothIn mHops;
	ZIn mRate;
	Z mBeatTime;
	Z mNextEventBeatTime;
	Z mEventCounter;
	Z mRateMul;
	int64_t mSampleTime;
	int64_t mPrevChaseTime;

	P<Form> mChasedSignals;

public:
	OverlapAdd(Thread& th, Arg sounds, Arg hops, Arg rate, P<Form> const& chasedSignals, int numChannels);
	virtual ~OverlapAdd() {}

	virtual const char* TypeName() const override { return "OverlapAdd"; }

	virtual void addNewSources(Thread& th, int blockSize) override;
	void chaseToTime(Thread& th, int64_t inSampleTime);
};

class OverlapAddInputSource : public Object
{
public:
	P<OverlapAddInputSource> mNextSource;
	std::vector<ZIn> mInputs;
	int mOffset;
	bool mSourceDone;

	OverlapAddInputSource(Thread& th, List* channels, int inOffset, P<OverlapAddInputSource> const& inNextSource)
		: mNextSource(inNextSource), mOffset(inOffset), mSourceDone(false)
	{
		if (channels->isVList()) {
			P<List> packedChannels = channels->pack(th);
			Array* a = packedChannels->mArray();

			// put channels into mInputs
			mInputs.reserve(a->size());
			for (int i = 0; i < a->size(); ++i) {
				ZIn zin(a->v()[i]);
				mInputs.push_back(zin);
			}
		} else {
			ZIn zin(channels);
			mInputs.push_back(zin);
		}
	}

	virtual const char* TypeName() const override { return "OverlapAdd"; }
};

class OverlapAddOutputChannel : public Gen
{
	friend class OverlapAddBase;
	P<OverlapAddBase> mOverlapAddBase;
	OverlapAddOutputChannel* mNextOutput;

public:
	OverlapAddOutputChannel(Thread& th, OverlapAddBase* inOverlapAdd)
        : Gen(th, itemTypeZ, false), mOverlapAddBase(inOverlapAdd), mNextOutput(nullptr)
	{
	}

	virtual void norefs() override
	{
		mOut = nullptr;
		mOverlapAddBase = nullptr;
	}

	virtual const char* TypeName() const override { return "OverlapAddOutputChannel"; }

	virtual void pull(Thread& th) override
	{
		if (mOverlapAddBase->pull(th)) {
			end();
		}
	}

};

OverlapAddBase::OverlapAddBase(int numChannels)
	: mNumChannels(numChannels)
{
}

OverlapAddBase::~OverlapAddBase()
{
	OverlapAddOutputChannel* output = mOutputs;
	do {
		OverlapAddOutputChannel* next = output->mNextOutput;
		delete output;
		output = next;
	} while (output);
}

OverlapAdd::OverlapAdd(Thread& th, Arg sounds, Arg hops, Arg rate, P<Form> const& chasedSignals, int numChannels)
	: OverlapAddBase(numChannels),
    mSounds(sounds), mHops(hops), mRate(rate),
	mBeatTime(0.), mNextEventBeatTime(0.), mEventCounter(0.), mRateMul(th.rate.invSampleRate),
	mSampleTime(0), mPrevChaseTime(0),
	mChasedSignals(chasedSignals)
{
}

P<List> OverlapAddBase::createOutputs(Thread& th)
{
	P<List> s = new List(itemTypeV, mNumChannels);

	// fill s->mArray with ola's output channels.
    OverlapAddOutputChannel* last = nullptr;
	P<Array> a = s->mArray;
	for (int i = 0; i < mNumChannels; ++i) {
        OverlapAddOutputChannel* c = new OverlapAddOutputChannel(th, this);
        if (last) last->mNextOutput = c;
        else mOutputs = c;
        last = c;
		a->add(new List(c));
	}

	return s;
}

void OverlapAdd::addNewSources(Thread& th, int blockSize)
{
	// integrate tempo and add new sources.
	Z* rate;
	int rateStride;
	if (mRate(th, blockSize, rateStride, rate)) {
		mNoMoreSources = true;
	} else if (!mNoMoreSources) {
		Z beatTime = mBeatTime;
		Z nextEventBeatTime = mNextEventBeatTime;
		Z ratemul = mRateMul;
		for (int i = 0; i < blockSize; ++i) {
			while (beatTime >= nextEventBeatTime) {

				chaseToTime(th, mSampleTime + i);

				V newSource;
				if (mSounds.one(th, newSource)) {
					mNoMoreSources = true;
					break;
				}

				if (newSource.isFun()) {
					SaveStack ss(th);
					th.push(mEventCounter);
					newSource.apply(th);
					newSource = th.pop();
				}

				V out;
				Z deltaTime;
				if (mHops.onez(th, deltaTime)) {
					mNoMoreSources = true;
					break;
				}

				if (newSource.isForm()) {
					if (mChasedSignals()) {
						V parents[2];
						parents[0] = mChasedSignals;
						parents[1] = newSource;
						newSource = linearizeInheritance(th, 2, parents);
					}
					newSource.dot(th, s_out, out);
					V hop;
					if (newSource.dot(th, s_dt, hop) && hop.isReal()) {
						deltaTime = hop.f;
					}

				} else {
					out = newSource;
				}

				// must be a finite array with fewer than mNumChannels
				if (out.isZList() || (out.isVList() && out.isFinite())) {
					List* s = (List*)out.o();
					// create an active source:
					P<OverlapAddInputSource> source = new OverlapAddInputSource(th, s, i, mActiveSources);
					mActiveSources = source;
				}

				nextEventBeatTime += deltaTime;
				mEventCounter += 1.;
			}
			beatTime += *rate * ratemul;
			rate += rateStride;
		}
		mBeatTime = beatTime;
		mNextEventBeatTime = nextEventBeatTime;
		mSampleTime += blockSize;

		mRate.advance(blockSize);

		chaseToTime(th, mSampleTime);
	}
}

void OverlapAddBase::fulfillOutputs(int blockSize)
{
	OverlapAddOutputChannel* output = mOutputs;
	do {
		if (output->mOut) {
			Z* out = output->mOut->fulfillz(blockSize);
			memset(out, 0, output->mBlockSize * sizeof(Z));
		}
		output = output->mNextOutput;
	} while (output);
}

int OverlapAddBase::renderActiveSources(Thread& th, int blockSize, bool& anyDone)
{
	int maxProduced = 0;
	OverlapAddInputSource* source = mActiveSources();
	while (source) {
		int offset = source->mOffset;
		int pullSize = blockSize - offset;
		std::vector<ZIn>& sourceChannels = source->mInputs;
		bool allOutputsDone = true; // initial value for reduction on &&
		OverlapAddOutputChannel* output = mOutputs;
		for (size_t j = 0; j < sourceChannels.size() && output; ++j, output = output->mNextOutput) {
			if (output->mOut) {
				ZIn& zin = sourceChannels[j];
				if (zin.mIsConstant && zin.mConstant.f == 0.)
					continue;

				int n = pullSize;
				Z* out = output->mOut->mArray->z() + offset;
				if (!zin.mix(th, n, out)) {
					allOutputsDone = false;
				}
				maxProduced = std::max(maxProduced, n);
			}
		}
		source->mOffset = 0;
		if (allOutputsDone) {
			// mark for removal from mActiveSources
			source->mSourceDone = true;
            anyDone = true;
		}
		source = source->mNextSource();
	}
	return maxProduced;
}

void OverlapAddBase::removeInactiveSources()
{
	P<OverlapAddInputSource> source = mActiveSources();
	P<OverlapAddInputSource> prevSource = nullptr;
	mActiveSources = nullptr;

	while (source) {
		P<OverlapAddInputSource> nextSource = source->mNextSource;
		source->mNextSource = nullptr;
		if (!source->mSourceDone) {
			if (prevSource()) prevSource->mNextSource = source;
			else mActiveSources = source;
            prevSource = source;
		}
		source = nextSource;
	}
}

void OverlapAddBase::produceOutputs(int shrinkBy)
{
	OverlapAddOutputChannel* output = mOutputs;
	do {
		if (output->mOut)
			output->produce(shrinkBy);
		output = output->mNextOutput;
	} while (output);
}

bool OverlapAddBase::pull(Thread& th)
{
	if (mFinished) {
		return mFinished;
    }

	OverlapAddOutputChannel* output = mOutputs;
	int blockSize = output->mBlockSize;
	addNewSources(th, blockSize);

	fulfillOutputs(blockSize);

    bool anyDone = false;
	int maxProduced = renderActiveSources(th, blockSize, anyDone);

	mFinished = mNoMoreSources && mActiveSources() == nullptr;
	int shrinkBy = mFinished ? blockSize - maxProduced : 0;

	produceOutputs(shrinkBy);

	if (anyDone)
        removeInactiveSources();

	return mFinished;
}

void OverlapAdd::chaseToTime(Thread& th, int64_t inSampleTime)
{
	int64_t n = inSampleTime - mPrevChaseTime;
	mPrevChaseTime = inSampleTime;

	if (mChasedSignals() && n > 0) {
		mChasedSignals = mChasedSignals->chaseForm(th, n);
	}
}

const int64_t kMaxOverlapAddChannels = 10000;

static void ola_(Thread& th, Prim* prim)
{
	int64_t numChannels = th.popInt("ola : numChannels");
	V rate = th.pop();
	V hops = th.popZInList("ola : hops");
	V sounds = th.pop();

	if (numChannels > kMaxOverlapAddChannels) {
		post("ola : too many channels\n");
		throw errFailed;
	}

	P<Form> chasedSignals;
	if (rate.isForm()) {
		chasedSignals = (Form*)rate.o();
		rate = 1.;
		chasedSignals->dot(th, s_tempo, rate);
	}

	P<OverlapAdd> ola = new OverlapAdd(th, sounds, hops, rate, chasedSignals, (int)numChannels);

	th.push(ola->createOutputs(th));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

class ITD;

class ITD_OutputChannel : public Gen
{
	friend class ITD;
	P<ITD> mITD;

public:
	ITD_OutputChannel(Thread& th, bool inFinite, ITD* inITD);

	virtual void norefs() override
	{
		mOut = nullptr;
		mITD = nullptr;
	}

	virtual const char* TypeName() const override { return "ITD_OutputChannel"; }

	virtual void pull(Thread& th) override;
};

struct ITD : public Gen
{
	ZIn in_;
	ZIn pan_;
	Z maxdelay_;
	Z half;
	int32_t bufSize;
	int32_t bufMask;
	int32_t bufPos;
	Z* buf;
	Z sr;
	ITD_OutputChannel* mLeft;
	ITD_OutputChannel* mRight;

	ITD(Thread& th, Arg in, Arg pan, Z maxdelay) : Gen(th, itemTypeZ, false), in_(in), pan_(pan), maxdelay_(maxdelay)
	{
		sr = th.rate.sampleRate;
		half = (int32_t)ceil(sr * maxdelay * .5 + .5);
		bufSize = NEXTPOWEROFTWO(2 * (int)half + 3);
		bufMask = bufSize - 1;
		bufPos = 0;
		buf = (Z*)calloc(bufSize, sizeof(Z));
	}

	~ITD() { delete mLeft; delete mRight; free(buf); }

	virtual const char* TypeName() const override { return "ITD"; }

	P<List> createOutputs(Thread& th);

	virtual void pull(Thread& th) override
	{
		int framesToFill = mLeft->mBlockSize;

		Z Sink = 0.;
		Z* Lout;
		Z* Rout;
		int Loutstride = 1;
		int Routstride = 1;

		if (mLeft->mOut) {
			Lout = mLeft->mOut->fulfillz(framesToFill);
		} else {
			Lout = &Sink;
			Loutstride = 0;
		}

		if (mRight->mOut) {
			Rout = mRight->mOut->fulfillz(framesToFill);
		} else {
			Rout = &Sink;
			Routstride = 0;
		}

		while (framesToFill) {
			int n = framesToFill;
			int inStride, panStride;
			Z *in, *pan;
			if (in_(th, n, inStride, in) || pan_(th, n, panStride, pan)) {
				mLeft->setDone();
				mRight->setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					{
						Z fpos = std::max(2., *pan * half + half);
						Z ipos = floor(fpos);
						Z frac = fpos - ipos;
						int32_t offset = bufPos-(int32_t)ipos;
						Z a = buf[(offset+1) & bufMask];
						Z b = buf[(offset  ) & bufMask];
						Z c = buf[(offset-1) & bufMask];
						Z d = buf[(offset-2) & bufMask];
						*Lout = lagrangeInterpolate(frac, a, b, c, d);
						Lout += Loutstride;
					}
					{
						Z fpos = std::max(2., -*pan * half + half);
						Z ipos = floor(fpos);
						Z frac = fpos - ipos;
						int32_t offset = bufPos-(int32_t)ipos;
						Z a = buf[(offset+1) & bufMask];
						Z b = buf[(offset  ) & bufMask];
						Z c = buf[(offset-1) & bufMask];
						Z d = buf[(offset-2) & bufMask];
						*Rout = lagrangeInterpolate(frac, a, b, c, d);
						Rout += Routstride;
					}
					buf[bufPos & bufMask] = *in;
					in += inStride;
					pan += panStride;
					++bufPos;
				}
				in_.advance(n);
				pan_.advance(n);
				framesToFill -= n;
			}
		}
		if (mLeft->mOut)  mLeft->produce(framesToFill);
		if (mRight->mOut) mRight->produce(framesToFill);
	}
};

ITD_OutputChannel::ITD_OutputChannel(Thread& th, bool inFinite, ITD* inITD) : Gen(th, itemTypeZ, inFinite), mITD(inITD)
{
}

void ITD_OutputChannel::pull(Thread& th)
{
	mITD->pull(th);
}

P<List> ITD::createOutputs(Thread& th)
{
	mLeft = new ITD_OutputChannel(th, finite, this);
	mRight = new ITD_OutputChannel(th, finite, this);

	P<Gen> left = mLeft;
	P<Gen> right = mRight;

	P<List> s = new List(itemTypeV, 2);
	P<Array> a = s->mArray;
	a->add(new List(left));
	a->add(new List(right));

	return s;
}

static void itd_(Thread& th, Prim* prim)
{
	Z maxdelay = th.popFloat("itd : maxdelay");
	V pan = th.popZIn("itd : pan");
	V in = th.popZIn("itd : in");

	P<ITD> itd = new ITD(th, in, pan, maxdelay);

	P<List> s = itd->createOutputs(th);

	th.push(s);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

inline Z fast_sin1(Z x)
{
	x = x - floor(x + .5);

    Z y = x * (8. - 16. * fabs(x));
	y = 0.225 * (y * fabs(y) - y) + y;

	return y;
}

inline Z fast_cos1(Z x)
{
	return fast_sin1(x + .25);
}

inline Z fast_pan(Z x)
{
	Z y = .75 + x * (.5 - .25 * x);
	y = 0.225 * (y * fabs(y) - y) + y;

	return y;
}

struct Pan2Out;

struct Pan2 : public Object
{
	ZIn _in;
	ZIn _pos;

	Pan2Out* mLeft;
	Pan2Out* mRight;

	Pan2(Thread& th, Arg inIn, Arg inPos)
		: _in(inIn), _pos(inPos)
	{
		finite = mostFinite(inIn, inPos);
	}

	P<List> createOutputs(Thread& th);

	virtual const char* TypeName() const override { return "Pan2"; }

	virtual void pull(Thread& th);
};

struct Pan2Out : public Gen
{
	P<Pan2> mPan2;

	Pan2Out(Thread& th, bool inFinite, P<Pan2> const& inPan2) : Gen(th, itemTypeZ, inFinite), mPan2(inPan2)
	{
	}

	virtual void norefs() override
	{
		mOut = nullptr;
		mPan2 = nullptr;
	}

	virtual const char* TypeName() const override { return "Pan2Out"; }

	virtual void pull(Thread& th) override
	{
		mPan2->pull(th);
	}

};

void Pan2::pull(Thread& th)
{
	int framesToFill = mLeft->mBlockSize;

	Z Sink = 0.;
	Z* Lout;
	Z* Rout;
	int Loutstride = 1;
	int Routstride = 1;

	if (mLeft->mOut) {
		Lout = mLeft->mOut->fulfillz(framesToFill);
	} else {
		Lout = &Sink;
		Loutstride = 0;
	}

	if (mRight->mOut) {
		Rout = mRight->mOut->fulfillz(framesToFill);
	} else {
		Rout = &Sink;
		Routstride = 0;
	}

	while (framesToFill) {
		Z *a, *b;
		int n, aStride, bStride;
		n = framesToFill;
		if (_in(th, n, aStride, a) || _pos(th, n, bStride, b)) {
			mLeft->setDone();
			mRight->setDone();
			break;
		}

		if (bStride == 0) {
			Z x = std::clamp(*b, -1., 1.);
			Z Lpan = fast_pan(-x);
			Z Rpan = fast_pan(x);
			for (int i = 0; i < n; ++i) {
				Z z = *a;
				*Lout = z * Lpan;
				*Rout = z * Rpan;
				a += aStride;
				Lout += Loutstride;
				Rout += Routstride;
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z x = std::clamp(*b, -1., 1.);
				Z z = *a;
				*Lout = z * fast_pan(-x);
				*Rout = z * fast_pan(x);
				a += aStride;
				b += bStride;
				Lout += Loutstride;
				Rout += Routstride;
			}
		}
		framesToFill -= n;
		_in.advance(n);
		_pos.advance(n);
	}
	if (mLeft->mOut) mLeft->produce(framesToFill);
	if (mRight->mOut) mRight->produce(framesToFill);
}

P<List> Pan2::createOutputs(Thread& th)
{
	mLeft = new Pan2Out(th, finite, this);
	mRight = new Pan2Out(th, finite, this);

	P<Gen> left = mLeft;
	P<Gen> right = mRight;

	P<List> s = new List(itemTypeV, 2);
	P<Array> a = s->mArray;
	a->add(new List(left));
	a->add(new List(right));

	return s;
}

static void pan2_(Thread& th, Prim* prim)
{
	V pos = th.popZIn("pan2 : pos");
	V in = th.popZIn("pan2 : in");

	P<Pan2> pan = new Pan2(th, in, pos);

	P<List> s = pan->createOutputs(th);

	th.push(s);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Balance2Out;

struct Balance2 : public Object
{
	ZIn _L;
	ZIn _R;
	ZIn _pos;

	P<Balance2Out> mLeft;
	P<Balance2Out> mRight;

	Balance2(Thread& th, Arg inL, Arg inR, Arg inPos)
		: _L(inL), _R(inR), _pos(inPos)
	{
		finite = mostFinite(inL, inR, inPos);
	}

	P<List> createOutputs(Thread& th);

	virtual const char* TypeName() const override { return "Balance2"; }

	virtual void pull(Thread& th);
};

struct Balance2Out : public Gen
{
	P<Balance2> mBalance2;

	Balance2Out(Thread& th, bool inFinite, P<Balance2> const& inBalance2) : Gen(th, itemTypeZ, inFinite), mBalance2(inBalance2)
	{
	}

	virtual void norefs() override
	{
		mOut = nullptr;
		mBalance2 = nullptr;
	}

	virtual const char* TypeName() const override { return "Balance2Out"; }

	virtual void pull(Thread& th) override
	{
		mBalance2->pull(th);
	}

};

void Balance2::pull(Thread& th)
{
	int framesToFill = mLeft->mBlockSize;

	Z Sink = 0.;
	Z* Lout;
	Z* Rout;
	int Loutstride = 1;
	int Routstride = 1;

	if (mLeft->mOut) {
		Lout = mLeft->mOut->fulfillz(framesToFill);
	} else {
		Lout = &Sink;
		Loutstride = 0;
	}

	if (mRight->mOut) {
		Rout = mRight->mOut->fulfillz(framesToFill);
	} else {
		Rout = &Sink;
		Routstride = 0;
	}

	while (framesToFill) {
		Z *a, *b, *c;
		int n, aStride, bStride, cStride;
		n = framesToFill;
		if (_L(th, n, aStride, a) || _R(th, n, bStride, b) || _pos(th, n, cStride, c)) {
			mLeft->setDone();
			mRight->setDone();
			break;
		}

		if (cStride == 0) {
			Z x = std::clamp(*c, -1., 1.);
			Z Lpan = fast_pan(-x);
			Z Rpan = fast_pan(x);
			for (int i = 0; i < n; ++i) {
				*Lout = *a * Lpan;
				*Rout = *b * Rpan;
				a += aStride;
				b += bStride;
				Lout += Loutstride;
				Rout += Routstride;
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z x = std::clamp(*c, -1., 1.);
				*Lout = *a * fast_pan(-x);
				*Rout = *b * fast_pan(x);
				a += aStride;
				b += bStride;
				c += cStride;
				Lout += Loutstride;
				Rout += Routstride;
			}
		}
		framesToFill -= n;
		_L.advance(n);
		_R.advance(n);
		_pos.advance(n);
	}
	if (mLeft->mOut) mLeft->produce(framesToFill);
	if (mRight->mOut) mRight->produce(framesToFill);
}

P<List> Balance2::createOutputs(Thread& th)
{
	mLeft = new Balance2Out(th, finite, this);
	mRight = new Balance2Out(th, finite, this);

	P<Gen> left = mLeft;
	P<Gen> right = mRight;

	P<List> s = new List(itemTypeV, 2);
	P<Array> a = s->mArray;
	a->add(new List(left));
	a->add(new List(right));

	return s;
}

static void bal2_(Thread& th, Prim* prim)
{
	V pos = th.popZIn("bal2 : pos");
	V R = th.popZIn("bal2 : right");
	V L = th.popZIn("bal2 : left");

	P<Balance2> bal = new Balance2(th, L, R, pos);

	P<List> s = bal->createOutputs(th);

	th.push(s);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Rot2Out;

struct Rot2 : public Object
{
	ZIn _L;
	ZIn _R;
	ZIn _pos;

	P<Rot2Out> mLeft;
	P<Rot2Out> mRight;

	Rot2(Thread& th, Arg inL, Arg inR, Arg inPos)
		: _L(inL), _R(inR), _pos(inPos)
	{
		finite = mostFinite(inL, inR, inPos);
	}

	P<List> createOutputs(Thread& th);

	virtual const char* TypeName() const override { return "Rot2"; }

	virtual void pull(Thread& th);
};

struct Rot2Out : public Gen
{
	P<Rot2> mRot2;

	Rot2Out(Thread& th, bool inFinite, P<Rot2> const& inRot2) : Gen(th, itemTypeZ, inFinite), mRot2(inRot2)
	{
	}

	virtual void norefs() override
	{
		mOut = nullptr;
		mRot2 = nullptr;
	}

	virtual const char* TypeName() const override { return "Rot2Out"; }

	virtual void pull(Thread& th) override
	{
		mRot2->pull(th);
	}

};

void Rot2::pull(Thread& th)
{
	int framesToFill = mLeft->mBlockSize;

	Z Sink = 0.;
	Z* Lout;
	Z* Rout;
	int Loutstride = 1;
	int Routstride = 1;

	if (mLeft->mOut) {
		Lout = mLeft->mOut->fulfillz(framesToFill);
	} else {
		Lout = &Sink;
		Loutstride = 0;
	}

	if (mRight->mOut) {
		Rout = mRight->mOut->fulfillz(framesToFill);
	} else {
		Rout = &Sink;
		Routstride = 0;
	}

	while (framesToFill) {
		Z *a, *b, *c;
		int n, aStride, bStride, cStride;
		n = framesToFill;
		if (_L(th, n, aStride, a) || _R(th, n, bStride, b) || _pos(th, n, cStride, c)) {
			mLeft->setDone();
			mRight->setDone();
			break;
		}

		if (cStride == 0) {
			if (_L.isZero()) {
				Z pos = .5 * *c;
				Z sn = -fast_sin1(pos);
				Z cs = fast_cos1(pos);
				for (int i = 0; i < n; ++i) {
					Z R = *b;
					*Lout = - R * sn;
					*Rout =   R * cs;
					b += bStride;
					Lout += Loutstride;
					Rout += Routstride;
				}
			} else if (_R.isZero()) {
				Z pos = .5 * *c;
				Z sn = -fast_sin1(pos);
				Z cs = fast_cos1(pos);
				for (int i = 0; i < n; ++i) {
					Z L = *a;
					*Lout = L * cs;
					*Rout = L * sn;
					a += aStride;
					Lout += Loutstride;
					Rout += Routstride;
				}
			} else {
				Z pos = .5 * *c;
				Z sn = -fast_sin1(pos);
				Z cs = fast_cos1(pos);
				for (int i = 0; i < n; ++i) {
					Z L = *a;
					Z R = *b;
					*Lout = L * cs - R * sn;
					*Rout = L * sn + R * cs;
					a += aStride;
					b += bStride;
					Lout += Loutstride;
					Rout += Routstride;
				}
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z L = *a;
				Z R = *b;
				Z pos = .5 * *c;
				Z sn = -fast_sin1(pos);
				Z cs = fast_cos1(pos);
				*Lout = L * cs - R * sn;
				*Rout = L * sn + R * cs;
				a += aStride;
				b += bStride;
				c += cStride;
				Lout += Loutstride;
				Rout += Routstride;
			}
		}
		framesToFill -= n;
		_L.advance(n);
		_R.advance(n);
		_pos.advance(n);
	}
	if (mLeft->mOut) mLeft->produce(framesToFill);
	if (mRight->mOut) mRight->produce(framesToFill);
}

P<List> Rot2::createOutputs(Thread& th)
{
	mLeft = new Rot2Out(th, finite, this);
	mRight = new Rot2Out(th, finite, this);

	P<Gen> left = mLeft;
	P<Gen> right = mRight;

	P<List> s = new List(itemTypeV, 2);
	P<Array> a = s->mArray;
	a->add(new List(left));
	a->add(new List(right));

	return s;
}

static void rot2_(Thread& th, Prim* prim)
{
	V pos = th.popZIn("rot2 : pos");
	V R = th.popZIn("rot2 : right");
	V L = th.popZIn("rot2 : left");

	P<Rot2> rot2 = new Rot2(th, L, R, pos);

	P<List> s = rot2->createOutputs(th);

	th.push(s);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Fade2 : public Gen
{
	ZIn _L;
	ZIn _R;
	ZIn _pos;

	Fade2(Thread& th, Arg inL, Arg inR, Arg inPos)
		: Gen(th, itemTypeZ, mostFinite(inL, inR, inPos)), _L(inL), _R(inR), _pos(inPos)
	{
	}

	virtual const char* TypeName() const override { return "Fade2"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);

		while (framesToFill) {
			Z *a, *b, *c;
			int n, aStride, bStride, cStride;
			n = framesToFill;
			if (_L(th, n, aStride, a) || _R(th, n, bStride, b) || _pos(th, n, cStride, c)) {
				setDone();
				break;
			}

			if (cStride == 0) {
				Z x = std::clamp(*c, -1., 1.);
				Z Lpan = fast_pan(-x);
				Z Rpan = fast_pan(x);
				for (int i = 0; i < n; ++i) {
					out[i] = *a * Lpan + *b * Rpan;
					a += aStride;
					b += bStride;
				}
			} else {
				for (int i = 0; i < n; ++i) {
					Z x = std::clamp(*c, -1., 1.);
					out[i] = *a * fast_pan(-x) + *b * fast_pan(x);
					a += aStride;
					b += bStride;
					c += cStride;
				}
			}
			framesToFill -= n;
			out += n;
			_L.advance(n);
			_R.advance(n);
			_pos.advance(n);
		}
		produce(framesToFill);
	}

};

static void fade2_(Thread& th, Prim* prim)
{
	V pos = th.popZIn("fade2 : pos");
	V R = th.popZIn("fade2 : right");
	V L = th.popZIn("fade2 : left");

	th.push(new List(new Fade2(th, L, R, pos)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Trig : public Gen
{
	ZIn _in;
	Z _prev;

	Trig(Thread& th, Arg in)
		: Gen(th, itemTypeZ, in.isFinite()), _in(in), _prev(0.)
	{
	}

	virtual const char* TypeName() const override { return "Trig"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z prev = _prev;
		while (framesToFill) {
			Z *in;
			int n, inStride;
			n = framesToFill;
			if (_in(th, n, inStride, in)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {

				Z cur = *in;
				out[i] = cur > 0. && prev <= 0. ? 1. : 0.;
				prev = cur;
				in += inStride;
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
		}

		_prev = prev;
		produce(framesToFill);
	}

};

struct NegTrig : public Gen
{
	ZIn _in;
	Z _prev;

	NegTrig(Thread& th, Arg in)
		: Gen(th, itemTypeZ, in.isFinite()), _in(in), _prev(-1.)
	{
	}

	virtual const char* TypeName() const override { return "NegTrig"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z prev = _prev;
		while (framesToFill) {
			Z *in;
			int n, inStride;
			n = framesToFill;
			if (_in(th, n, inStride, in)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {

				Z cur = *in;
				out[i] = cur >= 0. && prev < 0. ? 1. : 0.;
				prev = cur;
				in += inStride;
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
		}

		_prev = prev;
		produce(framesToFill);
	}

};

static void tr_(Thread& th, Prim* prim)
{
	V in   = th.popZIn("tr : in");

	th.push(new List(new Trig(th, in)));
}

static void ntr_(Thread& th, Prim* prim)
{
	V in   = th.popZIn("ntr : in");

	th.push(new List(new NegTrig(th, in)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Gate : public TwoInputUGen<Gate>
{
	Z phase;
	Z freq;
	Gate(Thread& th, Arg trig, Arg hold)
		: TwoInputUGen<Gate>(th, trig, hold), phase(INFINITY), freq(th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "Gate"; }

	void calc(int n, Z* out, Z* trig, Z* hold, int trigStride, int holdStride)
	{
		for (int i = 0; i < n; ++i) {
			Z t = *trig;
			if (t > 0.) {
				phase = 0.;
			}
			out[i] = phase < *hold ? 1. : 0.;
			phase += freq;
			trig += trigStride;
			hold += holdStride;
		}
	}
};

static void gate_(Thread& th, Prim* prim)
{
	V hold = th.popZIn("gate : hold");
	V in   = th.popZIn("gate : in");

	th.push(new List(new Gate(th, in, hold)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct SampleAndHold : public Gen
{
	ZIn _in;
	ZIn _tr;
	Z _val;

	SampleAndHold(Thread& th, Arg in, Arg tr)
		: Gen(th, itemTypeZ, mostFinite(in, tr)), _in(in), _tr(tr), _val(0.)
	{
	}

	virtual const char* TypeName() const override { return "SampleAndHold"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z val = _val;
		while (framesToFill) {
			Z *in, *tr;
			int n, inStride, trStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _tr(th, n, trStride, tr)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				if (*tr > 0.) val = *in;
				out[i] = val;
				in += inStride;
				tr += trStride;
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_tr.advance(n);
		}

		_val = val;
		produce(framesToFill);
	}

};

static void sah_(Thread& th, Prim* prim)
{
	V trigger   = th.popZIn("sah : trigger");
	V in   = th.popZIn("sah : in");

	th.push(new List(new SampleAndHold(th, in, trigger)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Sequencer : public Gen
{
	BothIn _in;
	ZIn _tr;
	Z _val;

	Sequencer(Thread& th, Arg in, Arg tr)
		: Gen(th, itemTypeZ, mostFinite(in, tr)), _in(in), _tr(tr), _val(0.)
	{
	}

	virtual const char* TypeName() const override { return "Sequencer"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z val = _val;
		while (framesToFill) {
			Z *tr;
			int n, trStride;
			n = framesToFill;
			if (_tr(th, n, trStride, tr)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				if (*tr > 0.) {
					Z z;
					if (_in.onez(th, z)) {
						setDone();
						produce(framesToFill - i);
						return;
					}
					val = z;
				}
				out[i] = val;
				tr += trStride;
			}

			framesToFill -= n;
			out += n;
			_tr.advance(n);
		}

		_val = val;
		produce(framesToFill);
	}

};

static void seq_(Thread& th, Prim* prim)
{
	V trigger   = th.popZIn("seq : trigger");
	V in   = th.pop();

	th.push(new List(new Sequencer(th, in, trigger)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct ImpulseSequencer : public Gen
{
	BothIn _in;
	ZIn _tr;

	ImpulseSequencer(Thread& th, Arg in, Arg tr)
		: Gen(th, itemTypeZ, mostFinite(in, tr)), _in(in), _tr(tr)
	{
	}

	virtual const char* TypeName() const override { return "ImpulseSequencer"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			Z *tr;
			int n, trStride;
			n = framesToFill;
			if (_tr(th, n, trStride, tr)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				if (*tr > 0.) {
					Z z;
					if (_in.onez(th, z)) {
						setDone();
						produce(framesToFill - i);
						return;
					}
					out[i] = z;
				} else {
					out[i] = 0.;
				}
				tr += trStride;
			}

			framesToFill -= n;
			out += n;
			_tr.advance(n);
		}

		produce(framesToFill);
	}

};

static void iseq_(Thread& th, Prim* prim)
{
	V trigger   = th.popZIn("iseq : trigger");
	V in   = th.pop();

	th.push(new List(new ImpulseSequencer(th, in, trigger)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct PulseDivider : public Gen
{
	ZIn _tr;
	ZIn _div;
	Z _count;

	PulseDivider(Thread& th, Arg tr, Arg div, Z start)
		: Gen(th, itemTypeZ, mostFinite(tr, div)), _tr(tr), _div(div), _count(start - 1.)
	{
	}

	virtual const char* TypeName() const override { return "PulseDivider"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			Z *tr, *div;
			int n, trStride, divStride;
			n = framesToFill;
			if (_tr(th, n, trStride, tr) || _div(th, n, divStride, div)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				if (*tr > 0.) {
					_count += 1.;
					Z idiv = floor(*div + .5);
					if (_count >= idiv) {
						_count -= idiv;
					}
					out[i] = _count == 0. ? *tr : 0.;
				} else out[i] = 0.;
				tr += trStride;
				div += divStride;
			}

			framesToFill -= n;
			out += n;
			_tr.advance(n);
			_div.advance(n);
		}

		produce(framesToFill);
	}

};

static void pdiv_(Thread& th, Prim* prim)
{
	Z start  = th.popFloat("pdiv : istart");
	V div  = th.popZIn("pdiv : n");
	V in   = th.popZIn("pdiv : in");

	th.push(new List(new PulseDivider(th, in, div, start)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Clip : public ThreeInputUGen<Clip>
{

	Clip(Thread& th, Arg a, Arg b, Arg c) : ThreeInputUGen<Clip>(th, a, b, c)
	{
	}

	virtual const char* TypeName() const override { return "Clip"; }

	void calc(int n, Z* out, Z* a, Z* b, Z* c, int aStride, int bStride, int cStride)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = std::clamp(*a, *b, *c);
			a += aStride;
			b += bStride;
			c += cStride;
		}
	}
};

struct Wrap : public ThreeInputUGen<Wrap>
{

	Wrap(Thread& th, Arg a, Arg b, Arg c) : ThreeInputUGen<Wrap>(th, a, b, c)
	{
	}

	virtual const char* TypeName() const override { return "Wrap"; }

	void calc(int n, Z* out, Z* a, Z* b, Z* c, int aStride, int bStride, int cStride)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = sc_wrap(*a, *b, *c);
			a += aStride;
			b += bStride;
			c += cStride;
		}
	}
};

struct Fold : public ThreeInputUGen<Fold>
{

	Fold(Thread& th, Arg a, Arg b, Arg c) : ThreeInputUGen<Fold>(th, a, b, c)
	{
	}

	virtual const char* TypeName() const override { return "Fold"; }

	void calc(int n, Z* out, Z* a, Z* b, Z* c, int aStride, int bStride, int cStride)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = sc_fold(*a, *b, *c);
			a += aStride;
			b += bStride;
			c += cStride;
		}
	}
};

struct IWrap : public ThreeInputUGen<IWrap>
{

	IWrap(Thread& th, Arg a, Arg b, Arg c) : ThreeInputUGen<IWrap>(th, a, b, c)
	{
	}

	virtual const char* TypeName() const override { return "IWrap"; }

	void calc(int n, Z* out, Z* a, Z* b, Z* c, int aStride, int bStride, int cStride)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = sc_iwrap(*a, *b, *c);
			a += aStride;
			b += bStride;
			c += cStride;
		}
	}
};

struct IFold : public ThreeInputUGen<IFold>
{

	IFold(Thread& th, Arg a, Arg b, Arg c) : ThreeInputUGen<IFold>(th, a, b, c)
	{
	}

	virtual const char* TypeName() const override { return "IFold"; }

	void calc(int n, Z* out, Z* a, Z* b, Z* c, int aStride, int bStride, int cStride)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = sc_ifold(*a, *b, *c);
			a += aStride;
			b += bStride;
			c += cStride;
		}
	}
};

static void clip_(Thread& th, Prim* prim)
{
	V hi   = th.popZIn("clip : hi");
	V lo   = th.popZIn("clip : lo");
	V in   = th.popZIn("clip : in");

	if (in.isReal() && lo.isReal() && hi.isReal()) {
		th.push(std::clamp(in.f, lo.f, hi.f));
	} else {
		th.push(new List(new Clip(th, in, lo, hi)));
	}
}

static void wrap_(Thread& th, Prim* prim)
{
	V hi   = th.popZIn("wrap : hi");
	V lo   = th.popZIn("wrap : lo");
	V in   = th.popZIn("wrap : in");

	if (in.isReal() && lo.isReal() && hi.isReal()) {
		th.push(sc_wrap(in.f, lo.f, hi.f));
	} else {
		th.push(new List(new Wrap(th, in, lo, hi)));
	}
}

static void fold_(Thread& th, Prim* prim)
{
	V hi   = th.popZIn("fold : hi");
	V lo   = th.popZIn("fold : lo");
	V in   = th.popZIn("fold : in");

	if (in.isReal() && lo.isReal() && hi.isReal()) {
		th.push(sc_fold(in.f, lo.f, hi.f));
	} else {
		th.push(new List(new Fold(th, in, lo, hi)));
	}
}

static void iwrap_(Thread& th, Prim* prim)
{
	V hi   = th.popZIn("iwrap : hi");
	V lo   = th.popZIn("iwrap : lo");
	V in   = th.popZIn("iwrap : in");

	if (in.isReal() && lo.isReal() && hi.isReal()) {
		th.push(sc_iwrap(in.f, lo.f, hi.f));
	} else {
		th.push(new List(new IWrap(th, in, lo, hi)));
	}
}

static void ifold_(Thread& th, Prim* prim)
{
	V hi   = th.popZIn("ifold : hi");
	V lo   = th.popZIn("ifold : lo");
	V in   = th.popZIn("ifold : in");

	if (in.isReal() && lo.isReal() && hi.isReal()) {
		th.push(sc_ifold(in.f, lo.f, hi.f));
	} else {
		th.push(new List(new IFold(th, in, lo, hi)));
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <Carbon/Carbon.h>

struct MouseUGenGlobalState {
	float mouseX, mouseY;
	bool mouseButton;
} gMouseUGenGlobals;

static void* gstate_update_func(void* arg)
{
	MouseUGenGlobalState* gstate = &gMouseUGenGlobals;

	CGDirectDisplayID display = kCGDirectMainDisplay; // to grab the main display ID
	CGRect bounds = CGDisplayBounds(display);
	float rscreenWidth = 1. / bounds.size.width;
	float rscreenHeight = 1. / bounds.size.height;
	for (;;) {
		HIPoint point;
		HICoordinateSpace space = 2;
		HIGetMousePosition(space, nullptr, &point);

		gstate->mouseX = point.x * rscreenWidth; //(float)p.h * rscreenWidth;
		gstate->mouseY = 1. - point.y * rscreenHeight; //(float)p.v * rscreenHeight;
		gstate->mouseButton = GetCurrentButtonState();
		usleep(17000);
	}

	return 0;
}

Z gMouseLagTime = .1;
Z gMouseLagMul = log001 / gMouseLagTime;

struct MouseX : public TwoInputUGen<MouseX>
{
	Z _b1;
	Z _y1;
	bool _once;

	MouseX(Thread& th, Arg lo, Arg hi) : TwoInputUGen<MouseX>(th, lo, hi), _b1(1. + gMouseLagMul * th.rate.invSampleRate), _once(true)
	{
	}

	virtual const char* TypeName() const override { return "MouseX"; }

	void calc(int n, Z* out, Z* lo, Z* hi, int loStride, int hiStride)
	{
		if (_once) {
			_once = false;
			_y1 = *lo + gMouseUGenGlobals.mouseX * (*hi - *lo);
		}
		Z y1 = _y1;
		Z b1 = _b1;
		for (int i = 0; i < n; ++i) {
			Z y0 = *lo + gMouseUGenGlobals.mouseX * (*hi - *lo);
			out[i] = y1 = y0 + b1 * (y1 - y0);
			lo += loStride;
			hi += hiStride;
		}
		_y1 = y1;
	}
};

struct MouseY : public TwoInputUGen<MouseY>
{
	Z _b1;
	Z _y1;
	bool _once;

	MouseY(Thread& th, Arg lo, Arg hi) : TwoInputUGen<MouseY>(th, lo, hi), _b1(1. + gMouseLagMul * th.rate.invSampleRate), _once(true)
	{
	}

	virtual const char* TypeName() const override { return "MouseY"; }

	void calc(int n, Z* out, Z* lo, Z* hi, int loStride, int hiStride)
	{
		if (_once) {
			_once = false;
			_y1 = *lo + gMouseUGenGlobals.mouseY * (*hi - *lo);
		}
		Z y1 = _y1;
		Z b1 = _b1;
		for (int i = 0; i < n; ++i) {
			Z y0 = *lo + gMouseUGenGlobals.mouseY * (*hi - *lo);
			out[i] = y1 = y0 + b1 * (y1 - y0);
			lo += loStride;
			hi += hiStride;
		}
		_y1 = y1;
	}
};

struct ExpMouseX : public TwoInputUGen<ExpMouseX>
{
	Z _b1;
	Z _y1;
	bool _once;

	ExpMouseX(Thread& th, Arg lo, Arg hi) : TwoInputUGen<ExpMouseX>(th, lo, hi), _b1(1. + gMouseLagMul * th.rate.invSampleRate), _once(true)
	{
	}

	virtual const char* TypeName() const override { return "MouseX"; }

	void calc(int n, Z* out, Z* lo, Z* hi, int loStride, int hiStride)
	{
		if (_once) {
			_once = false;
			_y1 = *lo * pow(*hi / *lo, gMouseUGenGlobals.mouseX);
		}
		Z y1 = _y1;
		Z b1 = _b1;
		for (int i = 0; i < n; ++i) {
			Z y0 = *lo * pow(*hi / *lo, gMouseUGenGlobals.mouseX);
			out[i] = y1 = y0 + b1 * (y1 - y0);
			lo += loStride;
			hi += hiStride;
		}
		_y1 = y1;
	}
};

struct ExpMouseY : public TwoInputUGen<ExpMouseY>
{
	Z _b1;
	Z _y1;
	bool _once;

	ExpMouseY(Thread& th, Arg lo, Arg hi) : TwoInputUGen<ExpMouseY>(th, lo, hi), _b1(1. + gMouseLagMul * th.rate.invSampleRate), _once(true)
	{
	}

	virtual const char* TypeName() const override { return "MouseY"; }

	void calc(int n, Z* out, Z* lo, Z* hi, int loStride, int hiStride)
	{
		if (_once) {
			_once = false;
			_y1 = *lo * pow(*hi / *lo, gMouseUGenGlobals.mouseY);
		}
		Z y1 = _y1;
		Z b1 = _b1;
		for (int i = 0; i < n; ++i) {
			Z y0 = *lo * pow(*hi / *lo, gMouseUGenGlobals.mouseY);
			out[i] = y1 = y0 + b1 * (y1 - y0);
			lo += loStride;
			hi += hiStride;
		}
		_y1 = y1;
	}
};

static void mousex_(Thread& th, Prim* prim)
{
	V hi   = th.popZIn("mousex : hi");
	V lo   = th.popZIn("mousex : lo");

	th.push(new List(new MouseX(th, lo, hi)));
}

static void mousey_(Thread& th, Prim* prim)
{
	V hi   = th.popZIn("mousey : hi");
	V lo   = th.popZIn("mousey : lo");

	th.push(new List(new MouseY(th, lo, hi)));
}

static void xmousex_(Thread& th, Prim* prim)
{
	V hi   = th.popZIn("xmousex : hi");
	V lo   = th.popZIn("xmousex : lo");

	th.push(new List(new ExpMouseX(th, lo, hi)));
}

static void xmousey_(Thread& th, Prim* prim)
{
	V hi   = th.popZIn("xmousey : hi");
	V lo   = th.popZIn("xmousey : lo");

	th.push(new List(new ExpMouseY(th, lo, hi)));
}

static void mousex1_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("mousex1 : hi");
	Z lo   = th.popFloat("mousex1 : lo");

	Z z = lo + gMouseUGenGlobals.mouseX * (hi - lo);
	th.push(z);
}

static void mousey1_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("mousey1 : hi");
	Z lo   = th.popFloat("mousey1 : lo");

	Z z = lo + gMouseUGenGlobals.mouseY * (hi - lo);
	th.push(z);
}

static void xmousex1_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("xmousex1 : hi");
	Z lo   = th.popFloat("xmousex1 : lo");

	Z z = lo * pow(hi / lo, gMouseUGenGlobals.mouseX);
	th.push(z);
}

static void xmousey1_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("xmousey1 : hi");
	Z lo   = th.popFloat("xmousey1 : lo");

	Z z = lo * pow(hi / lo, gMouseUGenGlobals.mouseY);
	th.push(z);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define DEF(NAME, N, HELP) 	vm.def(#NAME, N, 1, NAME##_, HELP);
#define DEFMCX(NAME, N, HELP) 	vm.defmcx(#NAME, N, NAME##_, HELP);
#define DEFAM(NAME, MASK, HELP) 	vm.defautomap(#NAME, #MASK, NAME##_, HELP);

void AddFilterUGenOps();
void AddOscilUGenOps();
void AddDelayUGenOps();

void AddUGenOps()
{
	s_tempo = getsym("tempo");
	s_dt = getsym("dt");
	s_out = getsym("out");

	pthread_t mouseListenThread;
	pthread_create (&mouseListenThread, nullptr, gstate_update_func, (void*)0);

	vm.addBifHelp("\n*** unit generators ***");
	vm.defmcx("*+", 3, madd_, "(a b c --> out) multiply add. a b * c +");

    AddOscilUGenOps();
    AddFilterUGenOps();
    AddDelayUGenOps();

	vm.addBifHelp("\n*** plugs ***");

	vm.addBifHelp("\n*** control rate subgraphs ***");
	gK2A = automap("zk", 2, new Prim(k2a_, V(0.), 2, 1, "", ""), "", "");
	gK2AC = automap("zk", 2, new Prim(k2ac_, V(0.), 2, 1, "", ""), "", "");
	DEF(kr, 2, "(fun n --> out) evaluates fun with the current sample rate divided by n, then linearly upsamples all returned signals by n.")
	DEF(krc, 2, "(fun n --> out) evaluates fun with the current sample rate divided by n, then cubically upsamples all returned signals by n.")

	vm.addBifHelp("\n*** control function unit generators ***");
	DEFAM(imps, aaz, "(values durs rate --> out) single sample impulses.");
	DEFAM(steps, aaz, "(values durs rate --> out) steps");
	DEFAM(gates, aaaz, "(values durs holds rate --> out) gates");
	DEFAM(lines, aaz, "(values durs rate --> out) lines");
	DEFAM(xlines, aaz, "(values durs rate --> out) exponential lines");
	DEFAM(cubics, az, "(values rate --> out) cubic splines");
	DEFAM(curves, aaaz, "(values curvatures durs rate --> out) curves.");

	vm.addBifHelp("\n*** random control unit generators ***");
	DEFMCX(lfnoise0, 1, "(freq --> out) step noise source.");
	DEFMCX(lfnoise1, 1, "(freq --> out) ramp noise source.");
	DEFMCX(lfnoise3, 1, "(freq --> out) cubic spline noise source.");

	vm.addBifHelp("\n*** tempo unit generators ***");
	DEFAM(tempo, az, "([bps dur bps dur ...] rate --> out) returns a signal of tempo vs time given a list of interleaved tempos (in beats per second) and durations (in beats).");
	DEFAM(beats, z, "(tempo --> beats) integrates a tempo signal to produce a signal of the time in beats.");

	vm.addBifHelp("\n*** envelope unit generators ***");
	vm.addBifHelp("\nFor asr, adsr, dadsr, dahdsr envelopes, the arguments are as follows:");
	vm.addBifHelp("   delay - a time in seconds. a period of time before the attack segment where the amplitude is zero.");
	vm.addBifHelp("   attack - a time in seconds to rise from zero to the level specified by the amp argument.");
	vm.addBifHelp("   hold - a time in seconds to hold at the level specified by the amp argument.");
	vm.addBifHelp("   delay - a time in seconds to fall from amp to the sustain level.");
	vm.addBifHelp("   sustain - a level from zero to one which is multiplied by the amp argument. The envelope holds at this level until released.");
	vm.addBifHelp("   release - a time in seconds to fall from the current level to zero. A release begins whenever the beat time (the integral of tempo), exceeds dur.");
	vm.addBifHelp("   amp - an amplitude that scales the peak and sustain levels of the envelope.");
	vm.addBifHelp("   dur - a time in beats to release the envelope.");
	vm.addBifHelp("   tempo - a signal giving the tempo in beats per second versus time.");
	vm.addBifHelp("");

	DEFAM(adsr, akkz, "([attack decay sustain release] amp dur tempo --> envelope) an envelope generator.")
	DEFAM(dadsr, akkz, "([delay attack decay sustain release] amp dur tempo --> envelope) an envelope generator.")
	DEFAM(dahdsr, akkz, "([delay attack hold decay sustain release] amp dur tempo --> envelope) an envelope generator.")
	vm.addBifHelp("");

	DEFAM(endfade, zkkkk, "(in startupTime holdTime fadeTime threshold --> out) after startupTime has elapsed, fade out the sound when peak amplitude has dropped below threshold for more than the holdTime.");
	DEFAM(fadeout, zkk, "(in sustainTime fadeTime --> out) fadeout after sustain.");
	DEFAM(fadein, zk, "(in fadeTime --> out) fade in.");
	DEFAM(parenv, k, "(dur --> out) parabolic envelope. 1-x^2 for x from -1 to 1")
	DEFAM(quadenv, k, "(dur --> out) 4th order envelope. 1-x^4 for x from -1 to 1")
	DEFAM(octenv, k, "(dur --> out) 8th order envelope. 1-x^8 for x from -1 to 1")
	DEFAM(trienv, k, "(dur --> out) triangular envelope. 1-|x| for x from -1 to 1")
	DEFAM(tri2env, k, "(dur --> out) triangle squared envelope. (1-|x|)^2 for x from -1 to 1")
	DEFAM(trapezenv, k, "(dur --> out) trapezoidal envelope. (2 - |x-.5| - |x+.5|) for x from -1 to 1")
	DEFAM(trapez2env, k, "(dur --> out) trapezoid squared envelope. (2 - |x-.5| - |x+.5|)^2 for x from -1 to 1")

	DEFAM(cosenv, k, "(dur --> out) cosine envelope.")
	DEFAM(hanenv, k, "(dur --> out) hanning envelope.")
	DEFAM(han2env, k, "(dur --> out) hanning squared envelope.")
	DEFAM(gaussenv, kk, "(dur width --> out) gaussian envelope. exp(x^2/(-2*width^2)) for x from -1 to 1")

	DEFAM(tsig, zza, "(trig signal amp --> out) trigger a signal.")

	DEFAM(tparenv, zaa, "(trig dur amp --> out) triggered parabolic envelope. 1-x^2 for x from -1 to 1")
	DEFAM(tquadenv, zaa, "(trig dur amp --> out) triggered 4th order envelope. 1-x^4 for x from -1 to 1")
	DEFAM(toctenv, zaa, "(trig dur amp --> out) triggered 8th order envelope. 1-x^8 for x from -1 to 1")
	DEFAM(ttrienv, zaa, "(trig dur amp --> out) triggered triangular envelope. 1-|x| for x from -1 to 1")
	DEFAM(ttri2env, zaa, "(trig dur amp --> out) triggered triangle squared envelope. (1-|x|)^2 for x from -1 to 1")
	DEFAM(ttrapezenv, zaa, "(trig dur amp --> out) triggered trapezoidal envelope. (2 - |x-.5| - |x+.5|) for x from -1 to 1")
	DEFAM(ttrapez2env, zaa, "(trig dur amp --> out) triggered trapezoid squared envelope. (2 - |x-.5| - |x+.5|)^2 for x from -1 to 1")

	DEFAM(tcosenv, zaa, "(trig dur amp --> out) triggered cosine envelope.")
	DEFAM(thanenv, zaa, "(trig dur amp --> out) triggered hanning envelope.")
	DEFAM(than2env, zaa, "(trig dur amp --> out) triggered hanning squared envelope.")

	vm.addBifHelp("\n*** spawn unit generators ***");
	DEF(ola, 4, "(sounds hops rate numChannels --> out) overlap add. This is the basic operator for polyphony. ")

	vm.addBifHelp("\n*** pause unit generator ***");
	DEFMCX(pause, 2, "(in amp --> out) pauses the input when amp is <= 0, otherwise in is multiplied by amp.")

	vm.addBifHelp("\n*** panner unit generators ***");
	DEFAM(itd, zzk, "(in pan maxdelay --> out) interaural time delay.");
	DEFMCX(pan2, 2, "(in pos --> [left right]) stereo pan. pos 0 is center. pos -1 is full left, pos +1 is full right.")
	DEFMCX(rot2, 3, "(left right pos --> [left right]) stereo rotation. pos 0 is no rotation, +/-1 is 180 degrees, -.5 is -90 degrees, +.5 is +90 degrees.")
	DEFMCX(bal2, 3, "(left right pos --> [left right]) stereo balance control. pos 0 is center. pos -1 is full left, pos +1 is full right.")
	DEFMCX(fade2, 3, "(left right pos --> out) cross fade between two inputs. pos 0 is equal mix. pos -1 is all left, pos +1 is all right.")

	vm.addBifHelp("\n*** trigger unit generators ***");
	DEFMCX(tr, 1, "(in --> out) transitions from nonpositive to positive become single sample impulses.")
	DEFMCX(ntr, 1, "(in --> out) transitions from negative to nonnegative become single sample impulses.")
	DEFMCX(gate, 1, "(in hold --> out) outputs 1 for hold seconds after each trigger, else outputs zero.")
	DEFMCX(sah, 2, "(in trigger --> out) sample and hold")
	DEFAM(seq, az, "(in trigger --> out) pulls one value from the input for each trigger. output sustains at that level until the next trigger.")
	DEFAM(iseq, az, "(in trigger --> out) pulls one value from the input for each trigger. outputs that value for one sample. outputs zero when there is no trigger.")
	DEFMCX(pdiv, 3, "(in n istart --> out) pulse divider. outputs one impulse from the output for each n impulses in the input. istart is an offset. istart = 0 outputs a pulse on the first input pulse.")

	vm.addBifHelp("\n*** bounds unit generators ***");
	DEFMCX(clip, 3, "(in lo hi --> out) constrain the input to the bounds by clipping.")
	DEFMCX(wrap, 3, "(in lo hi --> out) constrain the input to the bounds by wrapping.")
	DEFMCX(fold, 3, "(in lo hi --> out) constrain the input to the bounds by folding at the edges.")
	DEFMCX(iwrap, 3, "(in lo hi --> out) constrain the input to the bounds by wrapping. all inputs treated as integers.")
	DEFMCX(ifold, 3, "(in lo hi --> out) constrain the input to the bounds by folding at the edges. all inputs treated as integers.")

	vm.addBifHelp("\n*** mouse control unit generators ***");
	DEFMCX(mousex, 2, "(lo hi --> out) returns a signal of the X coordinate of the mouse mapped to the linear range lo to hi.");
	DEFMCX(mousey, 2, "(lo hi --> out) returns a signal of the Y coordinate of the mouse mapped to the linear range lo to hi.");
	DEFMCX(xmousex, 2, "(lo hi --> out) returns a signal of the X coordinate of the mouse mapped to the exponential range lo to hi.");
	DEFMCX(xmousey, 2, "(lo hi --> out) returns a signal of the Y coordinate of the mouse mapped to the exponential range lo to hi.");

	DEFMCX(mousex1, 2, "(lo hi --> out) returns the current value of the X coordinate of the mouse mapped to the linear range lo to hi.");
	DEFMCX(mousey1, 2, "(lo hi --> out) returns the current value of the Y coordinate of the mouse mapped to the linear range lo to hi.");
	DEFMCX(xmousex1, 2, "(lo hi --> out) returns the current value of the X coordinate of the mouse mapped to the exponential range lo to hi.");
	DEFMCX(xmousey1, 2, "(lo hi --> out) returns the current value of the Y coordinate of the mouse mapped to the exponential range lo to hi.");
}

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "DelayUGens.hpp"
#include "UGen.hpp"

#include "VM.hpp"
#include "clz.hpp"
#include "primes.hpp"
#include <cmath>
#include <float.h>
#include <vector>
#include <algorithm>
#include <Accelerate/Accelerate.h>

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

class DelayN : public Gen
{
	ZIn in_;
	ZIn delay_;
	Z maxdelay_;
	int32_t bufSize;
	int32_t bufMask;
	int32_t bufPos;
	Z* buf;
	Z sr;
public:

	DelayN(Thread& th, Arg in, Arg delay, Z maxdelay) : Gen(th, itemTypeZ, false), in_(in), delay_(delay), maxdelay_(maxdelay)
	{
		sr = th.rate.sampleRate;
		bufSize = NEXTPOWEROFTWO((int32_t)ceil(sr * maxdelay));
		bufMask = bufSize - 1;
		bufPos = 0;
		buf = (Z*)calloc(bufSize, sizeof(Z));
	}

	~DelayN() { free(buf); }

	virtual const char* TypeName() const override { return "DelayN"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int inStride, delayStride;
			Z *in, *delay;
			if (in_(th, n, inStride, in) || delay_(th, n, delayStride, delay)) {
				setDone();
				break;
			} else {
                for (int i = 0; i < n; ++i) {
					Z zdelay = *delay;
					zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
                    int32_t offset = std::max(1,(int32_t)floor(zdelay * sr + .5));
                    out[i] = buf[(bufPos-offset) & bufMask];
                    buf[bufPos & bufMask] = *in;
                    in += inStride;
                    delay += delayStride;
                    ++bufPos;
                }
				in_.advance(n);
				delay_.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

static void delayn_(Thread& th, Prim* prim)
{
	Z maxdelay = th.popFloat("delayn : maxdelay");
	V delay = th.popZIn("delayn : delay");
	V in = th.popZIn("delayn : in");

	if (maxdelay == 0.) {
		if (delay.isReal()) {
			maxdelay = delay.f;
		} else {
			post("delayn : maxdelay is zero and delay is a signal\n");
			throw errFailed;
		}
	}

	th.push(new List(new DelayN(th, in, delay, maxdelay)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

class DelayL : public Gen
{
	ZIn in_;
	ZIn delay_;
	Z maxdelay_;
	int32_t bufSize;
	int32_t bufMask;
	int32_t bufPos;
	Z* buf;
	Z sr;
public:

	DelayL(Thread& th, Arg in, Arg delay, Z maxdelay) : Gen(th, itemTypeZ, false), in_(in), delay_(delay), maxdelay_(maxdelay)
	{
		sr = th.rate.sampleRate;
		bufSize = NEXTPOWEROFTWO((int32_t)ceil(sr * maxdelay));
		bufMask = bufSize - 1;
		bufPos = 0;
		buf = (Z*)calloc(bufSize, sizeof(Z));
	}

	~DelayL() { free(buf); }

	virtual const char* TypeName() const override { return "DelayL"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int inStride, delayStride;
			Z *in, *delay;
			if (in_(th, n, inStride, in) || delay_(th, n, delayStride, delay)) {
				setDone();
				break;
			} else {
                if (delayStride == 0) {
					Z zdelay = *delay;
					zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
                    Z fpos = std::max(1., zdelay * sr);
                    Z ipos = floor(fpos);
                    Z frac = fpos - ipos;
                    int32_t offset = (int32_t)ipos;
                    for (int i = 0; i < n; ++i) {
                        int32_t offset2 = bufPos-offset;
                        Z a = buf[(offset2) & bufMask];
                        Z b = buf[(offset2-1) & bufMask];
                        out[i] = a + frac * (b - a);
                        buf[bufPos & bufMask] = *in;
                        in += inStride;
                        ++bufPos;
                    }
                } else {
                    for (int i = 0; i < n; ++i) {
						Z zdelay = *delay;
						zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
                        Z fpos = std::max(1., zdelay * sr);
                        Z ipos = floor(fpos);
                        Z frac = fpos - ipos;
                        int32_t offset = bufPos-(int32_t)ipos;
                        Z a = buf[(offset) & bufMask];
                        Z b = buf[(offset-1) & bufMask];
                        out[i] = a + frac * (b - a);
                        buf[bufPos & bufMask] = *in;
                        in += inStride;
                        delay += delayStride;
                        ++bufPos;
                    }
                }
				in_.advance(n);
				delay_.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

static void delayl_(Thread& th, Prim* prim)
{
	Z maxdelay = th.popFloat("delayl : maxdelay");
	V delay = th.popZIn("delayl : delay");
	V in = th.popZIn("delayl : in");

	if (maxdelay == 0.) {
		if (delay.isReal()) {
			maxdelay = delay.f;
		} else {
			post("delayl : maxdelay is zero and delay is a signal\n");
			throw errFailed;
		}
	}

	th.push(new List(new DelayL(th, in, delay, maxdelay)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

class DelayC : public Gen
{
	ZIn in_;
	ZIn delay_;
	Z maxdelay_;
	int32_t bufSize;
	int32_t bufMask;
	int32_t bufPos;
	Z* buf;
	Z sr;
public:

	DelayC(Thread& th, Arg in, Arg delay, Z maxdelay) : Gen(th, itemTypeZ, false), in_(in), delay_(delay), maxdelay_(maxdelay)
	{
		sr = th.rate.sampleRate;
		bufSize = NEXTPOWEROFTWO((int32_t)ceil(sr * maxdelay));
		bufMask = bufSize - 1;
		bufPos = 0;
		buf = (Z*)calloc(bufSize, sizeof(Z));
	}

	~DelayC() { free(buf); }

	virtual const char* TypeName() const override { return "DelayC"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int inStride, delayStride;
			Z *in, *delay;
			if (in_(th, n, inStride, in) || delay_(th, n, delayStride, delay)) {
				setDone();
				break;
			} else {
                if (delayStride == 0) {
					Z zdelay = *delay;
					zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
                    Z fpos = std::max(2., zdelay * sr);
                    Z ipos = floor(fpos);
                    Z frac = fpos - ipos;
                    int32_t offset = (int32_t)ipos;
                    for (int i = 0; i < n; ++i) {
                        int32_t offset2 = bufPos-offset;
                        Z a = buf[(offset2+1) & bufMask];
                        Z b = buf[(offset2  ) & bufMask];
                        Z c = buf[(offset2-1) & bufMask];
                        Z d = buf[(offset2-2) & bufMask];
                        out[i] = lagrangeInterpolate(frac, a, b, c, d);
                        buf[bufPos & bufMask] = *in;
                        in += inStride;
                        ++bufPos;
                    }
                } else {
                    for (int i = 0; i < n; ++i) {
						Z zdelay = *delay;
						zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
                        Z fpos = std::max(2., zdelay * sr);
                        Z ipos = floor(fpos);
                        Z frac = fpos - ipos;
                        int32_t offset = bufPos-(int32_t)ipos;
                        Z a = buf[(offset+1) & bufMask];
                        Z b = buf[(offset  ) & bufMask];
                        Z c = buf[(offset-1) & bufMask];
                        Z d = buf[(offset-2) & bufMask];
                        out[i] = lagrangeInterpolate(frac, a, b, c, d);
                        buf[bufPos & bufMask] = *in;
                        in += inStride;
                        delay += delayStride;
                        ++bufPos;
                    }
                }
				in_.advance(n);
				delay_.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

static void delayc_(Thread& th, Prim* prim)
{
	Z maxdelay = th.popFloat("delayc : maxdelay");
	V delay = th.popZIn("delayc : delay");
	V in = th.popZIn("delayc : in");

	if (maxdelay == 0.) {
		if (delay.isReal()) {
			maxdelay = delay.f;
		} else {
			post("delayc : maxdelay is zero and delay is a signal\n");
			throw errFailed;
		}
	}

	th.push(new List(new DelayC(th, in, delay, maxdelay)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

class Flange : public Gen
{
	ZIn in_;
	ZIn delay_;
	Z maxdelay_;
	int32_t bufSize;
	int32_t bufMask;
	int32_t bufPos;
	int32_t half;
    Z fhalf;
	Z* buf;
	Z sr;
public:

	Flange(Thread& th, Arg in, Arg delay, Z maxdelay) : Gen(th, itemTypeZ, false), in_(in), delay_(delay), maxdelay_(maxdelay)
	{
		sr = th.rate.sampleRate;
		fhalf = ceil(sr * maxdelay + .5);
		half = (int32_t)fhalf;
		bufSize = NEXTPOWEROFTWO(2 * half);
		bufMask = bufSize - 1;
		bufPos = 0;
		buf = (Z*)calloc(bufSize, sizeof(Z));
	}

	~Flange() { free(buf); }

	virtual const char* TypeName() const override { return "Flange"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int inStride, delayStride;
			Z *in, *delay;
			if (in_(th, n, inStride, in) || delay_(th, n, delayStride, delay)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					Z zdelay = *delay;
					zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
					Z fpos = std::max(2., zdelay * sr + fhalf);
					Z ipos = floor(fpos);
					Z frac = fpos - ipos;
					int32_t offset = bufPos-(int32_t)ipos;
					Z a = buf[(offset+1) & bufMask];
					Z b = buf[(offset  ) & bufMask];
					Z c = buf[(offset-1) & bufMask];
					Z d = buf[(offset-2) & bufMask];
					Z zin = buf[(bufPos-half) & bufMask];
					out[i] = lagrangeInterpolate(frac, a, b, c, d) - zin;
					buf[bufPos & bufMask] = *in;
					in += inStride;
					delay += delayStride;
					++bufPos;
				}
				in_.advance(n);
				delay_.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

class Flangep : public Gen
{
	ZIn in_;
	ZIn delay_;
	Z maxdelay_;
	int32_t bufSize;
	int32_t bufMask;
	int32_t bufPos;
	int32_t half;
    Z fhalf;
	Z* buf;
	Z sr;
public:

	Flangep(Thread& th, Arg in, Arg delay, Z maxdelay) : Gen(th, itemTypeZ, false), in_(in), delay_(delay), maxdelay_(maxdelay)
	{
		sr = th.rate.sampleRate;
		fhalf = ceil(sr * maxdelay + .5);
		half = (int32_t)fhalf;
		bufSize = NEXTPOWEROFTWO(2 * half);
		bufMask = bufSize - 1;
		bufPos = 0;
		buf = (Z*)calloc(bufSize, sizeof(Z));
	}

	~Flangep() { free(buf); }

	virtual const char* TypeName() const override { return "Flangep"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int inStride, delayStride;
			Z *in, *delay;
			if (in_(th, n, inStride, in) || delay_(th, n, delayStride, delay)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					Z zdelay = *delay;
					zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
					Z fpos = std::max(2., zdelay * sr + fhalf);
					Z ipos = floor(fpos);
					Z frac = fpos - ipos;
					int32_t offset = bufPos-(int32_t)ipos;
					Z a = buf[(offset+1) & bufMask];
					Z b = buf[(offset  ) & bufMask];
					Z c = buf[(offset-1) & bufMask];
					Z d = buf[(offset-2) & bufMask];
					Z zin = buf[(bufPos-half) & bufMask];
					out[i] = lagrangeInterpolate(frac, a, b, c, d) + zin;
					buf[bufPos & bufMask] = *in;
					in += inStride;
					delay += delayStride;
					++bufPos;
				}
				in_.advance(n);
				delay_.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

static void flange_(Thread& th, Prim* prim)
{
	Z maxdelay = th.popFloat("flange : maxdelay");
	V delay = th.popZIn("flange : delay");
	V in = th.popZIn("flange : in");

	if (maxdelay == 0.) {
		if (delay.isReal()) {
			maxdelay = delay.f;
		} else {
			post("flange : maxdelay is zero and delay is a signal\n");
			throw errFailed;
		}
	}

	th.push(new List(new Flange(th, in, delay, maxdelay)));
}

static void flangep_(Thread& th, Prim* prim)
{
	Z maxdelay = th.popFloat("flangep : maxdelay");
	V delay = th.popZIn("flangep : delay");
	V in = th.popZIn("flangep : in");

	if (maxdelay == 0.) {
		if (delay.isReal()) {
			maxdelay = delay.f;
		} else {
			post("flangep : maxdelay is zero and delay is a signal\n");
			throw errFailed;
		}
	}

	th.push(new List(new Flangep(th, in, delay, maxdelay)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

class CombN : public Gen
{
	ZIn in_;
	ZIn delay_;
	ZIn decay_;
	Z maxdelay_;
	int32_t bufSize;
	int32_t bufMask;
	int32_t bufPos;
	Z* buf;
	Z sr;
public:

	CombN(Thread& th, Arg in, Arg delay, Z maxdelay, Arg decay) : Gen(th, itemTypeZ, false), in_(in), delay_(delay), decay_(decay), maxdelay_(maxdelay)
	{
		sr = th.rate.sampleRate;
		bufSize = NEXTPOWEROFTWO((int32_t)ceil(sr * maxdelay + 1.));
		bufMask = bufSize - 1;
		bufPos = 0;
		buf = (Z*)calloc(bufSize, sizeof(Z));
	}

	~CombN() { free(buf); }

	virtual const char* TypeName() const override { return "CombN"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int inStride, delayStride, decayStride;
			Z *in, *delay, *decay;
			if (in_(th, n, inStride, in) || delay_(th, n, delayStride, delay) || decay_(th, n, decayStride, decay)) {
				setDone();
				break;
			} else {
				if (decayStride == 0) {
					double rdecay = 1. / *decay;
					for (int i = 0; i < n; ++i) {
						Z zdelay = *delay;
						zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
						Z fb = calcDecay(zdelay * rdecay);
						int32_t offset = std::max(1,(int32_t)floor(std::abs(zdelay) * sr + .5));
						Z z = fb * buf[(bufPos-offset) & bufMask];
						out[i] = z;
						buf[bufPos & bufMask] = *in + z;
						in += inStride;
						delay += delayStride;
						++bufPos;
					}
				} else {
					for (int i = 0; i < n; ++i) {
						Z zdelay = *delay;
						zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
						Z fb = calcDecay(zdelay / *decay);
						int32_t offset = (int32_t)floor(std::abs(zdelay) * sr + .5);
						Z z = fb * buf[(bufPos-offset) & bufMask];
						out[i] = z;
						buf[bufPos & bufMask] = *in + z;
						in += inStride;
						delay += delayStride;
						decay += decayStride;
						++bufPos;
					}
				}
				in_.advance(n);
				delay_.advance(n);
				decay_.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

static void combn_(Thread& th, Prim* prim)
{
	V decay = th.popZIn("combn : decay");
	Z maxdelay = th.popFloat("combn : maxdelay");
	V delay = th.popZIn("combn : delay");
	V in = th.popZIn("combn : in");

	if (maxdelay == 0.) {
		if (delay.isReal()) {
			maxdelay = delay.f;
		} else {
			post("combn : maxdelay is zero and delay is a signal\n");
			throw errFailed;
		}
	}

	th.push(new List(new CombN(th, in, delay, maxdelay, decay)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

class CombL : public Gen
{
	ZIn in_;
	ZIn delay_;
	ZIn decay_;
	Z maxdelay_;
	int32_t bufSize;
	int32_t bufMask;
	int32_t bufPos;
	Z* buf;
	Z sr;
public:

	CombL(Thread& th, Arg in, Arg delay, Z maxdelay, Arg decay) : Gen(th, itemTypeZ, false), in_(in), delay_(delay), decay_(decay), maxdelay_(maxdelay)
	{
		sr = th.rate.sampleRate;
		bufSize = NEXTPOWEROFTWO((int32_t)ceil(sr * maxdelay));
		bufMask = bufSize - 1;
		bufPos = 0;
		buf = (Z*)calloc(bufSize, sizeof(Z));
	}

	~CombL() { free(buf); }

	virtual const char* TypeName() const override { return "CombL"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int inStride, delayStride, decayStride;
			Z *in, *delay, *decay;
			if (in_(th, n, inStride, in) || delay_(th, n, delayStride, delay) || decay_(th, n, decayStride, decay)) {
				setDone();
				break;
			} else {
				if (decayStride == 0) {
                    if (delayStride == 0) {
						Z zdelay = *delay;
						zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
						Z fb = calcDecay(zdelay / *decay);
                        Z fpos = std::max(1., zdelay * sr);
                        Z ipos = floor(fpos);
                        Z frac = fpos - ipos;
                        int32_t offset = (int32_t)ipos;
                        for (int i = 0; i < n; ++i) {
							int32_t offset2 = bufPos-offset;
                            Z a = buf[(offset2) & bufMask];
                            Z b = buf[(offset2-1) & bufMask];
                            Z z = fb * (a + frac * (b - a));
                            out[i] = z;
                            buf[bufPos & bufMask] = *in + z;
                            in += inStride;
                            ++bufPos;
                        }
                    } else {
                        double rdecay = 1. / *decay;
                        for (int i = 0; i < n; ++i) {
							Z zdelay = *delay;
							zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
                            Z fb = calcDecay(zdelay * rdecay);
                            Z fpos = std::max(1., zdelay * sr);
                            Z ipos = floor(fpos);
                            Z frac = fpos - ipos;
                            int32_t offset = bufPos-(int32_t)ipos;
                            Z a = buf[offset & bufMask];
                            Z b = buf[(offset-1) & bufMask];
                            Z z = fb * (a + frac * (b - a));
                            out[i] = z;
                            buf[bufPos & bufMask] = *in + z;
                            in += inStride;
                            delay += delayStride;
                            ++bufPos;
                        }
                    }
				} else {
					for (int i = 0; i < n; ++i) {
						Z zdelay = *delay;
						zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
						Z fb = calcDecay(zdelay / *decay);
                        Z fpos = std::max(1., zdelay * sr);
                        Z ipos = floor(fpos);
                        Z frac = fpos - ipos;
						int32_t offset = bufPos-(int32_t)ipos;
                        Z a = buf[offset & bufMask];
                        Z b = buf[(offset-1) & bufMask];
						Z z = fb * (a + frac * (b - a));
						out[i] = z;
						buf[bufPos & bufMask] = *in + z;
						in += inStride;
						delay += delayStride;
						++bufPos;
					}
				}
				in_.advance(n);
				delay_.advance(n);
				decay_.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

static void combl_(Thread& th, Prim* prim)
{
	V decay = th.popZIn("combl : decay");
	Z maxdelay = th.popFloat("combl : maxdelay");
	V delay = th.popZIn("combl : delay");
	V in = th.popZIn("combl : in");

	if (maxdelay == 0.) {
		if (delay.isReal()) {
			maxdelay = delay.f;
		} else {
			post("combl : maxdelay is zero and delay is a signal\n");
			throw errFailed;
		}
	}

	th.push(new List(new CombL(th, in, delay, maxdelay, decay)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

class CombC : public Gen
{
	ZIn in_;
	ZIn delay_;
	ZIn decay_;
	Z maxdelay_;
	int32_t bufSize;
	int32_t bufMask;
	int32_t bufPos;
	Z* buf;
	Z sr;
public:

	CombC(Thread& th, Arg in, Arg delay, Z maxdelay, Arg decay) : Gen(th, itemTypeZ, false), in_(in), delay_(delay), decay_(decay), maxdelay_(maxdelay)
	{
		sr = th.rate.sampleRate;
		bufSize = NEXTPOWEROFTWO((int32_t)ceil(sr * maxdelay));
		bufMask = bufSize - 1;
		bufPos = 0;
		buf = (Z*)calloc(bufSize, sizeof(Z));
	}

	~CombC() { free(buf); }

	virtual const char* TypeName() const override { return "CombC"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int inStride, delayStride, decayStride;
			Z *in, *delay, *decay;
			if (in_(th, n, inStride, in) || delay_(th, n, delayStride, delay) || decay_(th, n, decayStride, decay)) {
				setDone();
				break;
			} else {
				if (decayStride == 0) {
                    if (delayStride == 0) {
						Z zdelay = *delay;
						zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
						Z fb = calcDecay(zdelay / *decay);
                        Z fpos = std::max(2., zdelay * sr);
                        Z ipos = floor(fpos);
                        Z frac = fpos - ipos;
                        int32_t offset = (int32_t)ipos;
                        for (int i = 0; i < n; ++i) {
							int32_t offset2 = bufPos-offset;
							Z a = buf[(offset2+1) & bufMask];
							Z b = buf[(offset2  ) & bufMask];
							Z c = buf[(offset2-1) & bufMask];
							Z d = buf[(offset2-2) & bufMask];
							Z z = fb * lagrangeInterpolate(frac, a, b, c, d);
                            out[i] = z;
                            buf[bufPos & bufMask] = *in + z;
                            in += inStride;
                            ++bufPos;
                        }
                    } else {
                        double rdecay = 1. / *decay;
                        for (int i = 0; i < n; ++i) {
							Z zdelay = *delay;
							zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
                            Z fb = calcDecay(zdelay * rdecay);
                            Z fpos = std::max(2., zdelay * sr);
                            Z ipos = floor(fpos);
                            Z frac = fpos - ipos;
							int32_t offset = bufPos-(int32_t)ipos;
							Z a = buf[(offset+1) & bufMask];
							Z b = buf[(offset  ) & bufMask];
							Z c = buf[(offset-1) & bufMask];
							Z d = buf[(offset-2) & bufMask];
							Z z = fb * lagrangeInterpolate(frac, a, b, c, d);
                            out[i] = z;
                            buf[bufPos & bufMask] = *in + z;
                            in += inStride;
                            delay += delayStride;
                            ++bufPos;
                        }
                    }
				} else {
					for (int i = 0; i < n; ++i) {
						Z zdelay = *delay;
						zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
						Z fb = calcDecay(zdelay / *decay);
                        Z fpos = std::max(2., zdelay * sr);
                        Z ipos = floor(fpos);
                        Z frac = fpos - ipos;
						int32_t offset = bufPos-(int32_t)ipos;
                        Z a = buf[(offset+1) & bufMask];
                        Z b = buf[(offset  ) & bufMask];
                        Z c = buf[(offset-1) & bufMask];
                        Z d = buf[(offset-2) & bufMask];
						Z z = fb * lagrangeInterpolate(frac, a, b, c, d);
						out[i] = z;
						buf[bufPos & bufMask] = *in + z;
						in += inStride;
						delay += delayStride;
						++bufPos;
					}
				}
				in_.advance(n);
				delay_.advance(n);
				decay_.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

static void combc_(Thread& th, Prim* prim)
{
	V decay = th.popZIn("combc : decay");
	Z maxdelay = th.popFloat("combc : maxdelay");
	V delay = th.popZIn("combc : delay");
	V in = th.popZIn("combc : in");

	if (maxdelay == 0.) {
		if (delay.isReal()) {
			maxdelay = delay.f;
		} else {
			post("combc : maxdelay is zero and delay is a signal\n");
			throw errFailed;
		}
	}

	th.push(new List(new CombC(th, in, delay, maxdelay, decay)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

class LPCombC : public Gen
{
	ZIn in_;
	ZIn delay_;
	ZIn decay_;
	ZIn lpfreq_;
	Z maxdelay_;
	Z y1_;
	Z freqmul_;
	int32_t bufSize;
	int32_t bufMask;
	int32_t bufPos;
	Z* buf;
	Z sr;
public:

	LPCombC(Thread& th, Arg in, Arg delay, Z maxdelay, Arg decay, Arg lpfreq) : Gen(th, itemTypeZ, false),
			in_(in), delay_(delay), decay_(decay), lpfreq_(lpfreq), maxdelay_(maxdelay), y1_(0.), freqmul_(th.rate.invNyquistRate * kFirstOrderCoeffScale)
	{
		sr = th.rate.sampleRate;
		bufSize = NEXTPOWEROFTWO((int32_t)ceil(sr * maxdelay));
		bufMask = bufSize - 1;
		bufPos = 0;
		buf = (Z*)calloc(bufSize, sizeof(Z));
	}

	~LPCombC() { free(buf); }

	virtual const char* TypeName() const override { return "LPCombC"; }

	virtual void pull(Thread& th) override
	{
		Z y1 = y1_;
		Z freqmul = freqmul_;
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int inStride, delayStride, decayStride, lpfreqStride;
			Z *in, *delay, *decay, *lpfreq;
			if (in_(th, n, inStride, in) || delay_(th, n, delayStride, delay) || decay_(th, n, decayStride, decay) || lpfreq_(th, n, lpfreqStride, lpfreq)) {
				setDone();
				break;
			} else {
                if (lpfreqStride == 0) {
                    Z b1 = t_firstOrderCoeff(*lpfreq * freqmul);
                    if (decayStride == 0) {
                        if (delayStride == 0) {
							Z zdelay = *delay;
							zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
                            Z fb = calcDecay(zdelay / *decay);
                            Z fpos = std::max(2., zdelay * sr);
                            Z ipos = floor(fpos);
                            Z frac = fpos - ipos;
                            int32_t offset = (int32_t)ipos;
                            for (int i = 0; i < n; ++i) {
                                int32_t offset2 = bufPos-offset;
                                Z a = buf[(offset2+1) & bufMask];
                                Z b = buf[(offset2  ) & bufMask];
                                Z c = buf[(offset2-1) & bufMask];
                                Z d = buf[(offset2-2) & bufMask];
                                Z z = fb * lagrangeInterpolate(frac, a, b, c, d);
                                z = z + b1 * (y1 - z);

                                out[i] = y1 = z;
                                buf[bufPos & bufMask] = *in + z;
                                in += inStride;
                                ++bufPos;
                            }
                        } else {
                            double rdecay = 1. / *decay;
                            for (int i = 0; i < n; ++i) {
								Z zdelay = *delay;
								zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
                                Z fb = calcDecay(zdelay * rdecay);
                                Z fpos = std::max(2., zdelay * sr);
                                Z ipos = floor(fpos);
                                Z frac = fpos - ipos;
                                int32_t offset = bufPos-(int32_t)ipos;
                                Z a = buf[(offset+1) & bufMask];
                                Z b = buf[(offset  ) & bufMask];
                                Z c = buf[(offset-1) & bufMask];
                                Z d = buf[(offset-2) & bufMask];
                                Z z = fb * lagrangeInterpolate(frac, a, b, c, d);
                                z = z + b1 * (y1 - z);
                                out[i] = y1 = z;
                                buf[bufPos & bufMask] = *in + z;
                                in += inStride;
                                delay += delayStride;
                                ++bufPos;
                            }
                        }
                    } else {
                        for (int i = 0; i < n; ++i) {
							Z zdelay = *delay;
							zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
                            Z fb = calcDecay(zdelay / *decay);
                            Z fpos = std::max(2., zdelay * sr);
                            Z ipos = floor(fpos);
                            Z frac = fpos - ipos;
                            int32_t offset = bufPos-(int32_t)ipos;
                            Z a = buf[(offset+1) & bufMask];
                            Z b = buf[(offset  ) & bufMask];
                            Z c = buf[(offset-1) & bufMask];
                            Z d = buf[(offset-2) & bufMask];
                            Z z = fb * lagrangeInterpolate(frac, a, b, c, d);
                            z = z + b1 * (y1 - z);
                            out[i] = y1 = z;
                            buf[bufPos & bufMask] = *in + z;
                            in += inStride;
                            delay += delayStride;
                            ++bufPos;
                        }
                    }
               } else {
                    if (decayStride == 0) {
                        if (delayStride == 0) {
							Z zdelay = *delay;
							zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
                            Z fb = calcDecay(zdelay / *decay);
                            Z fpos = std::max(2., zdelay * sr);
                            Z ipos = floor(fpos);
                            Z frac = fpos - ipos;
                            int32_t offset = (int32_t)ipos;
                            for (int i = 0; i < n; ++i) {
                                int32_t offset2 = bufPos-offset;
                                Z a = buf[(offset2+1) & bufMask];
                                Z b = buf[(offset2  ) & bufMask];
                                Z c = buf[(offset2-1) & bufMask];
                                Z d = buf[(offset2-2) & bufMask];
                                Z z = fb * lagrangeInterpolate(frac, a, b, c, d);
                                Z b1 = t_firstOrderCoeff(*lpfreq * freqmul);
                                z = z + b1 * (y1 - z);
                                out[i] = y1 = z;
                                buf[bufPos & bufMask] = *in + z;
                                in += inStride;
                                lpfreq += lpfreqStride;
                                ++bufPos;
                            }
                        } else {
                            double rdecay = 1. / *decay;
                            for (int i = 0; i < n; ++i) {
								Z zdelay = *delay;
								zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
                                Z fb = calcDecay(zdelay * rdecay);
                                Z fpos = std::max(2., zdelay * sr);
                                Z ipos = floor(fpos);
                                Z frac = fpos - ipos;
                                int32_t offset = bufPos-(int32_t)ipos;
                                Z a = buf[(offset+1) & bufMask];
                                Z b = buf[(offset  ) & bufMask];
                                Z c = buf[(offset-1) & bufMask];
                                Z d = buf[(offset-2) & bufMask];
                                Z z = fb * lagrangeInterpolate(frac, a, b, c, d);
                                Z b1 = t_firstOrderCoeff(*lpfreq * freqmul);
                                z = z + b1 * (y1 - z);
                                out[i] = y1 = z;
                                buf[bufPos & bufMask] = *in + z;
                                in += inStride;
                                delay += delayStride;
                                lpfreq += lpfreqStride;
                                ++bufPos;
                            }
                        }
                    } else {
                        for (int i = 0; i < n; ++i) {
							Z zdelay = *delay;
							zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
                            Z fb = calcDecay(zdelay / *decay);
                            Z fpos = std::max(2., zdelay * sr);
                            Z ipos = floor(fpos);
                            Z frac = fpos - ipos;
                            int32_t offset = bufPos-(int32_t)ipos;
                            Z a = buf[(offset+1) & bufMask];
                            Z b = buf[(offset  ) & bufMask];
                            Z c = buf[(offset-1) & bufMask];
                            Z d = buf[(offset-2) & bufMask];
                            Z z = fb * lagrangeInterpolate(frac, a, b, c, d);
                            Z b1 = t_firstOrderCoeff(*lpfreq * freqmul);
                            z = z + b1 * (y1 - z);
                            out[i] = y1 = z;
                            buf[bufPos & bufMask] = *in + z;
                            in += inStride;
                            delay += delayStride;
                            lpfreq += lpfreqStride;
                            ++bufPos;
                        }
                    }
                }
				y1_ = y1;
				in_.advance(n);
				delay_.advance(n);
				decay_.advance(n);
				lpfreq_.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

static void lpcombc_(Thread& th, Prim* prim)
{
	V lpfreq = th.popZIn("lpcombc : lpfreq");
	V decay = th.popZIn("lpcombc : decay");
	Z maxdelay = th.popFloat("lpcombc : maxdelay");
	V delay = th.popZIn("lpcombc : delay");
	V in = th.popZIn("lpcombc : in");

	if (maxdelay == 0.) {
		if (delay.isReal()) {
			maxdelay = delay.f;
		} else {
			post("lpcombc : maxdelay is zero and delay is a signal\n");
			throw errFailed;
		}
	}

	th.push(new List(new LPCombC(th, in, delay, maxdelay, decay, lpfreq)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

class AllpassN : public Gen
{
	ZIn in_;
	ZIn delay_;
	ZIn decay_;
	Z maxdelay_;
	int32_t bufSize;
	int32_t bufMask;
	int32_t bufPos;
	Z* buf;
	Z sr;
public:

	AllpassN(Thread& th, Arg in, Arg delay, Z maxdelay, Arg decay) : Gen(th, itemTypeZ, false), in_(in), delay_(delay), decay_(decay), maxdelay_(maxdelay)
	{
		sr = th.rate.sampleRate;
		bufSize = NEXTPOWEROFTWO((int32_t)ceil(sr * maxdelay));
		bufMask = bufSize - 1;
		bufPos = 0;
		buf = (Z*)calloc(bufSize, sizeof(Z));
	}

	~AllpassN() { free(buf); }

	virtual const char* TypeName() const override { return "AllpassN"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int inStride, delayStride, decayStride;
			Z *in, *delay, *decay;
			if (in_(th, n, inStride, in) || delay_(th, n, delayStride, delay) || decay_(th, n, decayStride, decay)) {
				setDone();
				break;
			} else {
				if (decayStride == 0) {
					double rdecay = 1. / *decay;
					for (int i = 0; i < n; ++i) {
						Z zdelay = *delay;
						zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
						Z fb = calcDecay(zdelay * rdecay);
						int32_t offset = std::max(1,(int32_t)floor(zdelay * sr + .5));
                        Z drd = buf[(bufPos-offset) & bufMask];
                        Z dwr = drd * fb + *in;
						buf[bufPos & bufMask] = dwr;
						out[i] = drd - fb * dwr;
						in += inStride;
						delay += delayStride;
						++bufPos;
					}
				} else {
					for (int i = 0; i < n; ++i) {
						Z zdelay = *delay;
						zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
						Z fb = calcDecay(zdelay / *decay);
						int32_t offset = (int32_t)floor(zdelay * sr + .5);
                        Z drd = buf[(bufPos-offset) & bufMask];
                        Z dwr = drd * fb + *in;
						buf[bufPos & bufMask] = dwr;
						out[i] = drd - fb * dwr;
						in += inStride;
						delay += delayStride;
						decay += decayStride;
						++bufPos;
					}
				}
				in_.advance(n);
				delay_.advance(n);
				decay_.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

static void alpasn_(Thread& th, Prim* prim)
{
	V decay = th.popZIn("alpasn : decay");
	Z maxdelay = th.popFloat("alpasn : maxdelay");
	V delay = th.popZIn("alpasn : delay");
	V in = th.popZIn("alpasn : in");

	if (maxdelay == 0.) {
		if (delay.isReal()) {
			maxdelay = delay.f;
		} else {
			post("alpasn : maxdelay is zero and delay is a signal\n");
			throw errFailed;
		}
	}

	th.push(new List(new AllpassN(th, in, delay, maxdelay, decay)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

class AllpassL : public Gen
{
	ZIn in_;
	ZIn delay_;
	ZIn decay_;
	Z maxdelay_;
	int32_t bufSize;
	int32_t bufMask;
	int32_t bufPos;
	Z* buf;
	Z sr;
public:

	AllpassL(Thread& th, Arg in, Arg delay, Z maxdelay, Arg decay) : Gen(th, itemTypeZ, false), in_(in), delay_(delay), decay_(decay), maxdelay_(maxdelay)
	{
		sr = th.rate.sampleRate;
		bufSize = NEXTPOWEROFTWO((int32_t)ceil(sr * maxdelay));
		bufMask = bufSize - 1;
		bufPos = 0;
		buf = (Z*)calloc(bufSize, sizeof(Z));
	}

	~AllpassL() { free(buf); }

	virtual const char* TypeName() const override { return "AllpassL"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int inStride, delayStride, decayStride;
			Z *in, *delay, *decay;
			if (in_(th, n, inStride, in) || delay_(th, n, delayStride, delay) || decay_(th, n, decayStride, decay)) {
				setDone();
				break;
			} else {
				if (decayStride == 0) {
                    if (delayStride == 0) {
						Z zdelay = *delay;
						zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
						Z fb = calcDecay(zdelay / *decay);
                        Z fpos = std::max(1., zdelay * sr);
                        Z ipos = floor(fpos);
                        Z frac = fpos - ipos;
                        int32_t offset = (int32_t)ipos;
                        for (int i = 0; i < n; ++i) {
							int32_t offset2 = bufPos-offset;
                            Z a = buf[(offset2) & bufMask];
                            Z b = buf[(offset2-1) & bufMask];
                            Z drd = a + frac * (b - a);
                            Z dwr = drd * fb + *in;
                            buf[bufPos & bufMask] = dwr;
                            out[i] = drd - fb * dwr;
                            in += inStride;
                            ++bufPos;
                        }
                    } else {
                        double rdecay = 1. / *decay;
                        for (int i = 0; i < n; ++i) {
							Z zdelay = *delay;
							zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
                            Z fb = calcDecay(zdelay * rdecay);
                            Z fpos = std::max(1., zdelay * sr);
                            Z ipos = floor(fpos);
                            Z frac = fpos - ipos;
                            int32_t offset = bufPos-(int32_t)ipos;
                            Z a = buf[(offset) & bufMask];
                            Z b = buf[(offset-1) & bufMask];
                            Z drd = a + frac * (b - a);
                            Z dwr = drd * fb + *in;
                            buf[bufPos & bufMask] = dwr;
                            out[i] = drd - fb * dwr;
                            in += inStride;
                            delay += delayStride;
                            ++bufPos;
                        }
                    }
				} else {
					for (int i = 0; i < n; ++i) {
						Z zdelay = *delay;
						zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
						Z fb = calcDecay(zdelay / *decay);
                        Z fpos = std::max(1., zdelay * sr);
                        Z ipos = floor(fpos);
                        Z frac = fpos - ipos;
						int32_t offset = bufPos-(int32_t)ipos;
                        Z a = buf[(offset) & bufMask];
                        Z b = buf[(offset-1) & bufMask];
                        Z drd = a + frac * (b - a);
                        Z dwr = drd * fb + *in;
                        buf[bufPos & bufMask] = dwr;
                        out[i] = drd - fb * dwr;
						in += inStride;
						delay += delayStride;
						++bufPos;
					}
				}
				in_.advance(n);
				delay_.advance(n);
				decay_.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

static void alpasl_(Thread& th, Prim* prim)
{
	V decay = th.popZIn("alpasl : decay");
	Z maxdelay = th.popFloat("alpasl : maxdelay");
	V delay = th.popZIn("alpasl : delay");
	V in = th.popZIn("alpasl : in");

	if (maxdelay == 0.) {
		if (delay.isReal()) {
			maxdelay = delay.f;
		} else {
			post("alpasl : maxdelay is zero and delay is a signal\n");
			throw errFailed;
		}
	}

	th.push(new List(new AllpassL(th, in, delay, maxdelay, decay)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

class AllpassC : public Gen
{
	ZIn in_;
	ZIn delay_;
	ZIn decay_;
	Z maxdelay_;
	int32_t bufSize;
	int32_t bufMask;
	int32_t bufPos;
	Z* buf;
	Z sr;
public:

	AllpassC(Thread& th, Arg in, Arg delay, Z maxdelay, Arg decay) : Gen(th, itemTypeZ, false), in_(in), delay_(delay), decay_(decay), maxdelay_(maxdelay)
	{
		sr = th.rate.sampleRate;
		bufSize = NEXTPOWEROFTWO((int32_t)ceil(sr * maxdelay));
		bufMask = bufSize - 1;
		bufPos = 0;
		buf = (Z*)calloc(bufSize, sizeof(Z));
	}

	~AllpassC() { free(buf); }

	virtual const char* TypeName() const override { return "AllpassC"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int inStride, delayStride, decayStride;
			Z *in, *delay, *decay;
			if (in_(th, n, inStride, in) || delay_(th, n, delayStride, delay) || decay_(th, n, decayStride, decay)) {
				setDone();
				break;
			} else {
				if (decayStride == 0) {
                    if (delayStride == 0) {
						Z zdelay = *delay;
						zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
						Z fb = calcDecay(zdelay / *decay);
                        Z fpos = std::max(2., zdelay * sr);
                        Z ipos = floor(fpos);
                        Z frac = fpos - ipos;
                        int32_t offset = (int32_t)ipos;
                        for (int i = 0; i < n; ++i) {
							int32_t offset2 = bufPos-offset;
							Z a = buf[(offset2+1) & bufMask];
							Z b = buf[(offset2  ) & bufMask];
							Z c = buf[(offset2-1) & bufMask];
							Z d = buf[(offset2-2) & bufMask];
                            Z drd = lagrangeInterpolate(frac, a, b, c, d);
                            Z dwr = drd * fb + *in;
                            buf[bufPos & bufMask] = dwr;
                            out[i] = drd - fb * dwr;
                            in += inStride;
                            ++bufPos;
                        }
                    } else {
                        double rdecay = 1. / *decay;
                        for (int i = 0; i < n; ++i) {
							Z zdelay = *delay;
							zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
                            Z fb = calcDecay(zdelay * rdecay);
                            Z fpos = std::max(2., zdelay * sr);
                            Z ipos = floor(fpos);
                            Z frac = fpos - ipos;
							int32_t offset = bufPos-(int32_t)ipos;
							Z a = buf[(offset+1) & bufMask];
							Z b = buf[(offset  ) & bufMask];
							Z c = buf[(offset-1) & bufMask];
							Z d = buf[(offset-2) & bufMask];
                            Z drd = lagrangeInterpolate(frac, a, b, c, d);
                            Z dwr = drd * fb + *in;
                            buf[bufPos & bufMask] = dwr;
                            out[i] = drd - fb * dwr;
                            in += inStride;
                            delay += delayStride;
                            ++bufPos;
                        }
                    }
				} else {
					for (int i = 0; i < n; ++i) {
						Z zdelay = *delay;
						zdelay = std::clamp(zdelay, -maxdelay_, maxdelay_);
						Z fb = calcDecay(zdelay / *decay);
                        Z fpos = std::max(2., zdelay * sr);
                        Z ipos = floor(fpos);
                        Z frac = fpos - ipos;
						int32_t offset = bufPos-(int32_t)ipos;
                        Z a = buf[(offset+1) & bufMask];
                        Z b = buf[(offset  ) & bufMask];
                        Z c = buf[(offset-1) & bufMask];
                        Z d = buf[(offset-2) & bufMask];
                        Z drd = lagrangeInterpolate(frac, a, b, c, d);
                        Z dwr = drd * fb + *in;
                        buf[bufPos & bufMask] = dwr;
                        out[i] = drd - fb * dwr;
						in += inStride;
						delay += delayStride;
						++bufPos;
					}
				}
				in_.advance(n);
				delay_.advance(n);
				decay_.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

static void alpasc_(Thread& th, Prim* prim)
{
	V decay = th.popZIn("alpasc : decay");
	Z maxdelay = th.popFloat("alpasc : maxdelay");
	V delay = th.popZIn("alpasc : delay");
	V in = th.popZIn("alpasc : in");

	if (maxdelay == 0.) {
		if (delay.isReal()) {
			maxdelay = delay.f;
		} else {
			post("alpasc : maxdelay is zero and delay is a signal\n");
			throw errFailed;
		}
	}

	th.push(new List(new AllpassC(th, in, delay, maxdelay, decay)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////
class FDN;

class FDN_OutputChannel : public Gen
{
	friend class FDN;
	P<FDN> mFDN;

public:
	FDN_OutputChannel(Thread& th, bool inFinite, FDN* inFDN);

	virtual void norefs() override
	{
		mOut = nullptr;
		mFDN = nullptr;
	}

	virtual const char* TypeName() const override { return "FDN_OutputChannel"; }

	virtual void pull(Thread& th) override;
};

class FDN : public Object
{
	ZIn in_;
	ZIn wet_;
	Z decayLo_;
	Z decayMid_;
	Z decayHi_;
	Z mindelay_;
	Z maxdelay_;
	Z sr_;

	Z a1Lo, a1Hi;
	Z scaleLoLPF, scaleLoHPF, scaleHiLPF, scaleHiHPF;
	FDN_OutputChannel* mLeft;
	FDN_OutputChannel* mRight;

	static const int kNumDelays = 16;

	struct FDNDelay
	{
		Z* buf;
		int size, mask, rpos, wpos, offset;
		Z delay, gain;
		Z fbLo, fbMid, fbHi;
		Z x1A, x1B, x1C, x1D;
		Z y1A, y1B, y1C, y1D;

		FDNDelay()
		{
		}
		~FDNDelay() { free(buf); }

		void set(Thread& th, const FDN& fdn, Z inDelay, int& ioSampleDelay)
		{
			int sampleDelay = (int)(th.rate.sampleRate * inDelay);
			if (sampleDelay <= ioSampleDelay) sampleDelay = ioSampleDelay + 2;
			sampleDelay = (int)nextPrime(sampleDelay);
			ioSampleDelay = sampleDelay;
			Z actualDelay = (Z)sampleDelay * th.rate.invSampleRate;
			size = NEXTPOWEROFTWO(sampleDelay);
			mask = size - 1;
			printf("delay %6d %6d %6d %f\n", sampleDelay, size, mask, actualDelay);
			rpos = 0;
			wpos = sampleDelay;
			buf = (Z*)calloc(size, sizeof(Z));
			const Z n1 = 1. / sqrt(kNumDelays);
			//const Z n1 = 1. / kNumDelays;
			fbLo  = n1 * calcDecay(actualDelay / fdn.decayLo_);
			fbMid = n1 * calcDecay(actualDelay / fdn.decayMid_);
			fbHi  = n1 * calcDecay(actualDelay / fdn.decayHi_);
			y1A = 0.;
			y1B = 0.;
			y1C = 0.;
			y1D = 0.;
			x1A = 0.;
			x1B = 0.;
			x1C = 0.;
			x1D = 0.;
		}
	};

public:

	FDNDelay mDelay[kNumDelays];

	FDN(Thread& th, Arg in, Arg wet, Z mindelay, Z maxdelay, Z decayLo, Z decayMid, Z decayHi, Z seed)
		: in_(in), wet_(wet),
		decayLo_(decayLo), decayMid_(decayMid), decayHi_(decayHi),
		mindelay_(mindelay), maxdelay_(maxdelay)
	{
		sr_ = th.rate.sampleRate;
		Z freqmul = th.rate.invNyquistRate * kFirstOrderCoeffScale;
		a1Lo = t_firstOrderCoeff(freqmul * 200.);
		a1Hi = t_firstOrderCoeff(freqmul * 2000.);
		scaleLoLPF = .5 * (1. - a1Lo);
		scaleLoHPF = .5 * (1. + a1Lo);
		scaleHiLPF = .5 * (1. - a1Hi);
		scaleHiHPF = .5 * (1. + a1Hi);

		Z delay = mindelay;
		Z ratio = maxdelay / mindelay;
		Z interval = pow(ratio, 1. / (kNumDelays - 1.));
		int prevSampleDelay = 0;
		for (int i = 0; i < kNumDelays; ++i) {
			double expon = (random() / 2147483647. - .5) * 0.8;
			double deviation = pow(interval,  expon);
			mDelay[i].set(th, *this, delay * deviation, prevSampleDelay);
			delay *= interval;
		}

	}

	~FDN() { delete mLeft; delete mRight; }

	virtual const char* TypeName() const override { return "FDN"; }

	P<List> createOutputs(Thread& th);

	void matrix(Z x[kNumDelays])
	{
		Z a0  = x[ 0];
		Z a1  = x[ 1];
		Z a2  = x[ 2];
		Z a3  = x[ 3];
		Z a4  = x[ 4];
		Z a5  = x[ 5];
		Z a6  = x[ 6];
		Z a7  = x[ 7];
		Z a8  = x[ 8];
		Z a9  = x[ 9];
		Z a10 = x[10];
		Z a11 = x[11];
		Z a12 = x[12];
		Z a13 = x[13];
		Z a14 = x[14];
		Z a15 = x[15];

		Z b0  =  a0 + a1;
		Z b1  =  a0 - a1;
		Z b2  =  a2 + a3;
		Z b3  =  a2 - a3;
		Z b4  =  a4 + a5;
		Z b5  =  a4 - a5;
		Z b6  =  a6 + a7;
		Z b7  =  a6 - a7;
		Z b8  =  a8 + a9;
		Z b9  =  a8 - a9;
		Z b10 = a10 + a11;
		Z b11 = a10 - a11;
		Z b12 = a12 + a13;
		Z b13 = a12 - a13;
		Z b14 = a14 + a15;
		Z b15 = a14 - a15;

		Z c0  =  b0 + b2;
		Z c1  =  b1 + b3;
		Z c2  =  b0 - b2;
		Z c3  =  b1 - b3;
		Z c4  =  b4 + b6;
		Z c5  =  b5 + b7;
		Z c6  =  b4 - b6;
		Z c7  =  b5 - b7;
		Z c8  =  b8 + b10;
		Z c9  =  b9 + b11;
		Z c10 =  b8 - b10;
		Z c11 =  b9 - b11;
		Z c12 = b12 + b14;
		Z c13 = b13 + b15;
		Z c14 = b12 - b14;
		Z c15 = b13 - b15;

		Z d0  =  c0 + c4;
		Z d1  =  c1 + c5;
		Z d2  =  c2 + c6;
		Z d3  =  c3 + c7;
		Z d4  =  c0 - c4;
		Z d5  =  c1 - c5;
		Z d6  =  c2 - c6;
		Z d7  =  c3 - c7;
		Z d8  =  c8 + c12;
		Z d9  =  c9 + c13;
		Z d10 = c10 + c14;
		Z d11 = c11 + c15;
		Z d12 =  c8 - c12;
		Z d13 =  c9 - c13;
		Z d14 = c10 - c14;
		Z d15 = c11 - c15;

		x[ 0] = d0 + d8;
		x[ 1] = d1 + d9;
		x[ 2] = d2 + d10;
		x[ 3] = d3 + d11;
		x[ 4] = d4 + d12;
		x[ 5] = d5 + d13;
		x[ 6] = d6 + d14;
		x[ 7] = d7 + d15;
		x[ 8] = d0 - d8;
		x[ 9] = d1 - d9;
		x[10] = d2 - d10;
		x[11] = d3 - d11;
		x[12] = d4 - d12;
		x[13] = d5 - d13;
		x[14] = d6 - d14;
		x[15] = d7 - d15;
	}

	virtual void pull(Thread& th)
	{
		int framesToFill = mLeft->mBlockSize;

		Z Sink = 0.;
		Z* Lout;
		Z* Rout;
		int Loutstride = 1;
		int Routstride = 1;

		if (mLeft->mOut) {
			Lout = mLeft->mOut->fulfillz(framesToFill);
		} else {
			Lout = &Sink;
			Loutstride = 0;
		}

		if (mRight->mOut) {
			Rout = mRight->mOut->fulfillz(framesToFill);
		} else {
			Rout = &Sink;
			Routstride = 0;
		}

		while (framesToFill) {
			int n = framesToFill;
			int inStride, wetStride;
			Z *in;
			Z *wet;
			if (in_(th, n, inStride, in) || wet_(th, n, wetStride, wet)) {
				mLeft->setDone();
				mRight->setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					Z x[kNumDelays];

					for (UInt32 j = 0; j < kNumDelays; ++j)
					{
						FDNDelay& d = mDelay[j];
						// read from delay line
						Z x0 = d.buf[d.rpos & d.mask];

						// attenuate and filter the output of the delay line.

						// high crossover
						Z x0A = scaleHiHPF * x0;
						Z x0B = scaleHiLPF * x0 ;
						Z y0A = x0A - d.x1A + a1Hi * d.y1A;	// hpf -> high band
						Z y0B = x0B + d.x1B + a1Hi * d.y1B;	// lpf -> low + mid
						d.y1A = y0A;
						d.y1B = y0B;
						d.x1A = x0A;
						d.x1B = x0B;

						// low crossover
						Z x0C = scaleLoHPF * y0B;
						Z x0D = scaleLoLPF * y0B;
						Z y0C = x0C - d.x1C + a1Lo * d.y1C;	// hpf -> mid band
						Z y0D = x0D + d.x1D + a1Lo * d.y1D;	// lpf -> low band
						d.y1C = y0C;
						d.y1D = y0D;
						d.x1C = x0C;
						d.x1D = x0D;

						x[j] = d.fbLo * y0D  +  d.fbMid * y0C  +  d.fbHi * y0A;

						++d.rpos;
					}

					matrix(x);

					Z ini = *in;
					Z w = *wet;
					*Lout = ini + w * (x[1] - ini);
					*Rout = ini + w * (x[2] - ini);
					Lout += Loutstride;
					Rout += Routstride;

					// write back to delay line
					for (UInt32 j = 0; j < kNumDelays; ++j)
					{
						FDNDelay& d = mDelay[j];

						d.buf[d.wpos & d.mask] = x[j] + ini;
						++d.wpos;

					}
					in += inStride;
					wet += wetStride;
				}
				in_.advance(n);
				framesToFill -= n;
			}
		}

		if (mLeft->mOut)  mLeft->produce(framesToFill);
		if (mRight->mOut) mRight->produce(framesToFill);
	}
};

FDN_OutputChannel::FDN_OutputChannel(Thread& th, bool inFinite, FDN* inFDN)
                                : Gen(th, itemTypeZ, inFinite), mFDN(inFDN)
{
}

void FDN_OutputChannel::pull(Thread& th)
{
	mFDN->pull(th);
}

P<List> FDN::createOutputs(Thread& th)
{
	mLeft = new FDN_OutputChannel(th, finite, this);
	mRight = new FDN_OutputChannel(th, finite, this);

	P<Gen> left = mLeft;
	P<Gen> right = mRight;

	P<List> s = new List(itemTypeV, 2);
	P<Array> a = s->mArray;
	a->add(new List(left));
	a->add(new List(right));

	return s;
}

static void fdn_(Thread& th, Prim* prim)
{
	Z seed = th.popFloat("fdn : seed");
	Z maxdelay = th.popFloat("fdn : maxdelay");
	Z mindelay = th.popFloat("fdn : mindelay");
	Z decayHi = th.popFloat("fdn : decayHi");
	Z decayMid = th.popFloat("fdn : decayMid");
	Z decayLo = th.popFloat("fdn : decayLo");
	V wet = th.popZIn("fdn : wet");
	V in = th.popZIn("fdn : in");

	P<FDN> fdn = new FDN(th, in, wet, mindelay, maxdelay, decayLo, decayMid, decayHi, seed);

	P<List> s = fdn->createOutputs(th);

	th.push(s);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

#define DEF(NAME, N, HELP) 	vm.def(#NAME, N, NAME##_, HELP);
#define DEFMCX(NAME, N, HELP) 	vm.defmcx(#NAME, N, NAME##_, HELP);
#define DEFAM(NAME, MASK, HELP) 	vm.defautomap(#NAME, #MASK, NAME##_, HELP);

void AddDelayUGenOps()
{
	vm.addBifHelp("\n*** delay unit generators ***");
	DEFAM(delayn, zzk, "(in delay maxdelay --> out) delay line with no interpolation.");
	DEFAM(delayl, zzk, "(in delay maxdelay --> out) delay line with linear interpolation.");
	DEFAM(delayc, zzk, "(in delay maxdelay --> out) delay line with cubic interpolation.");
	DEFAM(flange, zzk, "(in delay maxdelay --> out) flanger with cubic interpolation. delay can be negative. latency is maxdelay.");
	DEFAM(flangep, zzk, "(in delay maxdelay --> out) flanger with cubic interpolation. adds delayed signal instead of subtracts.");

	DEFAM(combn, zzkz, "(in delay maxdelay decayTime --> out) comb delay filter with no interpolation.");
	DEFAM(combl, zzkz, "(in delay maxdelay decayTime --> out) comb delay filter with linear interpolation.");
	DEFAM(combc, zzkz, "(in delay maxdelay decayTime --> out) comb delay filter with cubic interpolation.");
	DEFAM(lpcombc, zzkzz, "(in delay maxdelay decayTime lpfreq --> out) low pass comb delay filter with cubic interpolation.");

	DEFAM(alpasn, zzkz, "(in delay maxdelay decayTime --> out) all pass delay filter with no interpolation.");
	DEFAM(alpasl, zzkz, "(in delay maxdelay decayTime --> out) all pass delay filter with linear interpolation.");
	DEFAM(alpasc, zzkz, "(in delay maxdelay decayTime --> out) all pass delay filter with cubic interpolation.");
	//DEFAM(fdn, zzkkkkkk, "(in wet decayLo decayMid decayHi mindelay maxdelay rseed --> out) feedback delay network reverb.");
}

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "MathOps.hpp"
#include "clz.hpp"
#include <ctype.h>
#include "primes.hpp"
#include <Accelerate/Accelerate.h>

V BinaryOp::makeVList(Thread& th, Arg a, Arg b)
{
	return new List(new BinaryOpGen(th, this, a, b));
}

V BinaryOp::makeZList(Thread& th, Arg a, Arg b)
{
	return new List(new BinaryOpZGen(th, this, a, b));
}

V BinaryOpLink::makeVList(Thread& th, Arg a, Arg b)
{
	return new List(new BinaryOpLinkGen(th, this, a, b));
}

V BinaryOpLink::makeZList(Thread& th, Arg a, Arg b)
{
	return new List(new BinaryOpLinkZGen(th, this, a, b));
}

void UnaryOp::loop(Thread& th, int n, V *a, int astride, V *out)
{
	LOOP(i, n) {
		out[i] = a->unaryOp(th, this);
		a += astride;
	}
}

void BinaryOp::loop(Thread& th, int n, V *a, int astride, V *b, int bstride, V *out)
{
	LOOP(i, n) {
		out[i] = a->binaryOp(th, this, *b);
		a += astride;
		b += bstride;
	}
}

void BinaryOp::loopzv(Thread& th, int n, Z *aa, int astride, V *bb, int bstride, V *out)
{
	LOOP(i,n) {
		Arg a = *aa;
		Arg b = *bb;
		out[i] = a.binaryOp(th, this, b);
		aa += astride;
		bb += bstride;
	}
}

void BinaryOp::loopvz(Thread& th, int n, V *aa, int astride, Z *bb, int bstride, V *out)
{
	LOOP(i,n) {
		Arg a = *aa;
		Arg b = *bb;
		out[i] = a.binaryOp(th, this, b);
		aa += astride;
		bb += bstride;
	}
}

void BinaryOp::scan(Thread& th, int n, V& z, V *a, int astride, V *out)
{
	V x = z;
	LOOP(i, n) {
		out[i] = x = x.binaryOp(th, this, *a);
		a += astride;
	}
	z = x;
}

void BinaryOp::pairs(Thread& th, int n, V& z, V *a, int astride, V *out)
{
	V x = z;
	LOOP(i, n) {
		out[i] = a->binaryOp(th, this, x);
		x = *a;
		a += astride;
	}
	z = x;
}

void BinaryOp::reduce(Thread& th, int n, V& z, V *a, int astride)
{
	V x = z;
	LOOP(i, n) {
		x = x.binaryOp(th, this, *a);
		a += astride;
	}
	z = x;
}

void UnaryOpZGen::pull(Thread& th) {
	int framesToFill = mBlockSize;
	Z* out = mOut->fulfillz(framesToFill);
	while (framesToFill) {
		int n = framesToFill;
		int astride;
		Z *a;
		if (_a(th, n,astride, a)) {
			setDone();
			break;
		} else {
			op->loopz(n, a, astride, out);
			_a.advance(n);
			framesToFill -= n;
			out += n;
		}
	}
	produce(framesToFill);
}

void BinaryOpZGen::pull(Thread& th)
{
	int framesToFill = mBlockSize;
	Z* out = mOut->fulfillz(framesToFill);
	while (framesToFill) {
		int n = framesToFill;
		int astride, bstride;
		Z *a, *b;
		if (_a(th, n,astride, a) || _b(th, n,bstride, b)) {
			setDone();
			break;
		} else {
			op->loopz(n, a, astride, b, bstride, out);
			_a.advance(n);
			_b.advance(n);
			framesToFill -= n;
			out += n;
		}
	}
	produce(framesToFill);
}

void BinaryOpLinkZGen::pull(Thread& th)
{
	int framesToFill = mBlockSize;
	Z* out = mOut->fulfillz(framesToFill);
	while (framesToFill) {
		int n = framesToFill;
		int astride, bstride;
		Z *a, *b;
		if (_a(th, n,astride, a)) {
			produce(framesToFill);
			_b.link(th, mOut);
			setDone();
			break;
		} else if (_b(th, n,bstride, b)) {
			produce(framesToFill);
			_a.link(th, mOut);
			setDone();
			break;
		} else {
			op->loopz(n, a, astride, b, bstride, out);
			_a.advance(n);
			_b.advance(n);
			framesToFill -= n;
			out += n;
		}
	}
	produce(framesToFill);
}

static void DoPairwise(Thread& th, BinaryOp* op)
{
	V a = th.pop();
	if (a.isVList()) {
		O s = new List(new PairsOpGen(th, a, op));
		th.push(s);
	} else if (a.isZList()) {
		O s = new List(new PairsOpZGen(th, a, op));
		th.push(s);
	} else if (a.isReal() || a.isString()) {
		th.push(a);
	} else {
		std::string s = op->Name();
		s += "^ : a";
		wrongType(s.c_str(), "Real, List or String", a);
	}
}

static void DoScan(Thread& th, BinaryOp* op)
{
	V a = th.pop();
	if (a.isVList()) {
		O s = new List(new ScanOpGen(th, a, op));
		th.push(s);
	} else if (a.isZList()) {
		O s = new List(new ScanOpZGen(th, a, op));
		th.push(s);
	} else if (a.isReal() || a.isString()) {
		th.push(a);
	} else {
		std::string s = op->Name();
		s += "\\ : a";
		wrongType(s.c_str(), "Real, List or String", a);
	}
}

static void DoIPairwise(Thread& th, BinaryOp* op)
{
	V b = th.pop();
	V a = th.pop();
	if (a.isVList()) {
		O s = new List(new IPairsOpGen(th, a, b, op));
		th.push(s);
	} else if (a.isZList()) {
		O s = new List(new IPairsOpZGen(th, a, b.asFloat(), op));
		th.push(s);
	} else if (a.isReal() || a.isString()) {
		th.push(a);
	} else {
		std::string s = op->Name();
		s += "^ : a";
		wrongType(s.c_str(), "Real, List or String", a);
	}
}

static void DoIScan(Thread& th, BinaryOp* op)
{
	V b = th.pop();
	V a = th.pop();
	if (a.isVList()) {
		O s = new List(new IScanOpGen(th, a, b, op));
		th.push(s);
	} else if (a.isZList()) {
		O s = new List(new IScanOpZGen(th, a, b.asFloat(), op));
		th.push(s);
	} else if (a.isReal() || a.isString()) {
		th.push(a);
	} else {
		std::string s = op->Name();
		s += "\\ : a";
		wrongType(s.c_str(), "Real, List or String", a);
	}
}

static void DoReduce(Thread& th, BinaryOp* op)
{
	int n;
	V a = th.pop();
	if (a.isVList()) {
		if (!a.isFinite()) indefiniteOp(op->Name(), "/");
		V z, *x;
		int xstride;
		VIn _a(a);
		n = 1;
		if (!_a(th, n,xstride,x)) {
			z = *x;
			_a.advance(n);
			while(1) {
				n = kDefaultVBlockSize;
				if (_a(th, n,xstride, x)) break;
				op->reduce(th, n, z, x, xstride);
				_a.advance(n);
			}
		}
		th.push(z);
	} else if (a.isZList()) {
		if (!a.isFinite()) indefiniteOp(op->Name(), "/");
		Z z = 0., *x;
		int xstride;
		ZIn _a(a);
		n = 1;
		if (!_a(th, n,xstride,x)) {
			z = *x;
			_a.advance(n);
			while(1) {
				n = th.rate.blockSize;
				if (_a(th, n,xstride, x)) break;
				op->reducez(n, z, x, xstride);
				_a.advance(n);
			}
		}
		th.push(z);
	} else if (a.isReal() || a.isString()) {
		th.push(a);
	} else {
		std::string s = op->Name();
		s += "\\ : a";
		wrongType(s.c_str(), "Real, List or String", a);
	}
}

static void DoIReduce(Thread& th, BinaryOp* op)
{
	int n;
	V b = th.pop();
	V a = th.pop();
	if (a.isVList()) {
		if (!a.isFinite()) indefiniteOp(op->Name(), "/");
		V z = b, *x;
		b = 0.;
		int xstride;
		VIn _a(a);
		while(1) {
			n = kDefaultVBlockSize;
			if (_a(th, n,xstride, x)) break;
			op->reduce(th, n, z, x, xstride);
			_a.advance(n);
		}
		th.push(z);
	} else if (a.isZList()) {
		if (!a.isFinite()) indefiniteOp(op->Name(), "/");
		Z z = b.asFloat(), *x;
		b = 0.;
		int xstride;
		ZIn _a(a);
		while(1) {
			n = th.rate.blockSize;
			if (_a(th, n,xstride, x)) break;
			op->reducez(n, z, x, xstride);
			_a.advance(n);
		}
		th.push(z);
	} else if (a.isReal() || a.isString()) {
		th.push(a);
	} else {
		std::string s = op->Name();
		s += "\\ : a";
		wrongType(s.c_str(), "Real, List or String", a);
	}
}

#define UNARY_OP_PRIM(NAME) \
	static void NAME##_(Thread& th, Prim* prim) \
	{ \
		V a = th.pop(); \
		V c = a.unaryOp(th, &gUnaryOp_##NAME); \
		th.push(c); \
	} \

#define BINARY_OP_PRIM(NAME) \
	static void NAME##_(Thread& th, Prim* prim) \
	{ \
		V b = th.pop(); \
		V a = th.pop(); \
		V c = a.binaryOp(th, &gBinaryOp_##NAME, b); \
		th.push(c); \
	} \
	static void NAME##_reduce_(Thread& th, Prim* prim) \
	{ \
		DoReduce(th, &gBinaryOp_##NAME); \
	} \
	static void NAME##_scan_(Thread& th, Prim* prim) \
	{ \
		DoScan(th, &gBinaryOp_##NAME); \
	} \
	static void NAME##_pairs_(Thread& th, Prim* prim) \
	{ \
		DoPairwise(th, &gBinaryOp_##NAME); \
	} \
	static void NAME##_iscan_(Thread& th, Prim* prim) \
	{ \
		DoIScan(th, &gBinaryOp_##NAME); \
	} \
	static void NAME##_ipairs_(Thread& th, Prim* prim) \
	{ \
		DoIPairwise(th, &gBinaryOp_##NAME); \
	} \

#define DEFINE_UNOP_FLOAT(NAME, CODE) \
	struct UnaryOp_##NAME : public UnaryOp { \
		virtual const char *Name() { return #NAME; } \
		virtual double op(double a) { return CODE; } \
		virtual void loopz(int n, const Z *aa, int astride, Z *out) { \
			LOOP(i,n) { Z a = *aa; out[i] = CODE; aa += astride; } \
		} \
	}; \
	UnaryOp_##NAME gUnaryOp_##NAME; \
	UnaryOp* gUnaryOpPtr_##NAME = &gUnaryOp_##NAME; \
	UNARY_OP_PRIM(NAME)

#define DEFINE_UNOP_FLOATVV(NAME, CODE, VVNAME) \
	struct UnaryOp_##NAME : public UnaryOp { \
		virtual const char *Name() { return #NAME; } \
		virtual double op(double a) { return CODE; } \
		virtual void loopz(int n, const Z *x, int astride, Z *y) { \
			if (astride == 1) { \
				VVNAME(y, x, &n); \
			} else { \
				LOOP(i,n) { Z a = *x; y[i] = CODE; x += astride; } \
			} \
		} \
	}; \
	UnaryOp_##NAME gUnaryOp_##NAME; \
	UnaryOp* gUnaryOpPtr_##NAME = &gUnaryOp_##NAME; \
	UNARY_OP_PRIM(NAME)

#define DEFINE_UNOP_FLOATVV2(NAME, CODE, VVCODE) \
	struct UnaryOp_##NAME : public UnaryOp { \
		virtual const char *Name() { return #NAME; } \
		virtual double op(double a) { return CODE; } \
		virtual void loopz(int n, const Z *aa, int astride, Z *out) { \
			if (astride == 1) { \
				VVCODE; \
			} else { \
				LOOP(i,n) { Z a = *aa; out[i] = CODE; aa += astride; } \
			} \
		} \
	}; \
	UnaryOp_##NAME gUnaryOp_##NAME; \
	UnaryOp* gUnaryOpPtr_##NAME = &gUnaryOp_##NAME; \
	UNARY_OP_PRIM(NAME)

#define DEFINE_UNOP_INT(NAME, CODE) \
	struct UnaryOp_##NAME : public UnaryOp { \
		virtual const char *Name() { return #NAME; } \
		virtual double op(double aa) { \
			int64_t a = (int64_t)aa; \
			return (double)(CODE); \
		} \
		virtual void loopz(int n, const Z *aa, int astride, Z *out) { \
			LOOP(i,n) { int64_t a = (int64_t)*aa; out[i] = (Z)(CODE); aa += astride; } \
		} \
	}; \
	UnaryOp_##NAME gUnaryOp_##NAME; \
	UnaryOp* gUnaryOpPtr_##NAME = &gUnaryOp_##NAME; \
	UNARY_OP_PRIM(NAME)

#define DEFINE_UNOP_BOOL_FLOAT(NAME, CODE) \
	struct UnaryOp_##NAME : public UnaryOp { \
		virtual const char *Name() { return #NAME; } \
		virtual double op(double a) { return (CODE) ? 1. : 0.; } \
		virtual void loopz(int n, const Z *aa, int astride, Z *out) { \
			LOOP(i,n) { Z a = *aa; out[i] = (CODE) ? 1. : 0.; aa += astride; } \
		} \
	}; \
	UnaryOp_##NAME gUnaryOp_##NAME; \
	UnaryOp* gUnaryOpPtr_##NAME = &gUnaryOp_##NAME; \
	UNARY_OP_PRIM(NAME)

#define DEFINE_UNOP_BOOL_INT(NAME, CODE) \
	struct UnaryOp_##NAME : public UnaryOp { \
		virtual const char *Name() { return #NAME; } \
		virtual double op(double aa) { \
			int64_t a = (int64_t)aa; \
			return (CODE) ? 1. : 0.; \
		} \
		virtual void loopz(int n, const Z *aa, int astride, Z *out) { \
			LOOP(i,n) { int64_t a = (int64_t)*aa; out[i] = (CODE) ? 1. : 0.; aa += astride; } \
		} \
	}; \
	UnaryOp_##NAME gUnaryOp_##NAME; \
	UnaryOp* gUnaryOpPtr_##NAME = &gUnaryOp_##NAME; \
	UNARY_OP_PRIM(NAME)

#define DEFINE_BINOP_FLOAT(NAME, CODE) \
	struct BinaryOp_##NAME : public BinaryOp { \
		virtual const char *Name() { return #NAME; } \
		virtual double op(double a, double b) { return CODE; } \
		virtual void loopz(int n, const Z *aa, int astride, const Z *bb, int bstride, Z *out) { \
			LOOP(i,n) { Z a = *aa; Z b = *bb; out[i] = CODE; aa += astride; bb += bstride; } \
		} \
		virtual void pairsz(int n, Z& z, Z *aa, int astride, Z *out) { \
			Z b = z; \
			LOOP(i,n) { Z a = *aa; out[i] = CODE; b = a; aa += astride; } \
			z = b; \
		} \
		virtual void scanz(int n, Z& z, Z *aa, int astride, Z *out) { \
			Z a = z; \
			LOOP(i,n) { Z b = *aa; out[i] = a = CODE; aa += astride; } \
			z = a; \
		} \
		virtual void reducez(int n, Z& z, Z *aa, int astride) { \
			Z a = z; \
			LOOP(i,n) { Z b = *aa; a = CODE; aa += astride; } \
			z = a; \
		} \
	}; \
	BinaryOp_##NAME gBinaryOp_##NAME; \
	BinaryOp* gBinaryOpPtr_##NAME = &gBinaryOp_##NAME; \
	BINARY_OP_PRIM(NAME)

#define DEFINE_BINOP_FLOAT_STRING(NAME, CODE, STRCODE) \
	struct BinaryOp_##NAME : public BinaryOp { \
		virtual const char *Name() { return #NAME; } \
		virtual double op(double a, double b) { return CODE; } \
		virtual void loopz(int n, const Z *aa, int astride, const Z *bb, int bstride, Z *out) { \
			LOOP(i,n) { Z a = *aa; Z b = *bb; out[i] = CODE; aa += astride; bb += bstride; } \
		} \
		virtual void pairsz(int n, Z& z, Z *aa, int astride, Z *out) { \
			Z b = z; \
			LOOP(i,n) { Z a = *aa; out[i] = CODE; b = a; aa += astride; } \
			z = b; \
		} \
		virtual void scanz(int n, Z& z, Z *aa, int astride, Z *out) { \
			Z a = z; \
			LOOP(i,n) { Z b = *aa; out[i] = a = CODE; aa += astride; } \
			z = a; \
		} \
		virtual void reducez(int n, Z& z, Z *aa, int astride) { \
			Z a = z; \
			LOOP(i,n) { Z b = *aa; a = CODE; aa += astride; } \
			z = a; \
		} \
		virtual V stringOp(P<String> const& aa, P<String> const& bb) { \
			const char* a = aa->s; \
			const char* b = bb->s; \
			return (STRCODE); \
		} \
	}; \
	BinaryOp_##NAME gBinaryOp_##NAME; \
	BinaryOp* gBinaryOpPtr_##NAME = &gBinaryOp_##NAME; \
	BINARY_OP_PRIM(NAME)

#define DEFINE_BINOP_FLOATVV(NAME, CODE, VVCODE) \
	struct BinaryOp_##NAME : public BinaryOp { \
		virtual const char *Name() { return #NAME; } \
		virtual double op(double a, double b) { return CODE; } \
		virtual void loopz(int n, const Z *aa, int astride, const Z *bb, int bstride, Z *out) { \
			VVCODE; \
		} \
		virtual void pairsz(int n, Z& z, Z *aa, int astride, Z *out) { \
			Z b = z; \
			LOOP(i,n) { Z a = *aa; out[i] = CODE; b = a; aa += astride; } \
			z = b; \
		} \
		virtual void scanz(int n, Z& z, Z *aa, int astride, Z *out) { \
			Z a = z; \
			LOOP(i,n) { Z b = *aa; out[i] = a = CODE; aa += astride; } \
			z = a; \
		} \
		virtual void reducez(int n, Z& z, Z *aa, int astride) { \
			Z a = z; \
			LOOP(i,n) { Z b = *aa; a = CODE; aa += astride; } \
			z = a; \
		} \
	}; \
	BinaryOp_##NAME gBinaryOp_##NAME; \
	BinaryOp* gBinaryOpPtr_##NAME = &gBinaryOp_##NAME; \
	BINARY_OP_PRIM(NAME)

#define DEFINE_BINOP_FLOATVV1(NAME, CODE, VVCODE) \
	struct BinaryOp_##NAME : public BinaryOp { \
		virtual const char *Name() { return #NAME; } \
		virtual double op(double a, double b) { return CODE; } \
		virtual void loopz(int n, const Z *aa, int astride, const Z *bb, int bstride, Z *out) { \
			if (astride == 1 && bstride == 1) { \
				 VVCODE; \
			} else { \
				LOOP(i,n) { Z a = *aa; Z b = *bb; out[i] = CODE; aa += astride; bb += bstride; } \
			} \
		} \
		virtual void pairsz(int n, Z& z, Z *aa, int astride, Z *out) { \
			Z b = z; \
			LOOP(i,n) { Z a = *aa; out[i] = CODE; b = a; aa += astride; } \
			z = b; \
		} \
		virtual void scanz(int n, Z& z, Z *aa, int astride, Z *out) { \
			Z a = z; \
			LOOP(i,n) { Z b = *aa; out[i] = a = CODE; aa += astride; } \
			z = a; \
		} \
		virtual void reducez(int n, Z& z, Z *aa, int astride) { \
			Z a = z; \
			LOOP(i,n) { Z b = *aa; a = CODE; aa += astride; } \
			z = a; \
		} \
	}; \
	BinaryOp_##NAME gBinaryOp_##NAME; \
	BinaryOp* gBinaryOpPtr_##NAME = &gBinaryOp_##NAME; \
	BINARY_OP_PRIM(NAME)

#define DEFINE_BINOP_INT(NAME, CODE) \
	struct BinaryOp_##NAME : public BinaryOp { \
		virtual const char *Name() { return #NAME; } \
		virtual double op(double aa, double bb) { \
			int64_t a = (int64_t)aa; \
			int64_t b = (int64_t)bb; \
			return (double)(CODE); \
		} \
		virtual void loopz(int n, const Z *aa, int astride, const Z *bb, int bstride, Z *out) { \
			LOOP(i,n) { int64_t a = (int64_t)*aa; int64_t b = (int64_t)*bb; out[i] = (Z)(CODE); aa += astride; bb += bstride; } \
		} \
		virtual void pairsz(int n, Z& z, Z *aa, int astride, Z *out) { \
			int64_t b = (int64_t)z; \
			LOOP(i,n) { int64_t a = (int64_t)*aa; out[i] = CODE; b = a; aa += astride; } \
			z = (Z)b; \
		} \
		virtual void scanz(int n, Z& z, Z *aa, int astride, Z *out) { \
			int64_t a = (int64_t)z; \
			LOOP(i,n) { int64_t b = (int64_t)*aa; Z x = CODE; out[i] = x; a = (int64_t)x; aa += astride; } \
			z = a; \
		} \
		virtual void reducez(int n, Z& z, Z *aa, int astride) { \
			int64_t a = (int64_t)z; \
			LOOP(i,n) { int64_t b = (int64_t)*aa; a = (int64_t)(CODE); aa += astride; } \
			z = (Z)a; \
		} \
	}; \
	BinaryOp_##NAME gBinaryOp_##NAME; \
	BinaryOp* gBinaryOpPtr_##NAME = &gBinaryOp_##NAME; \
	BINARY_OP_PRIM(NAME)

#define DEFINE_BINOP_BOOL_FLOAT(NAME, CODE, STRCODE) \
	struct BinaryOp_##NAME : public BinaryOp { \
		virtual const char *Name() { return #NAME; } \
		virtual double op(double a, double b) { return (CODE) ? 1. : 0.; } \
		virtual void loopz(int n, const Z *aa, int astride, const Z *bb, int bstride, Z *out) { \
			LOOP(i,n) { Z a = *aa; Z b = *bb; out[i] = (CODE) ? 1. : 0.; aa += astride; bb += bstride; } \
		} \
		virtual void pairsz(int n, Z& z, Z *aa, int astride, Z *out) { \
			Z b = z; \
			LOOP(i,n) { Z a = *aa; out[i] = (CODE) ? 1. : 0.; b = a; aa += astride; } \
			z = b; \
		} \
		virtual void scanz(int n, Z& z, Z *aa, int astride, Z *out) { \
			Z a = z; \
			LOOP(i,n) { Z b = *aa; out[i] = a = (CODE) ? 1. : 0.; aa += astride; } \
			z = a; \
		} \
		virtual void reducez(int n, Z& z, Z *aa, int astride) { \
			Z a = z; \
			LOOP(i,n) { Z b = *aa; a = (CODE) ? 1. : 0.; aa += astride; } \
			z = a; \
		} \
		virtual V stringOp(P<String> const& aa, P<String> const& bb) { \
			const char* a = aa->s; \
			const char* b = bb->s; \
			return (STRCODE) ? 1. : 0.; \
		} \
	}; \
	BinaryOp_##NAME gBinaryOp_##NAME; \
	BinaryOp* gBinaryOpPtr_##NAME = &gBinaryOp_##NAME; \
	BINARY_OP_PRIM(NAME)

DEFINE_UNOP_BOOL_INT(isalnum, isalnum((int)a))
DEFINE_UNOP_BOOL_INT(isalpha, isalpha((int)a))
DEFINE_UNOP_BOOL_INT(isblank, isblank((int)a))
DEFINE_UNOP_BOOL_INT(iscntrl, iscntrl((int)a))
DEFINE_UNOP_BOOL_INT(isdigit, isdigit((int)a))
DEFINE_UNOP_BOOL_INT(isgraph, isgraph((int)a))
DEFINE_UNOP_BOOL_INT(islower, islower((int)a))
DEFINE_UNOP_BOOL_INT(isprint, isprint((int)a))
DEFINE_UNOP_BOOL_INT(ispunct, ispunct((int)a))
DEFINE_UNOP_BOOL_INT(isspace, isspace((int)a))
DEFINE_UNOP_BOOL_INT(isupper, isupper((int)a))
DEFINE_UNOP_BOOL_INT(isxdigit, isxdigit((int)a))
DEFINE_UNOP_BOOL_INT(isascii, isascii((int)a))

DEFINE_UNOP_BOOL_FLOAT(not, a == 0.)
DEFINE_UNOP_BOOL_FLOAT(nonneg, a >= 0.)
DEFINE_UNOP_BOOL_FLOAT(nonpos, a <= 0.)
DEFINE_UNOP_BOOL_FLOAT(isneg, a < 0.)
DEFINE_UNOP_BOOL_FLOAT(ispos, a > 0.)
DEFINE_UNOP_BOOL_FLOAT(iszero, a == 0.)
DEFINE_UNOP_BOOL_FLOAT(isint, (a - floor(a)) == 0.)
DEFINE_UNOP_BOOL_INT(iseven, !(a & 1))
DEFINE_UNOP_BOOL_INT(isodd, (a & 1))
DEFINE_UNOP_BOOL_INT(isprime, isprime(a))

DEFINE_UNOP_BOOL_FLOAT(isfinite, std::isfinite(a))
DEFINE_UNOP_BOOL_FLOAT(isinf, std::isinf(a))
DEFINE_UNOP_BOOL_FLOAT(isnormal, std::isnormal(a))
DEFINE_UNOP_BOOL_FLOAT(isnan, std::isnan(a))
DEFINE_UNOP_BOOL_FLOAT(signbit, std::signbit(a))

struct UnaryOp_ToZero : public UnaryOp {
	virtual const char *Name() { return "ToZero"; }
	virtual double op(double a) { return 0.; }
	virtual void loopz(int n, const Z *aa, int astride, Z *out) {
		LOOP(i,n) { out[i] = 0.; }
	}
};
UnaryOp_ToZero gUnaryOp_ToZero;

DEFINE_UNOP_FLOATVV2(neg, -a, vDSP_vnegD(const_cast<Z*>(aa), astride, out, 1, n))
DEFINE_UNOP_FLOAT(sgn, sc_sgn(a))
DEFINE_UNOP_FLOATVV(abs, fabs(a), vvfabs)

DEFINE_UNOP_INT(tolower, tolower((int)a))
DEFINE_UNOP_INT(toupper, toupper((int)a))
DEFINE_UNOP_INT(toascii, toascii((int)a))

DEFINE_UNOP_FLOATVV2(frac, a - floor(a), vvfloor(out, aa, &n); vDSP_vsubD(out, 1, aa, astride, out, 1, n))
DEFINE_UNOP_FLOATVV(floor, floor(a), vvfloor)
DEFINE_UNOP_FLOATVV(ceil, ceil(a), vvceil)
DEFINE_UNOP_FLOATVV(rint, rint(a), vvnint)

DEFINE_UNOP_FLOAT(erf, erf(a))
DEFINE_UNOP_FLOAT(erfc, erfc(a))

DEFINE_UNOP_FLOATVV(recip, 1./a, vvrec)
DEFINE_UNOP_FLOATVV(sqrt, sc_sqrt(a), vvsqrt)
DEFINE_UNOP_FLOATVV(rsqrt, 1./sc_sqrt(a), vvrsqrt)
DEFINE_UNOP_FLOAT(cbrt, cbrt(a))
DEFINE_UNOP_FLOATVV2(ssq, copysign(a*a, a), vDSP_vssqD(aa, astride, out, 1, n))
DEFINE_UNOP_FLOATVV2(sq, a*a, vDSP_vsqD(aa, astride, out, 1, n))
DEFINE_UNOP_FLOAT(cb, a*a*a)
DEFINE_UNOP_FLOAT(pow4, sc_fourth(a))
DEFINE_UNOP_FLOAT(pow5, sc_fifth(a))
DEFINE_UNOP_FLOAT(pow6, sc_sixth(a))
DEFINE_UNOP_FLOAT(pow7, sc_seventh(a))
DEFINE_UNOP_FLOAT(pow8, sc_eighth(a))
DEFINE_UNOP_FLOAT(pow9, sc_ninth(a))

DEFINE_UNOP_FLOATVV(exp, exp(a), vvexp)
DEFINE_UNOP_FLOATVV(exp2, exp2(a), vvexp2)
DEFINE_UNOP_FLOAT(exp10, pow(10., a))
DEFINE_UNOP_FLOATVV(expm1, expm1(a), vvexpm1)
DEFINE_UNOP_FLOATVV(log, sc_log(a), vvlog)
DEFINE_UNOP_FLOATVV(log2, sc_log2(a), vvlog2)
DEFINE_UNOP_FLOATVV(log10, sc_log10(a), vvlog10)
DEFINE_UNOP_FLOATVV(log1p, log1p(a), vvlog1p)
DEFINE_UNOP_FLOATVV(logb, logb(a), vvlogb)

DEFINE_UNOP_FLOAT(sinc, sc_sinc(a))

DEFINE_UNOP_FLOATVV(sin, sin(a), vvsin)
DEFINE_UNOP_FLOATVV(cos, cos(a), vvcos)
DEFINE_UNOP_FLOATVV2(sin1, sin(a * kTwoPi), Z b = kTwoPi; vDSP_vsmulD(const_cast<Z*>(aa), astride, &b, out, 1, n); vvsin(out, out, &n))
DEFINE_UNOP_FLOATVV2(cos1, cos(a * kTwoPi), Z b = kTwoPi; vDSP_vsmulD(const_cast<Z*>(aa), astride, &b, out, 1, n); vvcos(out, out, &n))
DEFINE_UNOP_FLOATVV(tan, tan(a), vvtan)
DEFINE_UNOP_FLOATVV(asin, asin(a), vvasin)
DEFINE_UNOP_FLOATVV(acos, acos(a), vvacos)
DEFINE_UNOP_FLOATVV(atan, atan(a), vvatan)
DEFINE_UNOP_FLOATVV(sinh, sinh(a), vvsinh)
DEFINE_UNOP_FLOATVV(cosh, cosh(a), vvcosh)
DEFINE_UNOP_FLOATVV(tanh, tanh(a), vvtanh)
DEFINE_UNOP_FLOATVV(asinh, asinh(a), vvasinh)
DEFINE_UNOP_FLOATVV(acosh, acosh(a), vvacosh)
DEFINE_UNOP_FLOATVV(atanh, atanh(a), vvatanh)

DEFINE_UNOP_FLOAT(J0, j0(a))
DEFINE_UNOP_FLOAT(J1, j1(a))
DEFINE_UNOP_FLOAT(Y0, y0(a))
DEFINE_UNOP_FLOAT(Y1, y1(a))

DEFINE_UNOP_FLOAT(tgamma, tgamma(a))
DEFINE_UNOP_FLOAT(lgamma, lgamma(a))

static void sc_clipv(int n, const Z* in, Z* out, Z a, Z b)
{
	for (int i = 0; i < n; ++i) {
		out[i] = std::clamp(in[i], a, b);
	}
}

DEFINE_UNOP_FLOATVV2(inc, a+1, Z b = 1.; vDSP_vsaddD(const_cast<Z*>(aa), astride, &b, out, 1, n))
DEFINE_UNOP_FLOATVV2(dec, a-1, Z b = -1.; vDSP_vsaddD(const_cast<Z*>(aa), astride, &b, out, 1, n))
DEFINE_UNOP_FLOATVV2(half, a*.5, Z b = .5; vDSP_vsmulD(aa, astride, &b, out, 1, n))
DEFINE_UNOP_FLOATVV2(twice, a*2., Z b = 2.; vDSP_vsmulD(const_cast<Z*>(aa), astride, &b, out, 1, n))

DEFINE_UNOP_FLOATVV2(biuni, a*.5+.5, Z b = .5; vDSP_vsmulD(const_cast<Z*>(aa), astride, &b, out, 1, n); vDSP_vsaddD(out, 1, &b, out, 1, n))
DEFINE_UNOP_FLOATVV2(unibi, a*2.-1., Z b = 2.; Z c = -1.; vDSP_vsmulD(aa, astride, &b, out, 1, n); vDSP_vsaddD(out, 1, &c, out, 1, n))
DEFINE_UNOP_FLOATVV2(biunic, std::clamp(a,-1.,1.)*.5+.5, Z b = .5; sc_clipv(n, aa, out, -1., 1.); vDSP_vsmulD(out, astride, &b, out, 1, n); vDSP_vsaddD(out, 1, &b, out, 1, n))
DEFINE_UNOP_FLOATVV2(unibic, std::clamp(a,0.,1.)*2.-1., Z b = 2.; Z c = -1.; sc_clipv(n, aa, out, 0., 1.); vDSP_vsmulD(out, astride, &b, out, 1, n); vDSP_vsaddD(out, 1, &c, out, 1, n))
DEFINE_UNOP_FLOAT(cmpl, 1.-a)

DEFINE_UNOP_FLOATVV2(ampdb,     sc_ampdb(a), Z b = 1.; vDSP_vdbconD(const_cast<Z*>(aa), astride, &b, out, 1, n, 1))
DEFINE_UNOP_FLOAT(dbamp,     sc_dbamp(a))

DEFINE_UNOP_FLOAT(hzo,   sc_hzoct(a))
DEFINE_UNOP_FLOAT(ohz,   sc_octhz(a))

DEFINE_UNOP_FLOAT(hzst,   sc_hzkey(a))
DEFINE_UNOP_FLOAT(sthz,   sc_keyhz(a))

DEFINE_UNOP_FLOAT(hznn,   sc_hznn(a))
DEFINE_UNOP_FLOAT(nnhz,   sc_nnhz(a))

DEFINE_UNOP_FLOAT(centsratio, sc_centsratio(a))
DEFINE_UNOP_FLOAT(ratiocents, sc_ratiocents(a))

DEFINE_UNOP_FLOAT(semiratio, sc_semiratio(a))
DEFINE_UNOP_FLOAT(ratiosemi, sc_ratiosemi(a))

DEFINE_UNOP_FLOATVV2(degrad, a*kDegToRad, Z b = kDegToRad; vDSP_vsmulD(aa, astride, &b, out, 1, n))
DEFINE_UNOP_FLOATVV2(raddeg, a*kRadToDeg, Z b = kRadToDeg; vDSP_vsmulD(aa, astride, &b, out, 1, n))
DEFINE_UNOP_FLOATVV2(minsec, a*kMinToSecs, Z b = kMinToSecs; vDSP_vsmulD(aa, astride, &b, out, 1, n))
DEFINE_UNOP_FLOATVV2(secmin, a*kSecsToMin, Z b = kSecsToMin; vDSP_vsmulD(aa, astride, &b, out, 1, n))
DEFINE_UNOP_FLOATVV2(bpmsec, kMinToSecs / a, Z b = kMinToSecs; vDSP_svdivD(&b, const_cast<double*>(aa), astride, out, 1, n))

DEFINE_UNOP_FLOAT(distort,  sc_distort(a))
DEFINE_UNOP_FLOAT(softclip, sc_softclip(a))

DEFINE_UNOP_FLOAT(rectWin,  sc_rectWindow(a))
DEFINE_UNOP_FLOAT(triWin,   sc_triWindow(a))
DEFINE_UNOP_FLOAT(bitriWin, sc_bitriWindow(a))
DEFINE_UNOP_FLOAT(hanWin,   sc_hanWindow(a))
DEFINE_UNOP_FLOAT(sinWin,   sc_sinWindow(a))
DEFINE_UNOP_FLOAT(ramp,     sc_ramp(a))
DEFINE_UNOP_FLOAT(scurve,   sc_scurve(a))
DEFINE_UNOP_FLOAT(sigm,		a/sqrt(1.+a*a))

DEFINE_UNOP_FLOAT(zapgremlins, zapgremlins(a))

////////////////////////////////////////////////////////////////////////////////////////////////////////

DEFINE_BINOP_BOOL_FLOAT(lt, a <  b, strcmp(a, b) < 0)
DEFINE_BINOP_BOOL_FLOAT(le, a <= b, strcmp(a, b) <= 0)
DEFINE_BINOP_BOOL_FLOAT(gt, a >  b, strcmp(a, b) > 0)
DEFINE_BINOP_BOOL_FLOAT(ge, a >= b, strcmp(a, b) >= 0)
DEFINE_BINOP_BOOL_FLOAT(eq, a == b, strcmp(a, b) == 0)
DEFINE_BINOP_BOOL_FLOAT(ne, a != b, strcmp(a, b) != 0)
DEFINE_BINOP_FLOAT_STRING(cmp,  sc_cmp(a, b), sc_sgn(strcmp(a, b)))

DEFINE_BINOP_FLOATVV1(copysign, copysign(a, b), vvcopysign(out, const_cast<Z*>(aa), bb, &n)) // bug in vForce.h requires const_cast
DEFINE_BINOP_FLOATVV1(nextafter, nextafter(a, b), vvnextafter(out, const_cast<Z*>(aa), bb, &n)) // bug in vForce.h requires const_cast

// identity optimizations of basic operators.

	struct BinaryOp_plus : public BinaryOp {
		virtual const char *Name() { return "plus"; }
		virtual double op(double a, double b) { return a + b; }
		virtual void loopz(int n, const Z *aa, int astride, const Z *bb, int bstride, Z *out) {
			if (astride == 0) {
				if (*aa == 0.) {
					memcpy(out, bb, n * sizeof(Z));
					//LOOP(i,n) { out[i] = *bb; bb += bstride; }
				} else {
					vDSP_vsaddD(const_cast<Z*>(bb), bstride, const_cast<Z*>(aa), out, 1, n);
				}
			} else if (bstride == 0 ) {
				if (*bb == 0.) {
					memcpy(out, aa, n * sizeof(Z));
					//LOOP(i,n) { out[i] = *aa; aa += bstride; }
				} else {
					vDSP_vsaddD(const_cast<Z*>(aa), astride, const_cast<Z*>(bb), out, 1, n);
				}
			} else {
				vDSP_vaddD(aa, astride, bb, bstride, out, 1, n);
				//LOOP(i,n) { Z a = *aa; Z b = *bb; out[i] = a + b; aa += astride; bb += bstride; }
			}
		}
		virtual void pairsz(int n, Z& z, Z *aa, int astride, Z *out) {
			Z b = z;
			LOOP(i,n) { Z a = *aa; out[i] = a + b; b = a; aa += astride; }
			z = b;
		}
		virtual void scanz(int n, Z& z, Z *aa, int astride, Z *out) {
			Z a = z;
			LOOP(i,n) { Z b = *aa; out[i] = a = a + b; aa += astride; }
			z = a;
		}
		virtual void reducez(int n, Z& z, Z *aa, int astride) {
			Z a = z;
			LOOP(i,n) { Z b = *aa; a = a + b; aa += astride; }
			z = a;
		}
		virtual V makeVList(Thread& th, Arg a, Arg b)
		{
			if (a.isReal() && a.f == 0.) return b;
			if (b.isReal() && b.f == 0.) return a;
			return new List(new BinaryOpGen(th, this, a, b));
		}

		virtual V makeZList(Thread& th, Arg a, Arg b)
		{
			if (a.isReal() && a.f == 0.) return b;
			if (b.isReal() && b.f == 0.) return a;
			return new List(new BinaryOpZGen(th, this, a, b));
		}
	};
	BinaryOp_plus gBinaryOp_plus;
	BinaryOp* gBinaryOpPtr_plus = &gBinaryOp_plus;
	BINARY_OP_PRIM(plus)

	struct BinaryOp_plus_link : public BinaryOp {
		virtual const char *Name() { return "plus"; }
		virtual double op(double a, double b) { return a + b; }
		virtual void loopz(int n, const Z *aa, int astride, const Z *bb, int bstride, Z *out) {
			if (astride == 0) {
				if (*aa == 0.) {
					memcpy(out, bb, n * sizeof(Z));
					//LOOP(i,n) { out[i] = *bb; bb += bstride; }
				} else {
					vDSP_vsaddD(const_cast<Z*>(bb), bstride, const_cast<Z*>(aa), out, 1, n);
				}
			} else if (bstride == 0 ) {
				if (*bb == 0.) {
					memcpy(out, aa, n * sizeof(Z));
					//LOOP(i,n) { out[i] = *aa; aa += bstride; }
				} else {
					vDSP_vsaddD(const_cast<Z*>(aa), astride, const_cast<Z*>(bb), out, 1, n);
				}
			} else {
				vDSP_vaddD(aa, astride, bb, bstride, out, 1, n);
				//LOOP(i,n) { Z a = *aa; Z b = *bb; out[i] = a + b; aa += astride; bb += bstride; }
			}
		}
		virtual void pairsz(int n, Z& z, Z *aa, int astride, Z *out) {
			Z b = z;
			LOOP(i,n) { Z a = *aa; out[i] = a + b; b = a; aa += astride; }
			z = b;
		}
		virtual void scanz(int n, Z& z, Z *aa, int astride, Z *out) {
			Z a = z;
			LOOP(i,n) { Z b = *aa; out[i] = a = a + b; aa += astride; }
			z = a;
		}
		virtual void reducez(int n, Z& z, Z *aa, int astride) {
			Z a = z;
			LOOP(i,n) { Z b = *aa; a = a + b; aa += astride; }
			z = a;
		}
		virtual V makeVList(Thread& th, Arg a, Arg b)
		{
			if (a.isReal() && a.f == 0.) return b;
			if (b.isReal() && b.f == 0.) return a;
			return new List(new BinaryOpLinkGen(th, this, a, b));
		}

		virtual V makeZList(Thread& th, Arg a, Arg b)
		{
			if (a.isReal() && a.f == 0.) return b;
			if (b.isReal() && b.f == 0.) return a;
			return new List(new BinaryOpLinkZGen(th, this, a, b));
		}
	};
	BinaryOp_plus_link gBinaryOp_plus_link;
	BinaryOp* gBinaryOpPtr_plus_link = &gBinaryOp_plus_link;
	BINARY_OP_PRIM(plus_link)

	struct BinaryOp_minus : public BinaryOp {
		virtual const char *Name() { return "minus"; }
		virtual double op(double a, double b) { return a - b; }
		virtual void loopz(int n, const Z *aa, int astride, const Z *bb, int bstride, Z *out) {
			if (astride == 0) {
				vDSP_vnegD(const_cast<Z*>(bb), bstride, out, 1, n);
				if (*aa != 0.) {
					vDSP_vnegD(const_cast<Z*>(bb), bstride, out, 1, n);
					vDSP_vsaddD(const_cast<Z*>(out), 1, const_cast<Z*>(aa), out, 1, n);
					//LOOP(i,n) { out[i] = *bb; bb += bstride; }
				}
			} else if (bstride == 0 ) {
				memcpy(out, aa, n * sizeof(Z));
				if (*bb != 0.) {
					Z b = -*bb;
					vDSP_vsaddD(const_cast<Z*>(out), 1, &b, out, 1, n);
					//LOOP(i,n) { out[i] = *aa; aa += bstride; }
				}
			} else {
				vDSP_vsubD(aa, astride, bb, bstride, out, 1, n);
				//LOOP(i,n) { Z a = *aa; Z b = *bb; out[i] = a + b; aa += astride; bb += bstride; }
			}
		}
		virtual void pairsz(int n, Z& z, Z *aa, int astride, Z *out) {
			Z b = z;
			LOOP(i,n) { Z a = *aa; out[i] = a - b; b = a; aa += astride; }
			z = b;
		}
		virtual void scanz(int n, Z& z, Z *aa, int astride, Z *out) {
			Z a = z;
			LOOP(i,n) { Z b = *aa; out[i] = a = a - b; aa += astride; }
			z = a;
		}
		virtual void reducez(int n, Z& z, Z *aa, int astride) {
			Z a = z;
			LOOP(i,n) { Z b = *aa; a = a - b; aa += astride; }
			z = a;
		}

		virtual V makeVList(Thread& th, Arg a, Arg b)
		{
			if (a.isReal() && a.f == 0.) return new List(new UnaryOpGen(th, &gUnaryOp_neg, b));
			if (b.isReal() && b.f == 0.) return a;
			return new List(new BinaryOpGen(th, this, a, b));
		}

		virtual V makeZList(Thread& th, Arg a, Arg b)
		{
			if (a.isReal() && a.f == 0.) return new List(new UnaryOpZGen(th, &gUnaryOp_neg, b));
			if (b.isReal() && b.f == 0.) return a;
			return new List(new BinaryOpZGen(th, this, a, b));
		}
	};
	BinaryOp_minus gBinaryOp_minus;
	BinaryOp* gBinaryOpPtr_minus = &gBinaryOp_minus;
	BINARY_OP_PRIM(minus)

	struct BinaryOp_mul : public BinaryOp {
		virtual const char *Name() { return "mul"; }
		virtual double op(double a, double b) { return a * b; }
		virtual void loopz(int n, const Z *aa, int astride, const Z *bb, int bstride, Z *out) {
			if (astride == 0) {
				if (*aa == 1.) {
					LOOP(i,n) { out[i] = *bb; bb += bstride; }
				} else if (*aa == 0.) {
					LOOP(i,n) { out[i] = 0.; }
				} else {
					vDSP_vsmulD(bb, bstride, aa, out, 1, n);
				}
			} else if (bstride == 0) {
				if (*bb == 1.) {
					LOOP(i,n) { out[i] = *aa; aa += astride; }
				} else if (*bb == 0.) {
					LOOP(i,n) { out[i] = 0.; }
				} else {
					vDSP_vsmulD(aa, astride, bb, out, 1, n);
				}
			} else {
				//LOOP(i,n) { Z a = *aa; Z b = *bb; out[i] = a * b; aa += astride; bb += bstride; }
				vDSP_vmulD(aa, astride, bb, bstride, out, 1, n);
			}
		}
		virtual void pairsz(int n, Z& z, Z *aa, int astride, Z *out) {
			Z b = z;
			LOOP(i,n) { Z a = *aa; out[i] = a * b; b = a; aa += astride; }
			z = b;
		}
		virtual void scanz(int n, Z& z, Z *aa, int astride, Z *out) {
			Z a = z;
			LOOP(i,n) { Z b = *aa; out[i] = a = a * b; aa += astride; }
			z = a;
		}
		virtual void reducez(int n, Z& z, Z *aa, int astride) {
			Z a = z;
			LOOP(i,n) { Z b = *aa; a = a * b; aa += astride; }
			z = a;
		}

		virtual V makeVList(Thread& th, Arg a, Arg b)
		{
			if (a.isReal()) {
				if (a.f == 1.) return b;
				if (a.f == 0.) return new List(new UnaryOpGen(th, &gUnaryOp_ToZero, b));
				if (a.f == -1.) return new List(new UnaryOpGen(th, &gUnaryOp_neg, b));
			}
			if (b.isReal()) {
				if (b.f == 1.) return a;
				if (b.f == 0.) return new List(new UnaryOpGen(th, &gUnaryOp_ToZero, a));
				if (b.f == -1.) return new List(new UnaryOpGen(th, &gUnaryOp_neg, a));
			}
			return new List(new BinaryOpGen(th, this, a, b));
		}

		virtual V makeZList(Thread& th, Arg a, Arg b)
		{
			if (a.isReal()) {
				if (a.f == 1.) return b;
				if (a.f == 0.) return new List(new UnaryOpZGen(th, &gUnaryOp_ToZero, b));
				if (a.f == -1.) return new List(new UnaryOpZGen(th, &gUnaryOp_neg, b));
			}
			if (b.isReal()) {
				if (b.f == 1.) return a;
				if (b.f == 0.) return new List(new UnaryOpZGen(th, &gUnaryOp_ToZero, a));
				if (b.f == -1.) return new List(new UnaryOpZGen(th, &gUnaryOp_neg, a));
			}
			return new List(new BinaryOpZGen(th, this, a, b));
		}
	};
	BinaryOp_mul gBinaryOp_mul;
	BinaryOp* gBinaryOpPtr_mul = &gBinaryOp_mul;
	BINARY_OP_PRIM(mul)

	struct BinaryOp_div : public BinaryOp {
		virtual const char *Name() { return "div"; }
		virtual double op(double a, double b) { return a / b; }
		virtual void loopz(int n, const Z *aa, int astride, const Z *bb, int bstride, Z *out) {
			if (astride == 0 && *aa == 0.) {
				LOOP(i,n) { out[i] = 0.; }
			} else if (bstride == 0) {
				if (*bb == 1.) {
					LOOP(i,n) { out[i] = *aa; aa += bstride; }
				} else {
					Z rb = 1. / *bb;
					vDSP_vsmulD(const_cast<Z*>(aa), astride, &rb, out, 1, n);
				}
			} else {
				vDSP_vdivD(const_cast<Z*>(bb), bstride, const_cast<Z*>(aa), astride, out, 1, n);
				//LOOP(i,n) { Z a = *aa; Z b = *bb; out[i] = a / b; aa += astride; bb += bstride; }
			}
		}
		virtual void pairsz(int n, Z& z, Z *aa, int astride, Z *out) {
			Z b = z;
			LOOP(i,n) { Z a = *aa; out[i] = a / b; b = a; aa += astride; }
			z = b;
		}
		virtual void scanz(int n, Z& z, Z *aa, int astride, Z *out) {
			Z a = z;
			LOOP(i,n) { Z b = *aa; out[i] = a = a / b; aa += astride; }
			z = a;
		}
		virtual void reducez(int n, Z& z, Z *aa, int astride) {
			Z a = z;
			LOOP(i,n) { Z b = *aa; a = a / b; aa += astride; }
			z = a;
		}
		virtual V makeVList(Thread& th, Arg a, Arg b)
		{
			if (a.isReal() && a.f == 0.) return new List(new UnaryOpGen(th, &gUnaryOp_ToZero, b));
			if (b.isReal() && b.f == 1.) return a;
			return new List(new BinaryOpGen(th, this, a, b));
		}

		virtual V makeZList(Thread& th, Arg a, Arg b)
		{
			if (a.isReal() && a.f == 0.) return new List(new UnaryOpZGen(th, &gUnaryOp_ToZero, b));
			if (b.isReal() && b.f == 1.) return a;
			return new List(new BinaryOpZGen(th, this, a, b));
		}
	};
	BinaryOp_div gBinaryOp_div;
	BinaryOp* gBinaryOpPtr_div = &gBinaryOp_div;
	BINARY_OP_PRIM(div)

DEFINE_BINOP_FLOAT(mod, sc_fmod(a, b))
DEFINE_BINOP_FLOAT(remainder, remainder(a, b))

DEFINE_BINOP_INT(idiv, sc_div(a, b))
DEFINE_BINOP_INT(imod, sc_imod(a, b))

DEFINE_BINOP_FLOATVV1(pow, sc_pow(a, b), vvpow(out, bb, aa, &n))
DEFINE_BINOP_FLOATVV1(atan2, atan2(a, b), vvatan2(out, aa, bb, &n))

DEFINE_BINOP_FLOAT(Jn, jn((int)b, a))
DEFINE_BINOP_FLOAT(Yn, yn((int)b, a))

DEFINE_BINOP_FLOATVV(min, fmin(a, b), vDSP_vminD(const_cast<Z*>(aa), astride, const_cast<Z*>(bb), bstride, out, 1, n))
DEFINE_BINOP_FLOATVV(max, fmax(a, b), vDSP_vmaxD(const_cast<Z*>(aa), astride, const_cast<Z*>(bb), bstride, out, 1, n))
DEFINE_BINOP_FLOAT(dim, fdim(a, b))
DEFINE_BINOP_FLOAT(xor, fdim(a, b))

DEFINE_BINOP_FLOAT(avg2, (a + b) * .5)
DEFINE_BINOP_FLOAT(absdif, fabs(a - b))
DEFINE_BINOP_FLOATVV(hypot, hypot(a, b), vDSP_vdistD(const_cast<Z*>(aa), astride, const_cast<Z*>(bb), bstride, out, 1, n))
DEFINE_BINOP_FLOAT(sumsq, a*a + b*b)
DEFINE_BINOP_FLOAT(difsq, a*a - b*b)
DEFINE_BINOP_FLOAT(sqsum, sc_squared(a + b))
DEFINE_BINOP_FLOAT(sqdif, sc_squared(a - b))

DEFINE_BINOP_FLOAT(thresh,   a <  b  ? 0. : a)
DEFINE_BINOP_FLOAT(absthresh,  fabs(a) <  b  ? 0. : a)
DEFINE_BINOP_FLOAT(amclip,   b <= 0. ? 0. : a * b)
DEFINE_BINOP_FLOAT(scaleneg, a <  0. ? a * b : a)

DEFINE_BINOP_FLOAT(ring1, a * b + a)
DEFINE_BINOP_FLOAT(ring2, a * b + a + b)
DEFINE_BINOP_FLOAT(ring3, a*a*b)
DEFINE_BINOP_FLOAT(ring4, a*b*(a - b))

DEFINE_BINOP_INT(gcd, sc_gcd(a, b))
DEFINE_BINOP_INT(lcm, sc_lcm(a, b))

DEFINE_BINOP_FLOAT(clip2, std::clamp(a, -b, b))
DEFINE_BINOP_FLOAT(wrap2, sc_wrap(a, -b, b))
DEFINE_BINOP_FLOAT(fold2, sc_fold(a, -b, b))
DEFINE_BINOP_INT(iwrap2, sc_iwrap(a, -b, b))
DEFINE_BINOP_INT(ifold2, sc_ifold(a, -b, b))
DEFINE_BINOP_FLOAT(excess, a - std::clamp(a, -b, b))

DEFINE_BINOP_FLOAT(clip0, std::clamp(a, 0., b))
DEFINE_BINOP_FLOAT(wrap0, sc_wrap(a, 0., b))
DEFINE_BINOP_FLOAT(fold0, sc_fold(a, 0., b))

DEFINE_BINOP_FLOAT(round, sc_round(a, b))
DEFINE_BINOP_FLOAT(roundUp, sc_roundUp(a, b))
DEFINE_BINOP_FLOAT(trunc, sc_trunc(a, b))

#define DEFN(FUNNAME, OPNAME, HELP) 	vm.def(OPNAME, 1, 1, FUNNAME##_, "(x --> z) " HELP);
#define DEFNa(FUNNAME, OPNAME, HELP) 	DEFN(FUNNAME, #OPNAME, HELP)
#define DEF(NAME, HELP) 	DEFNa(NAME, NAME, HELP);

#define DEFNa2(FUNNAME, OPNAME, HELP) 	\
	(vm.def(#OPNAME, 2, 1, FUNNAME##_, "(x y --> z) " HELP), \
	vm.def(#OPNAME "/", 1, 1, FUNNAME##_reduce_, nullptr), \
	vm.def(#OPNAME "\\", 1, 1, FUNNAME##_scan_, nullptr), \
	vm.def(#OPNAME "^", 1, 1, FUNNAME##_pairs_, nullptr), \
	vm.def(#OPNAME "\\i", 2, 1, FUNNAME##_iscan_, nullptr), \
	vm.def(#OPNAME "^i", 1, 1, FUNNAME##_ipairs_, nullptr));

#define DEF2(NAME, HELP) 	DEFNa2(NAME, NAME, HELP);
#define DEFAM(NAME, MASK, HELP) 	vm.defautomap(#NAME, #MASK, NAME##_, HELP);

void AddMathOps();
void AddMathOps()
{
	fillSineTable();
	fillDBAmpTable();
	fillDecayTable();
    fillFirstOrderCoeffTable();

	vm.addBifHelp("\n*** unary math ops ***");
	DEF(isalnum, "return whether an ASCII value is alphanumeric.")
	DEF(isalpha, "return whether an ASCII value is alphabetic.")
	DEF(isblank, "return whether an ASCII value is a space or tab character.")
	DEF(iscntrl, "return whether an ASCII value is a control character.")
	DEF(isdigit, "return whether an ASCII value is a digit.")
	DEF(isgraph, "return whether an ASCII value is a graphic character.");
	DEF(islower, "return whether an ASCII value is lower case.")
	DEF(isprint, "return whether an ASCII value is a printable character.")
	DEF(ispunct, "return whether an ASCII value is a punctuation character.")
	DEF(isspace, "return whether an ASCII value is a graphic character.")
	DEF(isupper, "return whether an ASCII value is upper case.")
	DEF(isxdigit, "return whether an ASCII value is a hexadecimal digit.")
	DEF(isascii, "return whether a value is ASCII")

	DEF(tolower, "convert an ASCII character value to lower case.")
	DEF(toupper, "convert an ASCII character value to upper case.")
	DEF(toascii, "convert a value to ASCII by stripping the upper bits.")

	DEFN(nonpos, "0<=", "less than or equal to zero.")
	DEFN(nonneg, "0>=", "greater than or equal to zero.")
	DEFN(isneg, "0<", "less than zero.")
	DEFN(ispos, "0>", "greater than zero.")
	DEFN(iszero, "0=", "equal to zero.")
	DEFN(iseven, "even?", "is even.")
	DEFN(isodd, "odd?", "is odd.")
	DEFN(isprime, "prime?", "is prime.")
	DEFN(isint, "int?", "is integer.")

	DEF(isfinite, "is x a finite number.")
	DEF(isinf, "is x an infinity.")
	DEF(isnan, "is x not a number.")
	DEF(isnormal, "is x a normalized number (as opposed to denormals).")
	DEF(signbit, "sign bit of x.")

	DEF(abs, "absolute value.")
	DEF(sgn, "signum function. returns -1 when x < 0, 0 when x == 0, 1 when x > 0.")
	DEFN(not, "~", "logical negation. returns 1 when x == 0, else returns 0.")
	DEF(neg, "negative. -x")
	DEF(sqrt, "square root.")
	DEF(cbrt, "cube root.")
	DEF(rsqrt, "reciprocal square root.")
	DEF(sq, "square. x x *")
	DEF(ssq, "signed square. x x abs *")
	DEF(cb, "x cubed. x 3 ^")
	DEFN(sq, "^2", "x squared. x x *")
	DEFN(cb, "^3", "x cubed. x 3 ^")
	DEFN(pow4, "^4", "x to the fourth power. x 4 ^")
	DEFN(pow5, "^5", "x to the fifth power. x 5 ^")
	DEFN(pow6, "^6", "x to the sixth power. x 6 ^")
	DEFN(pow7, "^7", "x to the seventh power. x 7 ^")
	DEFN(pow8, "^8", "x to the eighth power. x 8 ^")
	DEFN(pow9, "^9", "x to the ninth power. x 9 ^")

	DEF(recip, "reciprocal.")
	DEFN(recip, "1/", "reciprocal. 1 x /")
	DEF(exp, "e to the x.")
	DEF(exp2, "2 to the x.")
	DEF(exp10, "10 to the x.")
	DEFN(exp, "e^", "e to the x.")
	DEFN(exp2, "2^", "2 to the x.")
	DEFN(exp10, "10^", "10 to the x.")
	DEF(expm1, "computes exp(x-1) accurately even for very small values of x.")
	DEF(log, "base e log of x.")
	DEF(log2, "base 2 log of x.")
	DEF(log10, "base 10 log of x.")
	DEF(log1p, "computes the value of log(1+x) accurately even for very small values of x.")
	DEF(logb, "x log2 floor")

	DEF(frac, "fractional part.")
	DEF(floor, "nearest integer <= x.")
	DEF(ceil, "nearest integer >= x.")
	DEF(rint, "nearest integer.")
	DEF(erf, "the error function.")
	DEF(erfc, "the complement of the error function.")

	DEF(sinc, "sinc. x sin x /")
	DEF(sin, "sine.")
	DEF(cos, "cosine.")
	DEF(sin1, "sine(x * 2pi).")
	DEF(cos1, "cosine(x * 2pi).")
	DEF(tan, "tangent.")
	DEF(asin, "arcsine.")
	DEF(acos, "arccosine.")
	DEF(atan, "arctangent.")
	DEF(sinh, "hyperbolic sine.")
	DEF(cosh, "hyperbolic cosine.")
	DEF(tanh, "hyperbolic tangent.")
	DEF(asinh, "hyperbolic arcsine.")
	DEF(acosh, "hyperbolic arccosine.")
	DEF(atanh, "hyperbolic arctangent.")

	DEF(J0, "zeroth Bessel function of the first kind evaluated at x.")
	DEF(J1, "first Bessel function of the first kind evaluated at x.")
	DEF(Y0, "zeroth Bessel function of the second kind evaluated at x.")
	DEF(Y1, "first Bessel function of the second kind evaluated at x.")

	DEF(tgamma, "the gamma function.")
	DEF(lgamma, "natural logarithm of the absolute value of the gamma function.")

	DEF(inc, "increment. x 1 +")
	DEF(dec, "decrement. x 1 -")
	DEF(half, "x .5 *")
	DEF(twice, "x 2 *")
	DEFN(inc, "++", "increment. x 1 +")
	DEFN(dec, "--", "decrement. x 1 -")
	DEFN(half, "/2", "half.")
	DEFN(twice, "*2", "twice.")
	DEF(biuni, "convert bipolar to unipolar. .5 * .5 +")
	DEF(unibi, "convert unipolar to bipolar. 2 * 1 -")
	DEF(biunic, "convert bipolar to unipolar with clipping to range. -1 1 clip .5 * .5 +")
	DEF(unibic, "convert unipolar to bipolar with clipping to range. 0 1 clip 2 * 1 -")
	DEF(cmpl, "unipolar complement. 1 x -")

	DEF(ampdb, "convert linear amplitude to decibels.")
	DEF(dbamp, "convert decibels to linear amplitude.")

	DEF(ohz, "convert octaves to Hertz. Octave 0.0 is middle C.")
	DEF(hzo, "convert Hertz to octaves. Octave 0.0 is middle C.")
	DEF(nnhz, "convert MIDI note numbers to Hertz. 60 is middle C.")
	DEF(hznn, "convert Hertz to MIDI note numbers. 60 is middle C.")

	DEF(centsratio, "convert an interval in cents to a ratio.")
	DEF(ratiocents, "convert a ratio to an interval in cents.")

	DEF(semiratio, "convert an interval in semitones to a ratio.")
	DEF(ratiosemi, "a ratio to an interval in semitones.")

	DEF(minsec, "convert from minutes to seconds. also for converting from bps to bpm")
	DEF(secmin, "convert from seconds to minutes. also for converting from bpm to bps.")
	DEF(bpmsec, "convert from beats per minute to a period in seconds(e.g. for delay times)")
	DEF(degrad, "convert from degrees to radians.")
	DEF(raddeg, "convert from radians to degrees.")

	DEF(distort, "sigmoid wave distortion function. x/sqrt(1 + x^2)")
	DEF(softclip, "sigmoid wave distortion function. returns x when abs(x) < .5, else returns (abs(x) - .25) / x")
	DEF(sigm, "sigmoid wave distortion function. x/sqrt(1+x*x).")

	DEF(rectWin, "rectangular window for x in the interval [0,1].")
	DEF(triWin, "triangular window for x in the interval [0,1].")
	DEF(bitriWin, "triangular window for x in the interval [-1,1]")
	DEF(hanWin, "hanning window for x in the interval [0,1]")
	DEF(sinWin, "sine window for x in the interval [0,1]")
	DEF(ramp, "return 0 when x <= 0, return x when 0 < x < 1, return 1 when x > 1.")
	DEF(scurve, "return 0 when x <= 0, return 3*x*x - 2*x*x*x when 0 < x < 1, return 1 when x > 1.")

	DEF(zapgremlins, "")

	/////////////////////////////////////////////////////

	vm.addBifHelp("\n*** binary math ops ***");
	vm.addBifHelp("\n   All built-in binary math operators have the following variations defined:");
	vm.addBifHelp("      op/   (list --> z) reducing math operator.");
	vm.addBifHelp("      op\\   (list --> z) scanning math operator.");
	vm.addBifHelp("      op^   (list --> z) pairwise math operator.");
	vm.addBifHelp("      op/i  (list init --> z) reducing math operator with initial value.");
	vm.addBifHelp("      op\\i  (list init --> z) scanning math operator with initial value.");
	vm.addBifHelp("      op^i  (list init --> z) pairwise math operator with initial value.");
	vm.addBifHelp("   For example, + has the following variations: +/  +\\  +^  +/i  +\\i  +^i");
	vm.addBifHelp("");

	vm.plusFun = DEFNa2(plus, +, "addition.")
	DEFNa2(plus_link, +>, "addition. For lists, acts as if shorter list were extended with zeroes.")
	DEFNa2(minus, -, "subtraction.")
	vm.mulFun = DEFNa2(mul, *, "multiplication.")
	DEFNa2(div, /, "real division.")
	DEFNa2(mod, %, "modulo.")
	DEF2(idiv, "integer division.")
	DEF2(imod, "integer modulo.")
	DEF2(remainder, "remainder.")

	DEFNa2(lt, <, "less than.")
	DEFNa2(le, <=, "less than or equal.")
	DEFNa2(gt, >, "greater than.")
	DEFNa2(ge, >=, "greater than or equal.")
	DEFNa2(eq, ==, "equal.")
	DEFNa2(ne, !=, "not equal.")

	DEF2(cmp, "returns -1 when x < y, returns 1 when x > y, returns 0 when x == y.")

	DEF2(copysign, "copy the sign of y to the value of x.")
	DEF2(nextafter, "return the next machine representable number from x in direction y.")

	DEF2(pow, "x to the power y.")
	DEFNa2(pow, ^, "x to the power y.")
	DEF2(atan2, "arctangent of y/x.")

	DEF2(Jn, "yth Bessel function of the first kind evaluated at x.")
	DEF2(Yn, "yth Bessel function of the second kind evaluated at x.")

	vm.minFun = DEFNa2(min, &, "return the minimum of x and y. functions as logical AND.")
	vm.maxFun = DEFNa2(max, |, "return the maximum of x and y. functions as logical OR.")

	DEF2(avg2, "x y + .5 *")
	DEF2(dim, "positive difference of x and y. x y - 0 |")
	DEF2(absdif, "x y - abs")
	DEF2(hypot, "x sq y sq + sqrt")
	DEF2(sumsq, "x sq y sq +")
	DEF2(difsq, "x sq y sq -")
	DEF2(sqsum, "x y + sq")
	DEF2(sqdif, "x y - sq")

	DEF2(thresh, "returns 0 when x < y, else returns x.")
	DEF2(absthresh, "returns 0 when |x| < y, else returns x.")
	DEF2(amclip, "returns 0 when y <= 0, else returns x*y.")
	DEF2(scaleneg, "returns x*y when x < 0, else returns x.")

	DEF2(ring1, "x y * x +")
	DEF2(ring2, "x y * x + y +")
	DEF2(ring3, "x sq y *")
	DEF2(ring4, "x y * x y - *")

	DEF2(gcd, "greatest common divisor.")
	DEF2(lcm, "least common multiple.")

	DEF2(clip0, "clip x between 0 and y.")
	DEF2(wrap0, "wrap x between 0 and y.")
	DEF2(fold0, "fold x between 0 and y.")

	DEF2(clip2, "clip x between -y and y.")
	DEF2(wrap2, "wrap x between -y and y.")
	DEF2(fold2, "fold x between -y and y.")
	DEF2(iwrap2, "wrap integer x between -y and y.")
	DEF2(ifold2, "fold integer x between -y and y.")
	DEF2(excess, "return the excess after clipping. x x y clip2 -")

	DEF2(round, "round x to nearest multiple of y.")
	DEF2(roundUp, "round x to nearest multiple of y >= x.")
	DEF2(trunc, "round x to nearest multiple of y <= x")

}

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "VM.hpp"
#include "clz.hpp"
#include "elapsedTime.hpp"
#include <cmath>
#include <float.h>
#include <vector>
#include "MultichannelExpansion.hpp"
#include "UGen.hpp"

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma mark RANDOM STREAMS

template <typename T>
void swapifgt(T& a, T& b) {
	if (a > b) {
		T t = a;
		a = b;
		b = t;
	}
}

struct URand : ZeroInputGen<URand>
{
    RGen r;

	URand(Thread& th) : ZeroInputGen<URand>(th, false)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "URand"; }

	void calc(int n, V* out)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = r.drand();
		}
	}
};

struct URandz : ZeroInputUGen<URandz>
{
    RGen r;

	URandz(Thread& th) : ZeroInputUGen<URandz>(th, false)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "URandz"; }

	void calc(int n, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = r.drand();
		}
	}
};

struct NURand : NZeroInputGen<NURand>
{
    RGen r;

	NURand(Thread& th, int64_t n) : NZeroInputGen<NURand>(th, n)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NURand"; }

	void calc(int n, V* out)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = r.drand();
		}
	}
};

struct NURandz : NZeroInputUGen<NURandz>
{
    RGen r;

	NURandz(Thread& th, int64_t n) : NZeroInputUGen<NURandz>(th, n)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NURandz"; }

	void calc(int n, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = r.drand();
		}
	}
};

struct BRand : ZeroInputGen<BRand>
{
    RGen r;

	BRand(Thread& th) : ZeroInputGen<BRand>(th, false)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "BRand"; }

	void calc(int n, V* out)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = r.drand2();
		}
	}
};

struct BRandz : ZeroInputUGen<BRandz>
{
    RGen r;

	BRandz(Thread& th) : ZeroInputUGen<BRandz>(th, false)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "BRandz"; }

	void calc(int n, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = r.drand2();
		}
	}
};

struct NBRand : NZeroInputGen<NBRand>
{
    RGen r;

	NBRand(Thread& th, int64_t n) : NZeroInputGen<NBRand>(th, n)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NBRand"; }

	void calc(int n, V* out)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = r.drand2();
		}
	}
};

struct NBRandz : NZeroInputUGen<NBRandz>
{
    RGen r;

	NBRandz(Thread& th, int64_t n) : NZeroInputUGen<NBRandz>(th, n)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NBRandz"; }

	void calc(int n, Z* out)
	{
		for (int i = 0; i < n; ++i) {
			out[i] = r.drand2();
		}
	}
};

struct Rand : TwoInputGen<Rand>
{
    RGen r;

	Rand(Thread& th,  Arg a, Arg b) : TwoInputGen<Rand>(th, a, b)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "Rand"; }

	void calc(int n, V* out, V* aa, V* bb, int aStride, int bStride)
	{
		if (aStride == 0 && bStride == 0) {
			Z a = aa->asFloat();
			Z b = bb->asFloat();
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = a + (b - a) * r.drand();
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = aa->asFloat(); aa += aStride;
				Z b = bb->asFloat(); bb += bStride;
				swapifgt(a, b);
				out[i] = a + (b - a) * r.drand();
			}
		}
	}
};

struct Randz : TwoInputUGen<Randz>
{
    RGen r;

	Randz(Thread& th,  Arg a, Arg b) : TwoInputUGen<Randz>(th, a, b)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "Randz"; }

	void calc(int n, Z* out, Z* aa, Z* bb, int aStride, int bStride)
	{
		if (aStride == 0 && bStride == 0) {
			Z a = *aa;
			Z b = *bb;
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = a + (b - a) * r.drand();
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = *aa; aa += aStride;
				Z b = *bb; bb += bStride;
				swapifgt(a, b);
				out[i] = a + (b - a) * r.drand();
			}
		}
	}
};

struct NRand : NTwoInputGen<NRand>
{
    RGen r;

	NRand(Thread& th, int64_t n, Arg a, Arg b) : NTwoInputGen<NRand>(th, n, a, b)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NRand"; }

	void calc(int n, V* out, V* aa, V* bb, int aStride, int bStride)
	{
		if (aStride == 0 && bStride == 0) {
			Z a = aa->asFloat();
			Z b = bb->asFloat();
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = a + (b - a) * r.drand();
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = aa->asFloat(); aa += aStride;
				Z b = bb->asFloat(); bb += bStride;
				swapifgt(a, b);
				out[i] = a + (b - a) * r.drand();
			}
		}
	}
};

struct NRandz : NTwoInputUGen<NRandz>
{
    RGen r;

	NRandz(Thread& th, int64_t n, Arg a, Arg b) : NTwoInputUGen<NRandz>(th, n, a, b)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NRandz"; }

	void calc(int n, Z* out, Z* aa, Z* bb, int aStride, int bStride)
	{
		if (aStride == 0 && bStride == 0) {
			Z a = *aa;
			Z b = *bb;
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = a + (b - a) * r.drand();
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = *aa; aa += aStride;
				Z b = *bb; bb += bStride;
				swapifgt(a, b);
				out[i] = a + (b - a) * r.drand();
			}
		}
	}
};

static void urands_(Thread& th, Prim* prim)
{
	Gen* g = new URand(th);
	th.push(new List(g));
}

static void urandz_(Thread& th, Prim* prim)
{
	Gen* g = new URandz(th);
	th.push(new List(g));
}

static void brands_(Thread& th, Prim* prim)
{
	Gen* g = new BRand(th);
	th.push(new List(g));
}

static void brandz_(Thread& th, Prim* prim)
{
	Gen* g = new BRandz(th);
	th.push(new List(g));
}

static void nurands_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("nurands : n");
	Gen* g = new NURand(th, n);
	th.push(new List(g));
}

static void nurandz_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("nurandz : n");
	Gen* g = new NURandz(th, n);
	th.push(new List(g));
}

static void nbrands_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("nbrands : n");
	Gen* g = new NBRand(th, n);
	th.push(new List(g));
}

static void nbrandz_(Thread& th, Prim* prim)
{
	int64_t n = th.popInt("nbrandz : n");
	Gen* g = new NBRandz(th, n);
	th.push(new List(g));
}

static void rands_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();

	Gen* g = new Rand(th, a, b);
	th.push(new List(g));
}

static void randz_(Thread& th, Prim* prim)
{
	V b = th.popZIn("randz : hi");
	V a = th.popZIn("randz : lo");

	Gen* g = new Randz(th, a, b);
	th.push(new List(g));
}

static void nrands_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();
	int64_t n = th.popInt("nrands : n");

	Gen* g = new NRand(th, n, a, b);
	th.push(new List(g));
}

static void nrandz_(Thread& th, Prim* prim)
{
	V b = th.popZIn("nrandz : hi");
	V a = th.popZIn("nrandz : lo");
	int64_t n = th.popInt("nrandz : n");

	Gen* g = new NRandz(th, n, a, b);
	th.push(new List(g));
}

static void urand_(Thread& th, Prim* prim)
{
	Z z = th.rgen.drand();
	th.push(z);
}

static void brand_(Thread& th, Prim* prim)
{
	Z z = th.rgen.drand2();
	th.push(z);
}

static void newseed_(Thread& th, Prim* prim)
{
	V v;
	v.i = timeseed();
	th.push(v);
}

static void setseed_(Thread& th, Prim* prim)
{
	V v = th.pop();
	if (!v.isReal()) wrongType("setseed : seed", "Float", v);
	th.rgen.init(v.i);
}

static void rand_(Thread& th, Prim* prim)
{
	Z b = th.popFloat("rand : hi");
	Z a = th.popFloat("rand : lo");

	swapifgt(a, b);
	Z z = th.rgen.rand(a, b);
	th.push(z);
}

struct Coin : OneInputGen<Coin>
{
    RGen r;

	Coin(Thread& th,  Arg a) : OneInputGen<Coin>(th, a)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "Coin"; }

	void calc(int n, V* out, V* aa, int aStride)
	{
		if (aStride == 0) {
			Z a = aa->asFloat();
			for (int i = 0; i < n; ++i) {
				out[i] = r.coin(a);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = aa->asFloat(); aa += aStride;
				out[i] = r.coin(a);
			}
		}
	}
};

struct Coinz : OneInputUGen<Coinz>
{
    RGen r;

	Coinz(Thread& th,  Arg a) : OneInputUGen<Coinz>(th, a)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "Coinz"; }

	void calc(int n, Z* out, Z* aa, int aStride)
	{
		if (aStride == 0) {
			Z a = *aa;
			for (int i = 0; i < n; ++i) {
				out[i] = r.coin(a);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = *aa; aa += aStride;
				out[i] = r.coin(a);
			}
		}
	}
};

struct NCoin : NOneInputGen<NCoin>
{
    RGen r;

	NCoin(Thread& th, int64_t n, Arg a) : NOneInputGen<NCoin>(th, n, a)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NCoin"; }

	void calc(int n, V* out, V* aa, int aStride)
	{
		if (aStride == 0) {
			Z a = aa->asFloat();
			for (int i = 0; i < n; ++i) {
				out[i] = r.coin(a);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = aa->asFloat(); aa += aStride;
				out[i] = r.coin(a);
			}
		}
	}
};

struct NCoinz : NOneInputUGen<NCoinz>
{
    RGen r;

	NCoinz(Thread& th, int64_t n, Arg a) : NOneInputUGen<NCoinz>(th, n, a)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NCoinz"; }

	void calc(int n, Z* out, Z* aa, int aStride)
	{
		if (aStride == 0) {
			Z a = *aa;
			for (int i = 0; i < n; ++i) {
				out[i] = r.coin(a);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = *aa; aa += aStride;
				out[i] = r.coin(a);
			}
		}
	}
};

static void coin_(Thread& th, Prim* prim)
{
	Z p = th.popFloat("coin : p");

	Z z = th.rgen.coin(p);
	th.push(z);
}

static void coins_(Thread& th, Prim* prim)
{
	V a = th.pop();

	Gen* g = new Coin(th, a);
	th.push(new List(g));
}

static void coinz_(Thread& th, Prim* prim)
{
	V a = th.pop();

	Gen* g = new Coinz(th, a);
	th.push(new List(g));
}

static void ncoins_(Thread& th, Prim* prim)
{
	V a = th.pop();
	int64_t n = th.popInt("ncoins : n");

	Gen* g = new NCoin(th, n, a);
	th.push(new List(g));
}

static void ncoinz_(Thread& th, Prim* prim)
{
	V a = th.pop();
	int64_t n = th.popInt("ncoinz : n");

	Gen* g = new NCoinz(th, n, a);
	th.push(new List(g));
}

struct IRand : TwoInputGen<IRand>
{
    RGen r;

	IRand(Thread& th,  Arg a, Arg b) : TwoInputGen<IRand>(th, a, b)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "IRand"; }

	void calc(int n, V* out, V* aa, V* bb, int astride, int bstride)
	{
		if (astride == 0 && bstride == 0) {
			int32_t a = (int32_t)aa->asInt();
			int32_t b = (int32_t)bb->asInt();
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = (Z)r.irand(a, b);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				int32_t a = (int32_t)aa->asInt(); aa += astride;
				int32_t b = (int32_t)bb->asInt(); bb += bstride;
				swapifgt(a, b);
				out[i] = (Z)r.irand(a, b);
			}
		}
	}
};

struct IRandz : TwoInputUGen<IRandz>
{
    RGen r;

	IRandz(Thread& th,  Arg a, Arg b) : TwoInputUGen<IRandz>(th, a, b)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "IRandz"; }

	void calc(int n, Z* out, Z* aa, Z* bb, int astride, int bstride)
	{
		if (astride == 0 && bstride == 0) {
			int32_t a = (int32_t)*aa;
			int32_t b = (int32_t)*bb;
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = (Z)r.irand(a, b);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				int32_t a = (int32_t)*aa; aa += astride;
				int32_t b = (int32_t)*bb; bb += bstride;
				swapifgt(a, b);
				out[i] = (Z)r.irand(a, b);
			}
		}
	}
};

struct NIRand : NTwoInputGen<NIRand>
{
    RGen r;

	NIRand(Thread& th, int64_t n, Arg a, Arg b) : NTwoInputGen<NIRand>(th, n, a, b)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NIRand"; }

	void calc(int n, V* out, V* aa, V* bb, int astride, int bstride)
	{
		if (astride == 0 && bstride == 0) {
			int32_t a = (int32_t)aa->asInt();
			int32_t b = (int32_t)bb->asInt();
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = (Z)r.irand(a, b);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				int32_t a = (int32_t)aa->asInt(); aa += astride;
				int32_t b = (int32_t)bb->asInt(); bb += bstride;
				swapifgt(a, b);
				out[i] = (Z)r.irand(a, b);
			}
		}
	}
};

struct NIRandz : NTwoInputUGen<NIRandz>
{
    RGen r;

	NIRandz(Thread& th, int64_t n, Arg a, Arg b) : NTwoInputUGen<NIRandz>(th, n, a, b)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NIRandz"; }

	void calc(int n, Z* out, Z* aa, Z* bb, int astride, int bstride)
	{
		if (astride == 0 && bstride == 0) {
			int32_t a = (int32_t)*aa;
			int32_t b = (int32_t)*bb;
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = (Z)r.irand(a, b);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				int32_t a = (int32_t)*aa; aa += astride;
				int32_t b = (int32_t)*bb; bb += bstride;
				swapifgt(a, b);
				out[i] = (Z)r.irand(a, b);
			}
		}
	}
};

static void irands_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();

	Gen* g = new IRand(th, a, b);
	th.push(new List(g));
}

static void irandz_(Thread& th, Prim* prim)
{
	V b = th.popZIn("irandz : hi");
	V a = th.popZIn("irandz : lo");

	Gen* g = new IRandz(th, a, b);
	th.push(new List(g));
}

static void nirands_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();
	int64_t n = th.popInt("nirands : n");

	Gen* g = new NIRand(th, n, a, b);
	th.push(new List(g));
}

static void nirandz_(Thread& th, Prim* prim)
{
	V b = th.popZIn("nirandz : hi");
	V a = th.popZIn("nirandz : lo");
	int64_t n = th.popInt("nirandz : n");

	Gen* g = new NIRandz(th, n, a, b);
	th.push(new List(g));
}

static void irand_(Thread& th, Prim* prim)
{
	int64_t b = th.popInt("irand : hi");
	int64_t a = th.popInt("irand : lo");
	RGen& r = th.rgen;

	swapifgt(a, b);
	Z z = (Z)r.irand(a, b);
	th.push(z);
}

struct ExcRand : TwoInputGen<ExcRand>
{
    RGen r;
	int64_t prev;

	ExcRand(Thread& th,  Arg a, Arg b) : TwoInputGen<ExcRand>(th, a, b), prev(INT32_MIN)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "ExcRand"; }

	void calc(int n, V* out, V* aa, V* bb, int astride, int bstride)
	{
		if (astride == 0 && bstride == 0) {
			int64_t a = (int64_t)aa->asInt();
			int64_t b = (int64_t)bb->asInt();
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				int64_t x = r.irand(a, b);
				if (x == prev) x = b;
				prev = x;
				out[i] = x;
			}
		} else {
			for (int i = 0; i < n; ++i) {
				int64_t a = (int64_t)aa->asInt(); aa += astride;
				int64_t b = (int64_t)bb->asInt(); bb += bstride;
				swapifgt(a, b);

				int64_t x = r.irand(a, b);
				if (x == prev) x = b;
				prev = x;
				out[i] = (Z)x;
			}
		}
	}
};

struct ExcRandz : TwoInputUGen<ExcRandz>
{
    RGen r;
	int64_t prev;

	ExcRandz(Thread& th,  Arg a, Arg b) : TwoInputUGen<ExcRandz>(th, a, b), prev(INT32_MIN)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "ExcRandz"; }

	void calc(int n, Z* out, Z* aa, Z* bb, int astride, int bstride)
	{
		if (astride == 0 && bstride == 0) {
			int64_t a = (int64_t)*aa;
			int64_t b = (int64_t)*bb;
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				int64_t x = r.irand(a, b);
				if (x == prev) x = b;
				prev = x;
				out[i] = (Z)x;
			}
		} else {
			for (int i = 0; i < n; ++i) {
				int64_t a = (int64_t)*aa; aa += astride;
				int64_t b = (int64_t)*bb; bb += bstride;
				swapifgt(a, b);

				int64_t x = r.irand(a, b);
				if (x == prev) x = b;
				prev = x;
				out[i] = (Z)x;
			}
		}
	}
};

struct NExcRand : NTwoInputGen<NExcRand>
{
    RGen r;
 	int64_t prev;

	NExcRand(Thread& th, int64_t n, Arg a, Arg b) : NTwoInputGen<NExcRand>(th, n, a, b), prev(INT32_MIN)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NExcRand"; }

	void calc(int n, V* out, V* aa, V* bb, int astride, int bstride)
	{
		if (astride == 0 && bstride == 0) {
			int64_t a = (int64_t)aa->asInt();
			int64_t b = (int64_t)bb->asInt();
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				int64_t x = r.irand(a, b);
				if (x == prev) x = b;
				prev = x;
				out[i] = x;
			}
		} else {
			for (int i = 0; i < n; ++i) {
				int64_t a = (int64_t)aa->asInt(); aa += astride;
				int64_t b = (int64_t)bb->asInt(); bb += bstride;
				swapifgt(a, b);

				int64_t x = r.irand(a, b);
				if (x == prev) x = b;
				prev = x;
				out[i] = (Z)x;
			}
		}
	}
};

struct NExcRandz : NTwoInputUGen<NExcRandz>
{
    RGen r;
	int64_t prev;

	NExcRandz(Thread& th, int64_t n, Arg a, Arg b) : NTwoInputUGen<NExcRandz>(th, n, a, b), prev(INT32_MIN)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NIRandz"; }

	void calc(int n, Z* out, Z* aa, Z* bb, int astride, int bstride)
	{
		if (astride == 0 && bstride == 0) {
			int64_t a = (int64_t)*aa;
			int64_t b = (int64_t)*bb;
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = (Z)r.irand(a, b);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				int64_t a = (int64_t)*aa; aa += astride;
				int64_t b = (int64_t)*bb; bb += bstride;
				swapifgt(a, b);
				out[i] = (Z)r.irand(a, b);
			}
		}
	}
};

static void eprands_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();

	Gen* g = new ExcRand(th, a, b);
	th.push(new List(g));
}

static void eprandz_(Thread& th, Prim* prim)
{
	V b = th.popZIn("eprandz : hi");
	V a = th.popZIn("eprandz : lo");

	Gen* g = new ExcRandz(th, a, b);
	th.push(new List(g));
}

static void neprands_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();
	int64_t n = th.popInt("neprands : n");

	Gen* g = new NExcRand(th, n, a, b);
	th.push(new List(g));
}

static void neprandz_(Thread& th, Prim* prim)
{
	V b = th.popZIn("eprands : hi");
	V a = th.popZIn("eprands : lo");
	int64_t n = th.popInt("neprandz : n");

	Gen* g = new NExcRandz(th, n, a, b);
	th.push(new List(g));
}

struct ExpRand : TwoInputGen<ExpRand>
{
    RGen r;

	ExpRand(Thread& th,  Arg a, Arg b) : TwoInputGen<ExpRand>(th, a, b)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "ExpRand"; }

	void calc(int n, V* out, V* aa, V* bb, int aStride, int bStride)
	{
		if (aStride == 0 && bStride == 0) {
			Z a = aa->asFloat();
			Z b = bb->asFloat();
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = r.xrand(a, b);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = aa->asFloat(); aa += aStride;
				Z b = bb->asFloat(); bb += bStride;
				swapifgt(a, b);
				out[i] = r.xrand(a, b);
			}
		}
	}
};

struct ExpRandz : TwoInputUGen<ExpRandz>
{
    RGen r;

	ExpRandz(Thread& th,  Arg a, Arg b) : TwoInputUGen<ExpRandz>(th, a, b)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "ExpRandz"; }

	void calc(int n, Z* out, Z* aa, Z* bb, int aStride, int bStride)
	{
		if (aStride == 0 && bStride == 0) {
			Z a = *aa;
			Z b = *bb;
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = r.xrand(a, b);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = *aa; aa += aStride;
				Z b = *bb; bb += bStride;
				swapifgt(a, b);
				out[i] = r.xrand(a, b);
			}
		}
	}
};

struct NExpRand : NTwoInputGen<NExpRand>
{
    RGen r;

	NExpRand(Thread& th, int64_t n, Arg a, Arg b) : NTwoInputGen<NExpRand>(th, n, a, b)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NExpRand"; }

	void calc(int n, V* out, V* aa, V* bb, int aStride, int bStride)
	{
		if (aStride == 0 && bStride == 0) {
			Z a = aa->asFloat();
			Z b = bb->asFloat();
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = r.xrand(a, b);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = aa->asFloat(); aa += aStride;
				Z b = bb->asFloat(); bb += bStride;
				swapifgt(a, b);
				out[i] = r.xrand(a, b);
			}
		}
	}
};

struct NExpRandz : NTwoInputUGen<NExpRandz>
{
    RGen r;

	NExpRandz(Thread& th, int64_t n, Arg a, Arg b) : NTwoInputUGen<NExpRandz>(th, n, a, b)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NExpRandz"; }

	void calc(int n, Z* out, Z* aa, Z* bb, int aStride, int bStride)
	{
		if (aStride == 0 && bStride == 0) {
			Z a = *aa;
			Z b = *bb;
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = r.xrand(a, b);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = *aa; aa += aStride;
				Z b = *bb; bb += bStride;
				swapifgt(a, b);
				out[i] = r.xrand(a, b);
			}
		}
	}
};

static void xrands_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();

	Gen* g = new ExpRand(th, a, b);
	th.push(new List(g));
}

static void xrandz_(Thread& th, Prim* prim)
{
	V b = th.popZIn("xrandz : hi");
	V a = th.popZIn("xrandz : lo");

	Gen* g = new ExpRandz(th, a, b);
	th.push(new List(g));
}

static void nxrands_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();
	int64_t n = th.popInt("nxrands : n");

	Gen* g = new NExpRand(th, n, a, b);
	th.push(new List(g));
}

static void nxrandz_(Thread& th, Prim* prim)
{
	V b = th.popZIn("nxrandz : hi");
	V a = th.popZIn("nxrandz : lo");
	int64_t n = th.popInt("xrandz : n");

	Gen* g = new NExpRandz(th, n, a, b);
	th.push(new List(g));
}

static void xrand_(Thread& th, Prim* prim)
{
	Z b = th.popFloat("xrand : hi");
	Z a = th.popFloat("xrand : lo");
	RGen& r = th.rgen;

	if (b < a) { Z x; x = a; a = b; b = x; }
	Z z = r.xrand(a, b);
	th.push(z);
}

struct ILinRand : TwoInputGen<ILinRand>
{
    RGen r;

	ILinRand(Thread& th,  Arg a, Arg b) : TwoInputGen<ILinRand>(th, a, b)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "ILinRand"; }

	void calc(int n, V* out, V* aa, V* bb, int astride, int bstride)
	{
		if (astride == 0 && bstride == 0) {
			int64_t a = (int64_t)aa->asInt();
			int64_t b = (int64_t)bb->asInt();
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = (Z)r.irand(a, b);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				int64_t a = (int64_t)aa->asInt(); aa += astride;
				int64_t b = (int64_t)bb->asInt(); bb += bstride;
				swapifgt(a, b);
				out[i] = (Z)r.ilinrand(a, b);
			}
		}
	}
};

struct ILinRandz : TwoInputUGen<ILinRandz>
{
    RGen r;

	ILinRandz(Thread& th,  Arg a, Arg b) : TwoInputUGen<ILinRandz>(th, a, b)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "ILinRandz"; }

	void calc(int n, Z* out, Z* aa, Z* bb, int astride, int bstride)
	{
		if (astride == 0 && bstride == 0) {
			int64_t a = (int64_t)*aa;
			int64_t b = (int64_t)*bb;
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = (Z)r.ilinrand(a, b);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				int64_t a = (int64_t)*aa; aa += astride;
				int64_t b = (int64_t)*bb; bb += bstride;
				swapifgt(a, b);
				out[i] = (Z)r.ilinrand(a, b);
			}
		}
	}
};

struct NILinRand : NTwoInputGen<NILinRand>
{
    RGen r;

	NILinRand(Thread& th, int64_t n, Arg a, Arg b) : NTwoInputGen<NILinRand>(th, n, a, b)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NILinRand"; }

	void calc(int n, V* out, V* aa, V* bb, int astride, int bstride)
	{
		if (astride == 0 && bstride == 0) {
			int64_t a = (int64_t)aa->asInt();
			int64_t b = (int64_t)bb->asInt();
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = (Z)r.ilinrand(a, b);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				int64_t a = (int64_t)aa->asInt(); aa += astride;
				int64_t b = (int64_t)bb->asInt(); bb += bstride;
				swapifgt(a, b);
				out[i] = (Z)r.ilinrand(a, b);
			}
		}
	}
};

struct NILinRandz : NTwoInputUGen<NILinRandz>
{
    RGen r;

	NILinRandz(Thread& th, int64_t n, Arg a, Arg b) : NTwoInputUGen<NILinRandz>(th, n, a, b)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NILinRandz"; }

	void calc(int n, Z* out, Z* aa, Z* bb, int astride, int bstride)
	{
		if (astride == 0 && bstride == 0) {
			int64_t a = (int64_t)*aa;
			int64_t b = (int64_t)*bb;
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = (Z)r.ilinrand(a, b);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				int64_t a = (int64_t)*aa; aa += astride;
				int64_t b = (int64_t)*bb; bb += bstride;
				swapifgt(a, b);
				out[i] = (Z)r.ilinrand(a, b);
			}
		}
	}
};

static void ilinrands_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();

	Gen* g = new ILinRand(th, a, b);
	th.push(new List(g));
}

static void ilinrandz_(Thread& th, Prim* prim)
{
	V b = th.popZIn("ilinrandz : hi");
	V a = th.popZIn("ilinrandz : lo");

	Gen* g = new ILinRandz(th, a, b);
	th.push(new List(g));
}

static void nilinrands_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();
	int64_t n = th.popInt("nilinrands : n");

	Gen* g = new NILinRand(th, n, a, b);
	th.push(new List(g));
}

static void nilinrandz_(Thread& th, Prim* prim)
{
	V b = th.popZIn("nilinrandz : hi");
	V a = th.popZIn("nilinrandz : lo");
	int64_t n = th.popInt("nilinrandz : n");

	Gen* g = new NILinRandz(th, n, a, b);
	th.push(new List(g));
}

static void ilinrand_(Thread& th, Prim* prim)
{
	int64_t b = th.popInt("ilinrand : hi");
	int64_t a = th.popInt("ilinrand : lo");
	RGen& r = th.rgen;

	int64_t x;
	if (b < a) { x = a; a = b; b = x; }
	Z z = (Z)r.ilinrand(a, b);
	th.push(z);
}

struct LinRand : TwoInputGen<LinRand>
{
    RGen r;

	LinRand(Thread& th,  Arg a, Arg b) : TwoInputGen<LinRand>(th, a, b)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "LinRand"; }

	void calc(int n, V* out, V* aa, V* bb, int aStride, int bStride)
	{
		if (aStride == 0 && bStride == 0) {
			Z a = aa->asFloat();
			Z b = bb->asFloat();
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = r.linrand(a, b);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = aa->asFloat(); aa += aStride;
				Z b = bb->asFloat(); bb += bStride;
				swapifgt(a, b);
				out[i] = r.linrand(a, b);
			}
		}
	}
};

struct LinRandz : TwoInputUGen<LinRandz>
{
    RGen r;

	LinRandz(Thread& th,  Arg a, Arg b) : TwoInputUGen<LinRandz>(th, a, b)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "LinRandz"; }

	void calc(int n, Z* out, Z* aa, Z* bb, int aStride, int bStride)
	{
		if (aStride == 0 && bStride == 0) {
			Z a = *aa;
			Z b = *bb;
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = r.linrand(a, b);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = *aa; aa += aStride;
				Z b = *bb; bb += bStride;
				swapifgt(a, b);
				out[i] = r.linrand(a, b);
			}
		}
	}
};

struct NLinRand : NTwoInputGen<NLinRand>
{
    RGen r;

	NLinRand(Thread& th, int64_t n, Arg a, Arg b) : NTwoInputGen<NLinRand>(th, n, a, b)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NLinRand"; }

	void calc(int n, V* out, V* aa, V* bb, int aStride, int bStride)
	{
		if (aStride == 0 && bStride == 0) {
			Z a = aa->asFloat();
			Z b = bb->asFloat();
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = r.linrand(a, b);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = aa->asFloat(); aa += aStride;
				Z b = bb->asFloat(); bb += bStride;
				swapifgt(a, b);
				out[i] = r.linrand(a, b);
			}
		}
	}
};

struct NLinRandz : NTwoInputUGen<NLinRandz>
{
    RGen r;

	NLinRandz(Thread& th, int64_t n, Arg a, Arg b) : NTwoInputUGen<NLinRandz>(th, n, a, b)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NLinRandz"; }

	void calc(int n, Z* out, Z* aa, Z* bb, int aStride, int bStride)
	{
		if (aStride == 0 && bStride == 0) {
			Z a = *aa;
			Z b = *bb;
			swapifgt(a, b);
			for (int i = 0; i < n; ++i) {
				out[i] = r.linrand(a, b);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = *aa; aa += aStride;
				Z b = *bb; bb += bStride;
				swapifgt(a, b);
				out[i] = r.linrand(a, b);
			}
		}
	}
};

static void linrands_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();

	Gen* g = new LinRand(th, a, b);
	th.push(new List(g));
}

static void linrandz_(Thread& th, Prim* prim)
{
	V b = th.popZIn("linrandz : hi");
	V a = th.popZIn("linrandz : lo");

	Gen* g = new LinRandz(th, a, b);
	th.push(new List(g));
}

static void nlinrands_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();
	int64_t n = th.popInt("linrandz : n");

	Gen* g = new NLinRand(th, n, a, b);
	th.push(new List(g));
}

static void nlinrandz_(Thread& th, Prim* prim)
{
	V b = th.popZIn("linrandz : hi");
	V a = th.popZIn("linrandz : lo");
	int64_t n = th.popInt("randz : n");

	Gen* g = new NLinRandz(th, n, a, b);
	th.push(new List(g));
}

static void linrand_(Thread& th, Prim* prim)
{
	Z b = th.popFloat("linrand : hi");
	Z a = th.popFloat("linrand : lo");
	RGen& r = th.rgen;

	if (b < a) { Z x; x = a; a = b; b = x; }
	Z z = r.linrand(a, b);
	th.push(z);
}

struct Rand2 : OneInputGen<Rand2>
{
    RGen r;

	Rand2(Thread& th,  Arg a) : OneInputGen<Rand2>(th, a)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "Rand2"; }

	void calc(int n, V* out, V* aa, int aStride)
	{
		if (aStride == 0) {
			Z a = aa->asFloat();
			Z a2 = 2. * a;
			for (int i = 0; i < n; ++i) {
				out[i] = a2 * r.drand() - a;
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = aa->asFloat(); aa += aStride;
				out[i] = 2. * a * r.drand() - a;
			}
		}
	}
};

struct Rand2z : OneInputUGen<Rand2z>
{
    RGen r;

	Rand2z(Thread& th,  Arg a) : OneInputUGen<Rand2z>(th, a)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "Rand2z"; }

	void calc(int n, Z* out, Z* aa, int aStride)
	{
		if (aStride == 0) {
			Z a = *aa;
			Z a2 = 2. * a;
			for (int i = 0; i < n; ++i) {
				out[i] = a2 * r.drand() - a;
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = *aa; aa += aStride;
				out[i] = 2. * a * r.drand() - a;
			}
		}
	}
};

struct XorNoise1 : OneInputUGen<XorNoise1>
{
    uint64_t x = 0xA40203C12F2AD936LL;

	XorNoise1(Thread& th,  Arg a) : OneInputUGen<XorNoise1>(th, a)
	{
	}

	virtual const char* TypeName() const override { return "XorNoise1"; }

	void calc(int n, Z* out, Z* aa, int aStride)
	{
		if (aStride == 0) {
			Z a = *aa;
			for (int i = 0; i < n; ++i) {
				x = xorshift64star(x);
				out[i] = itof2(x,a);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = *aa; aa += aStride;
				x = xorshift64star(x);
				out[i] = itof2(x,a);
			}
		}
	}
};

struct XorNoise2 : OneInputUGen<XorNoise2>
{
    uint64_t s[2] = { 0xA40203C12F2AD936LL, 0x9E390BD16B74D6D3LL };

	XorNoise2(Thread& th,  Arg a) : OneInputUGen<XorNoise2>(th, a)
	{
	}

	virtual const char* TypeName() const override { return "XorNoise2"; }

	void calc(int n, Z* out, Z* aa, int aStride)
	{
		if (aStride == 0) {
			Z a = *aa;
			for (int i = 0; i < n; ++i) {
				uint64_t x = xorshift128plus(s);
				out[i] = itof2(x,a);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = *aa; aa += aStride;
				uint64_t x = xorshift128plus(s);
				out[i] = itof2(x,a);
			}
		}
	}
};

inline uint32_t raprng(uint64_t i, uint64_t seed)
{
// http://cessu.blogspot.com/2008/11/random-access-pseudo-random-numbers.html
  uint64_t r = (2857720171ULL * ((uint32_t) i)) ^ 0x1EF57D8A7B344E7BULL;
  r ^= r >> 29;
  r += r << 16;
  r ^= r >> 21;
  r += r >> 32;
  r = (2857720171ULL * ((uint32_t) (i ^ r))) ^ (0xD9EA571C8AF880B6ULL + seed);
  r ^= r >> 29;
  r += r << 16;
  r ^= r >> 21;
  return uint32_t(r + (r >> 32));
}

struct RandomAccessNoise : OneInputUGen<RandomAccessNoise>
{
    uint64_t seed = 0xA40203C12F2AD936LL;
	uint64_t k = 0;

	RandomAccessNoise(Thread& th,  Arg a) : OneInputUGen<RandomAccessNoise>(th, a)
	{
	}

	virtual const char* TypeName() const override { return "RandomAccessNoise"; }

	void calc(int n, Z* out, Z* aa, int aStride)
	{
		if (aStride == 0) {
			Z a = *aa;
			for (int i = 0; i < n; ++i) {
				uint32_t x = raprng(k++, seed);
				out[i] = itof2(x,a);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = *aa; aa += aStride;
				uint32_t x = raprng(k++, seed);
				out[i] = itof2(x,a);
			}
		}
	}
};

inline uint64_t hash64shift(uint64_t key)
{
  key = (~key) + (key << 21); // key = (key << 21) - key - 1;
  key = key ^ (key >> 24);
  key = (key + (key << 3)) + (key << 8); // key * 265
  key = key ^ (key >> 14);
  key = (key + (key << 2)) + (key << 4); // key * 21
  key = key ^ (key >> 28);
  key = key + (key << 31);
  return key;
}

struct WangNoise : OneInputUGen<WangNoise>
{
	uint64_t k = 0xA40203C12F2AD936LL;

	WangNoise(Thread& th,  Arg a) : OneInputUGen<WangNoise>(th, a)
	{
	}

	virtual const char* TypeName() const override { return "WangNoise"; }

	void calc(int n, Z* out, Z* aa, int aStride)
	{
		if (aStride == 0) {
			Z a = *aa;
			for (int i = 0; i < n; ++i) {
				uint32_t x = (uint32_t)hash64shift(k++);
				out[i] = itof2(x,a);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = *aa; aa += aStride;
				uint32_t x = (uint32_t)hash64shift(k++);
				out[i] = itof2(x,a);
			}
		}
	}
};

inline uint64_t Hash128to64(uint64_t x, uint64_t y) {
	const uint64_t kMul = 0x9ddfea08eb382d69ULL;
	uint64_t a = (x ^ y) * kMul;
	a ^= (a >> 47);
	uint64_t b = (y ^ a) * kMul;
	b ^= (b >> 47);
	b *= kMul;
	return b;
}

struct CityNoise : OneInputUGen<CityNoise>
{
	uint64_t k = 0xA40203C12F2AD936LL;

	CityNoise(Thread& th,  Arg a) : OneInputUGen<CityNoise>(th, a)
	{
	}

	virtual const char* TypeName() const override { return "CityNoise"; }

	void calc(int n, Z* out, Z* aa, int aStride)
	{
		if (aStride == 0) {
			Z a = *aa;
			for (int i = 0; i < n; ++i) {
				uint32_t x = (uint32_t)Hash128to64(k++, 0x1EF57D8A7B344E7BULL);
				out[i] = itof2(x,a);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = *aa; aa += aStride;
				uint32_t x = (uint32_t)Hash128to64(k++, 0x1EF57D8A7B344E7BULL);
				out[i] = itof2(x,a);
			}
		}
	}
};

struct Violet : OneInputUGen<Violet>
{
    RGen r;
	Z prev;

	Violet(Thread& th,  Arg a) : OneInputUGen<Violet>(th, a), prev(0.)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "Violet"; }

	void calc(int n, Z* out, Z* aa, int aStride)
	{
		if (aStride == 0) {
			Z a = *aa;
			Z a2 = .5 * a;
			for (int i = 0; i < n; ++i) {
				Z x = a * r.drand() - a2;
				out[i] = x - prev;
				prev = x;
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = *aa; aa += aStride;
				Z x = a * r.drand() - .5 * a;
				out[i] = x - prev;
				prev = x;
			}
		}
	}
};

struct NRand2 : NOneInputGen<NRand2>
{
    RGen r;

	NRand2(Thread& th, int64_t n, Arg a) : NOneInputGen<NRand2>(th, n, a)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NRand2"; }

	void calc(int n, V* out, V* aa, int aStride)
	{
		if (aStride == 0) {
			Z a = aa->asFloat();
			Z a2 = 2. * a;
			for (int i = 0; i < n; ++i) {
				out[i] = a2 * r.drand() - a;
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = aa->asFloat(); aa += aStride;
				out[i] = 2. * a * r.drand() - a;
			}
		}
	}
};

struct NRand2z : NOneInputUGen<NRand2z>
{
    RGen r;

	NRand2z(Thread& th, int64_t n, Arg a) : NOneInputUGen<NRand2z>(th, n, a)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NRand2z"; }

	void calc(int n, Z* out, Z* aa, int aStride)
	{
		if (aStride == 0) {
			Z a = *aa;
			Z a2 = 2. * a;
			for (int i = 0; i < n; ++i) {
				out[i] = a2 * r.drand() - a;
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = *aa; aa += aStride;
				out[i] = 2. * a * r.drand() - a;
			}
		}
	}
};

static void rand2s_(Thread& th, Prim* prim)
{
	V a = th.pop();

	Gen* g = new Rand2(th, a);
	th.push(new List(g));
}

static void rand2z_(Thread& th, Prim* prim)
{
	V a = th.pop();

	Gen* g = new Rand2z(th, a);
	th.push(new List(g));
}

static void nrand2s_(Thread& th, Prim* prim)
{
	V a = th.pop();
	int64_t n = th.popInt("nrand2s : n");

	Gen* g = new NRand2(th, n, a);
	th.push(new List(g));
}

static void nrand2z_(Thread& th, Prim* prim)
{
	V a = th.pop();
	int64_t n = th.popInt("nrand2z : n");

	Gen* g = new NRand2z(th, n, a);
	th.push(new List(g));
}

static void white_(Thread& th, Prim* prim)
{
	// synonym for rand2z
	V a = th.popZIn("white : a");
	Gen* g = new Rand2z(th, a);
	th.push(new List(g));
}

static void wangwhite_(Thread& th, Prim* prim)
{
	// synonym for rand2z
	V a = th.popZIn("wangwhite : a");
	Gen* g = new WangNoise(th, a);
	th.push(new List(g));
}

static void citywhite_(Thread& th, Prim* prim)
{
	// synonym for rand2z
	V a = th.popZIn("citywhite : a");
	Gen* g = new CityNoise(th, a);
	th.push(new List(g));
}

static void rawhite_(Thread& th, Prim* prim)
{
	// synonym for rand2z
	V a = th.popZIn("rawhite : a");
	Gen* g = new RandomAccessNoise(th, a);
	th.push(new List(g));
}

static void xorwhite_(Thread& th, Prim* prim)
{
	// synonym for rand2z
	V a = th.popZIn("xorwhite : a");
	Gen* g = new XorNoise1(th, a);
	th.push(new List(g));
}

static void xorwhite2_(Thread& th, Prim* prim)
{
	// synonym for rand2z
	V a = th.popZIn("xorwhite2 : a");
	Gen* g = new XorNoise2(th, a);
	th.push(new List(g));
}

static void violet_(Thread& th, Prim* prim)
{
	// synonym for rand2z
	V a = th.popZIn("white : a");
	Gen* g = new Violet(th, a);
	th.push(new List(g));
}

static void rand2_(Thread& th, Prim* prim)
{
	Z a = th.popFloat("rand2 : a");
	RGen& r = th.rgen;

	Z z = 2. * a * r.drand() - a;
	th.push(z);
}

struct IRand2 : OneInputGen<IRand2>
{
    RGen r;

	IRand2(Thread& th,  Arg a) : OneInputGen<IRand2>(th, a)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "IRand2"; }

	void calc(int n, V* out, V* aa, int aStride)
	{
		if (aStride == 0) {
			Z a = aa->asFloat();
			Z a2p1 = 2. * a + 1.;
			for (int i = 0; i < n; ++i) {
				out[i] = floor(a2p1 * r.drand() - a);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = aa->asFloat(); aa += aStride;
				out[i] = floor((2. * a + 1.) * r.drand() - a);
			}
		}
	}
};

struct IRand2z : OneInputUGen<IRand2z>
{
    RGen r;

	IRand2z(Thread& th,  Arg a) : OneInputUGen<IRand2z>(th, a)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "IRand2z"; }

	void calc(int n, Z* out, Z* aa, int aStride)
	{
		if (aStride == 0) {
			Z a = *aa;
			Z a2p1 = 2. * a + 1.;
			for (int i = 0; i < n; ++i) {
				out[i] = floor(a2p1 * r.drand() - a);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = *aa; aa += aStride;
				out[i] = floor((2. * a + 1.) * r.drand() - a);
			}
		}
	}
};

struct NIRand2 : NOneInputGen<NIRand2>
{
    RGen r;

	NIRand2(Thread& th, int64_t n, Arg a) : NOneInputGen<NIRand2>(th, n, a)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NIRand2"; }

	void calc(int n, V* out, V* aa, int aStride)
	{
		if (aStride == 0) {
			Z a = aa->asFloat();
			Z a2p1 = 2. * a + 1.;
			for (int i = 0; i < n; ++i) {
				out[i] = floor(a2p1 * r.drand() - a);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = aa->asFloat(); aa += aStride;
				out[i] = floor((2. * a + 1.) * r.drand() - a);
			}
		}
	}
};

struct NIRand2z : NOneInputUGen<NIRand2z>
{
    RGen r;

	NIRand2z(Thread& th, int64_t n, Arg a) : NOneInputUGen<NIRand2z>(th, n, a)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NIRand2z"; }

	void calc(int n, Z* out, Z* aa, int aStride)
	{
		if (aStride == 0) {
			Z a = *aa;
			Z a2p1 = 2. * a + 1.;
			for (int i = 0; i < n; ++i) {
				out[i] = floor(a2p1 * r.drand() - a);
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = *aa; aa += aStride;
				out[i] = floor((2. * a + 1.) * r.drand() - a);
			}
		}
	}
};

static void irand2s_(Thread& th, Prim* prim)
{
	V a = th.pop();

	Gen* g = new IRand2(th, a);
	th.push(new List(g));
}

static void irand2z_(Thread& th, Prim* prim)
{
	V a = th.pop();

	Gen* g = new IRand2z(th, a);
	th.push(new List(g));
}

static void nirand2s_(Thread& th, Prim* prim)
{
	V a = th.pop();
	int64_t n = th.popInt("nirand2s : n");

	Gen* g = new NIRand2(th, n, a);
	th.push(new List(g));
}

static void nirand2z_(Thread& th, Prim* prim)
{
	V a = th.pop();
	int64_t n = th.popInt("nirand2z : n");

	Gen* g = new NIRand2z(th, n, a);
	th.push(new List(g));
}

static void irand2_(Thread& th, Prim* prim)
{
	int64_t a = th.popInt("irand2 : a");
	RGen& r = th.rgen;

	Z z = floor((2. * a + 1.) * r.drand() - a);
	th.push(z);
}

struct Pick : ZeroInputGen<Pick>
{
	P<Array> _array;
	RGen r;

	Pick(Thread& th, P<Array> const& array) : ZeroInputGen<Pick>(th, false), _array(array)
	{
		r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "Pick"; }

	void calc(int n, V* out)
	{
		int64_t hi = _array->size();
		if (_array->isZ()) {
			Z* items = _array->z();
			for (int i = 0; i < n; ++i) {
				out[i] = items[r.irand0(hi)];
			}
		} else {
			V* items = _array->v();
			for (int i = 0; i < n; ++i) {
				out[i] = items[r.irand0(hi)];
			}
		}
	}
};

struct Pickz : ZeroInputUGen<Pickz>
{
	P<Array> _array;
	RGen r;

	Pickz(Thread& th, P<Array> const& array) : ZeroInputUGen<Pickz>(th, false), _array(array)
	{
		r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "Pickz"; }

	void calc(int n, Z* out)
	{
		int64_t hi = _array->size();
		Z* items = _array->z();
		for (int i = 0; i < n; ++i) {
			out[i] = items[r.irand0(hi)];
		}
	}
};

struct NPick : NZeroInputGen<NPick>
{
	P<Array> _array;
	RGen r;

	NPick(Thread& th, int64_t n, P<Array> const& array) : NZeroInputGen<NPick>(th, n), _array(array)
	{
		r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NPick"; }

	void calc(int n, V* out)
	{
		int64_t hi = _array->size();
		if (_array->isZ()) {
			Z* items = _array->z();
			for (int i = 0; i < n; ++i) {
				out[i] = items[r.irand0(hi)];
			}
		} else {
			V* items = _array->v();
			for (int i = 0; i < n; ++i) {
				out[i] = items[r.irand0(hi)];
			}
		}
	}
};

struct NPickz : NZeroInputUGen<NPickz>
{
	P<Array> _array;
	RGen r;

	NPickz(Thread& th, int64_t n, P<Array> const& array) : NZeroInputUGen<NPickz>(th, n), _array(array)
	{
		r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NPickz"; }

	void calc(int n, Z* out)
	{
		int64_t hi = _array->size();
		Z* items = _array->z();
		for (int i = 0; i < n; ++i) {
			out[i] = items[r.irand0(hi)];
		}
	}
};

static void picks_(Thread& th, Prim* prim)
{
	P<List> a = th.popList("picks : list");
	if (!a->isFinite())
		indefiniteOp("picks : list must be finite","");

	a = a->pack(th);

	Gen* g = new Pick(th, a->mArray);
	th.push(new List(g));
}

static void pickz_(Thread& th, Prim* prim)
{
	P<List> a = th.popList("pickz : list");
	if (!a->isFinite())
		indefiniteOp("pickz : list must be finite","");

	a = a->pack(th);

	Gen* g = new Pickz(th, a->mArray);
	th.push(new List(g));
}

static void npicks_(Thread& th, Prim* prim)
{
	P<List> a = th.popList("npicks : list");
	int64_t n = th.popInt("npicks : n");

	if (!a->isFinite())
		indefiniteOp("npicks : list must be finite","");

	a = a->pack(th);

	Gen* g = new NPick(th, n, a->mArray);
	th.push(new List(g));
}

static void npickz_(Thread& th, Prim* prim)
{
	P<List> a = th.popList("npickz : list");
	int64_t n = th.popInt("npickz : n");

	if (!a->isFinite())
		indefiniteOp("npickz : list must be finite","");

	a = a->pack(th);

	Gen* g = new NPickz(th, n, a->mArray);
	th.push(new List(g));
}

static void pick_(Thread& th, Prim* prim)
{
	P<List> a = th.popList("pick : list");
	if (!a->isFinite())
		indefiniteOp("pick : list must be finite","");

	a = a->pack(th);
	int64_t n = a->mArray->size();
	th.push(a->at(th.rgen.irand0(n)));
}

static int64_t weightIndex(int64_t n, Z* z, Z r)
{
	Z sum = 0.;
	for (int64_t i = 0; i < n-1; ++i) {
		sum += z[i];
		if (r < sum) {
			return i;
		}
	}
	return n-1;
}

struct WPick : ZeroInputGen<WPick>
{
	P<Array> _array;
	P<Array> _weights;
	RGen r;

	WPick(Thread& th, P<Array> const& array, P<Array> const& weights) : ZeroInputGen<WPick>(th, false), _array(array), _weights(weights)
	{
		r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "WPick"; }

	void calc(int n, V* out)
	{
		int64_t an = _array->size();
		Z* w = _weights->z();
		if (_array->isZ()) {
			Z* items = _array->z();
			for (int i = 0; i < n; ++i) {
				int64_t j = weightIndex(an, w, r.drand());
				out[i] = items[j];
			}
		} else {
			V* items = _array->v();
			for (int i = 0; i < n; ++i) {
				int64_t j = weightIndex(an, w, r.drand());
				out[i] = items[j];
			}
		}
	}
};

struct WPickz : ZeroInputUGen<WPickz>
{
	P<Array> _array;
	P<Array> _weights;
	RGen r;

	WPickz(Thread& th, P<Array> const& array, P<Array> const& weights) : ZeroInputUGen<WPickz>(th, false), _array(array), _weights(weights)
	{
		r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "WPickz"; }

	void calc(int n, Z* out)
	{
		int64_t an = _array->size();
		Z* w = _weights->z();
		Z* items = _array->z();
		for (int i = 0; i < n; ++i) {
			int64_t j = weightIndex(an, w, r.drand());
			out[i] = items[j];
		}
	}
};

struct NWPick : NZeroInputGen<NWPick>
{
	P<Array> _array;
	P<Array> _weights;
	RGen r;

	NWPick(Thread& th, int64_t n, P<Array> const& array, P<Array> const& weights) : NZeroInputGen<NWPick>(th, n), _array(array), _weights(weights)
	{
		r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NWPick"; }

	void calc(int n, V* out)
	{
		int64_t an = _array->size();
		Z* w = _weights->z();
		if (_array->isZ()) {
			Z* items = _array->z();
			for (int i = 0; i < n; ++i) {
				int64_t j = weightIndex(an, w, r.drand());
				out[i] = items[j];
			}
		} else {
			V* items = _array->v();
			for (int i = 0; i < n; ++i) {
				int64_t j = weightIndex(an, w, r.drand());
				out[i] = items[j];
			}
		}
	}
};

struct NWPickz : NZeroInputUGen<NWPickz>
{
	P<Array> _array;
	P<Array> _weights;
	RGen r;

	NWPickz(Thread& th, int64_t n, P<Array> const& array, P<Array> const& weights) : NZeroInputUGen<NWPickz>(th, n), _array(array), _weights(weights)
	{
		r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NWPickz"; }

	void calc(int n, Z* out)
	{
		int64_t an = _array->size();
		Z* w = _weights->z();
		Z* items = _array->z();
		for (int i = 0; i < n; ++i) {
			int64_t j = weightIndex(an, w, r.drand());
			out[i] = items[j];
		}
	}
};

static P<Array> sumWeights(P<Array>& weights)
{
	const int64_t n = weights->size();

	P<Array> summedWeights = new Array(itemTypeZ, n);
	summedWeights->setSize(n);

	Z sum = 0.;
	Z* z = summedWeights->z();
	for (int64_t i = 0; i < n-1; ++i) {
		sum += weights->atz(i);
		z[i] = sum;
	}
	Z scale = 1. / sum;

	for (int64_t i = 0; i < n-1; ++i) {
		z[i] *= scale;
	}

	return summedWeights;
}

static void wpicks_(Thread& th, Prim* prim)
{
	P<List> w = th.popList("wpicks : weights");
	P<List> a = th.popList("wpicks : list");
	if (!a->isFinite())
		indefiniteOp("wpicks : list must be finite","");
	if (!w->isFinite())
		indefiniteOp("wpicks : weights must be finite","");

	a = a->pack(th);
	w = w->packz(th);

	P<Array> aa = a->mArray;
	P<Array> wa = w->mArray;

	if (aa->size() != wa->size()) {
		post("list and weights are not the same length.\n");
		throw errFailed;
	}

	Gen* g = new WPick(th, aa, wa);
	th.push(new List(g));
}

static void wpickz_(Thread& th, Prim* prim)
{
	P<List> w = th.popList("wpickz : weights");
	P<List> a = th.popList("wpickz : list");
	if (!a->isFinite())
		indefiniteOp("wpickz : list must be finite","");
	if (!w->isFinite())
		indefiniteOp("wpickz : weights must be finite","");

	a = a->pack(th);
	w = w->packz(th);

	P<Array> aa = a->mArray;
	P<Array> wa = w->mArray;

	if (aa->size() != wa->size()) {
		post("list and weights are not the same length.\n");
		throw errFailed;
	}

	Gen* g = new WPickz(th, aa, wa);
	th.push(new List(g));
}

static void nwpicks_(Thread& th, Prim* prim)
{
	P<List> w = th.popList("nwpicks : weights");
	P<List> a = th.popList("nwpicks : list");
	int64_t n = th.popInt("nwpicks : n");

	if (!a->isFinite())
		indefiniteOp("nwpicks : list must be finite","");
	if (!w->isFinite())
		indefiniteOp("nwpicks : weights must be finite","");

	a = a->pack(th);
	w = w->packz(th);

	P<Array> aa = a->mArray;
	P<Array> wa = w->mArray;

	if (aa->size() != wa->size()) {
		post("list and weights are not the same length.\n");
		throw errFailed;
	}

	Gen* g = new NWPick(th, n, aa, wa);
	th.push(new List(g));
}

static void nwpickz_(Thread& th, Prim* prim)
{
	P<List> w = th.popList("nwpickz : weights");
	P<List> a = th.popList("nwpickz : list");
	int64_t n = th.popInt("nwpickz : n");

	if (!a->isFinite())
		indefiniteOp("nwpickz : list must be finite","");
	if (!w->isFinite())
		indefiniteOp("nwpickz : weights must be finite","");

	a = a->pack(th);
	w = w->packz(th);

	P<Array> aa = a->mArray;
	P<Array> wa = w->mArray;

	if (aa->size() != wa->size()) {
		post("list and weights are not the same length.\n");
		throw errFailed;
	}

	Gen* g = new NWPickz(th, n, aa, wa);
	th.push(new List(g));
}

static void wpick_(Thread& th, Prim* prim)
{
	P<List> w = th.popList("wpick : weights");
	P<List> a = th.popList("wpick : list");
	if (!a->isFinite())
		indefiniteOp("wpick : list must be finite","");
	if (!w->isFinite())
		indefiniteOp("wpick : weights must be finite","");

	a = a->pack(th);
	w = w->pack(th);

	P<Array> aa = a->mArray;
	P<Array> wa = w->mArray;
	const int64_t n = aa->size();
	const int64_t wn = wa->size();

	if (n != wn) {
		post("list and weights are not the same length.\n");
		throw errFailed;
	}

	const Z r = th.rgen.drand();
	Z sum = 0.;
	for (int64_t i = 0; i < n-1; ++i) {
		sum += wa->atz(i);
		if (r < sum) {
			th.push(aa->at(i));
			return;
		}
	}
	th.push(aa->at(n-1));
}

struct WRand : ZeroInputGen<WRand>
{
	P<Array> _weights;
	RGen r;

	WRand(Thread& th, P<Array> const& weights) : ZeroInputGen<WRand>(th, false), _weights(weights)
	{
		r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "WRand"; }

	void calc(int n, V* out)
	{
		int64_t wn = _weights->size();
		Z* w = _weights->z();
		for (int i = 0; i < n; ++i) {
			out[i] = (Z)weightIndex(wn, w, r.drand());
		}
	}
};

struct WRandz : ZeroInputUGen<WRandz>
{
	P<Array> _weights;
	RGen r;

	WRandz(Thread& th, P<Array> const& weights) : ZeroInputUGen<WRandz>(th, false), _weights(weights)
	{
		r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "WRandz"; }

	void calc(int n, Z* out)
	{
		int64_t wn = _weights->size();
		Z* w = _weights->z();
		for (int i = 0; i < n; ++i) {
			out[i] = (Z)weightIndex(wn, w, r.drand());
		}
	}
};

struct NWRand : NZeroInputGen<NWRand>
{
	P<Array> _weights;
	RGen r;

	NWRand(Thread& th, int64_t n, P<Array> const& weights) : NZeroInputGen<NWRand>(th, n), _weights(weights)
	{
		r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NWRand"; }

	void calc(int n, V* out)
	{
		int64_t wn = _weights->size();
		Z* w = _weights->z();
		for (int i = 0; i < n; ++i) {
			out[i] = (Z)weightIndex(wn, w, r.drand());
		}
	}
};

struct NWRandz : NZeroInputUGen<NWRandz>
{
	P<Array> _weights;
	RGen r;

	NWRandz(Thread& th, int64_t n, P<Array> const& weights) : NZeroInputUGen<NWRandz>(th, n), _weights(weights)
	{
		r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "NWRandz"; }

	void calc(int n, Z* out)
	{
		int64_t wn = _weights->size();
		Z* w = _weights->z();
		for (int i = 0; i < n; ++i) {
			out[i] = (Z)weightIndex(wn, w, r.drand());
		}
	}
};

static void wrands_(Thread& th, Prim* prim)
{
	P<List> w = th.popList("wrands : weights");
	if (!w->isFinite())
		indefiniteOp("wrands : weights must be finite","");

	w = w->packz(th);

	P<Array> wa = w->mArray;

	Gen* g = new WRand(th, wa);
	th.push(new List(g));
}

static void wrandz_(Thread& th, Prim* prim)
{
	P<List> w = th.popList("wrandz : weights");
	if (!w->isFinite())
		indefiniteOp("wrandz : weights must be finite","");

	w = w->packz(th);

	P<Array> wa = w->mArray;

	Gen* g = new WRandz(th, wa);
	th.push(new List(g));
}

static void nwrands_(Thread& th, Prim* prim)
{
	P<List> w = th.popList("nwrands : weights");
	int64_t n = th.popInt("nwrands : n");

	if (!w->isFinite())
		indefiniteOp("nwrands : weights must be finite","");

	w = w->packz(th);

	P<Array> wa = w->mArray;

	Gen* g = new NWRand(th, n, wa);
	th.push(new List(g));
}

static void nwrandz_(Thread& th, Prim* prim)
{
	P<List> w = th.popList("nwrandz : weights");
	int64_t n = th.popInt("nwrandz : n");

	if (!w->isFinite())
		indefiniteOp("nwrandz : weights must be finite","");

	w = w->packz(th);

	P<Array> wa = w->mArray;

	Gen* g = new NWRandz(th, n, wa);
	th.push(new List(g));
}

static void wrand_(Thread& th, Prim* prim)
{
	P<List> w = th.popList("wrand : weights");
	if (!w->isFinite())
		indefiniteOp("wrand : weights must be finite","");

	w = w->pack(th);

	P<Array> wa = w->mArray;
	const int64_t n = wa->size();

	const Z r = th.rgen.drand();
	Z sum = 0.;
	for (int64_t i = 0; i < n-1; ++i) {
		sum += wa->atz(i);
		if (r < sum) {
			th.push((Z)i);
			return;
		}
	}
	th.push((Z)(n-1));
}

struct GrayNoise : OneInputUGen<GrayNoise>
{
    RGen r;
	int32_t counter_;

	GrayNoise(Thread& th,  Arg a) : OneInputUGen<GrayNoise>(th, a), counter_(0)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "GrayNoise"; }

	void calc(int n, Z* out, Z* aa, int aStride)
	{
		Z K = 4.65661287308e-10f;
		int32_t counter = counter_;
		if (aStride == 0) {
			Z a = *aa * K;
			for (int i = 0; i < n; ++i) {
				counter ^= int32_t(1) << (r.trand() & 31);
				out[i] = counter * a;
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = *aa * K; aa += aStride;
				counter ^= int32_t(1) << (r.trand() & 31);
				out[i] = counter * a;
			}
		}
		counter_ = counter;
	}
};

struct Gray64Noise : OneInputUGen<Gray64Noise>
{
    RGen r;
	int64_t counter_;

	Gray64Noise(Thread& th,  Arg a) : OneInputUGen<Gray64Noise>(th, a), counter_(0)
	{
        r.init(th.rgen.trand());
	}

	virtual const char* TypeName() const override { return "Gray64Noise"; }

	void calc(int n, Z* out, Z* aa, int aStride)
	{
		Z K = 1.084202172485504434e-19;
		int64_t counter = counter_;
		if (aStride == 0) {
			Z a = *aa * K;
			for (int i = 0; i < n; ++i) {
				counter ^= 1LL << (r.trand() & 63);
				out[i] = counter * a;
			}
		} else {
			for (int i = 0; i < n; ++i) {
				Z a = *aa * K; aa += aStride;
				counter ^= 1LL << (r.trand() & 63);
				out[i] = counter * a;
			}
		}
		counter_ = counter;
	}
};

static void gray_(Thread& th, Prim* prim)
{
	V a = th.popZIn("gray : a");

	th.push(new List(new GrayNoise(th, a)));
}

static void gray64_(Thread& th, Prim* prim)
{
	V a = th.popZIn("gray64 : a");

	th.push(new List(new Gray64Noise(th, a)));
}

struct PinkNoise : Gen
{
	ZIn _a;
	uint64_t dice[16];
	uint64_t total_;

	PinkNoise(Thread& th, Arg a)
    : Gen(th, itemTypeZ, a.isFinite()), _a(a)
	{
		total_ = 0;
		RGen& r = th.rgen;
		for (int i = 0; i < 16; ++i) {
			int64_t x = (uint64_t)r.trand() >> 16;
			total_ += x;
			dice[i] = x;
		}
	}

	virtual const char* TypeName() const override { return "PinkNoise"; }

	virtual void pull(Thread& th) override {
		RGen& r = th.rgen;
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		uint64_t total = total_;
		while (framesToFill) {
			int n = framesToFill;
			int astride;
			Z *aa;
			if (_a(th, n,astride, aa)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					uint64_t newrand = r.trand(); // Magnus Jonsson's suggestion.
					uint32_t counter = (uint32_t)newrand;
					newrand = newrand >> 16;
					int k = (CTZ(counter)) & 15;
					uint64_t prevrand = dice[k];
					dice[k] = newrand;
					total += (newrand - prevrand);
					newrand = (uint64_t)r.trand() >> 16;
					union { int64_t i; double f; } u;
					u.i = (total + newrand) | 0x4000000000000000LL;
					out[i] = *aa * (u.f - 3.);
					aa += astride;
				}
				_a.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		total_ = total;
		produce(framesToFill);
	}
};

struct PinkNoise0 : Gen
{
	ZIn _a;
	uint64_t dice[16];
	uint64_t total_;

	PinkNoise0(Thread& th, Arg a)
    : Gen(th, itemTypeZ, a.isFinite()), _a(a)
	{
		total_ = 0;
		for (int i = 0; i < 16; ++i) {
			dice[i] = 0;
		}
	}

	virtual const char* TypeName() const override { return "PinkNoise0"; }

	virtual void pull(Thread& th) override {
		RGen& r = th.rgen;
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		uint64_t total = total_;
		const double scale = pow(2.,-47.)/17.;
		while (framesToFill) {
			int n = framesToFill;
			int astride;
			Z *aa;
			if (_a(th, n,astride, aa)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					uint64_t newrand = r.trand(); // Magnus Jonsson's suggestion.
					uint32_t counter = (uint32_t)newrand;
					newrand = newrand >> 16;
					int k = (CTZ(counter)) & 15;
					uint64_t prevrand = dice[k];
					dice[k] = newrand;
					total += (newrand - prevrand);
					newrand = (uint64_t)r.trand() >> 16;
					out[i] = *aa * (scale * double(total + newrand)) - 1;
					aa += astride;
				}
				_a.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		total_ = total;
		produce(framesToFill);
	}
};

struct BlueNoise : Gen
{
	ZIn _a;
	uint64_t dice[16];
	uint64_t total_;
	Z prev;

	BlueNoise(Thread& th, Arg a)
    : Gen(th, itemTypeZ, a.isFinite()), _a(a), prev(0.)
	{
		total_ = 0;
		RGen& r = th.rgen;
		for (int i = 0; i < 16; ++i) {
			int64_t x = (uint64_t)r.trand() >> 16;
			total_ += x;
			dice[i] = x;
		}
	}

	virtual const char* TypeName() const override { return "BlueNoise"; }

	virtual void pull(Thread& th) override {
		RGen& r = th.rgen;
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		uint64_t total = total_;
		while (framesToFill) {
			int n = framesToFill;
			int astride;
			Z *aa;
			if (_a(th, n,astride, aa)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					uint64_t newrand = r.trand(); // Magnus Jonsson's suggestion.
					uint32_t counter = (uint32_t)newrand;
					newrand = newrand >> 16;
					int k = (CTZ(counter)) & 15;
					uint64_t prevrand = dice[k];
					dice[k] = newrand;
					total += (newrand - prevrand);
					newrand = (uint64_t)r.trand() >> 16;
					union { int64_t i; double f; } u;
					u.i = (total + newrand) | 0x4000000000000000LL;
					Z x = 4. * *aa * (u.f - 3.);
					out[i] = x - prev;
					prev = x;
					aa += astride;
				}
				_a.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		total_ = total;
		produce(framesToFill);
	}
};

static void pink_(Thread& th, Prim* prim)
{
	V a = th.popZIn("pink : a");

	th.push(new List(new PinkNoise(th, a)));
}

static void pink0_(Thread& th, Prim* prim)
{
	V a = th.popZIn("pink0 : a");

	th.push(new List(new PinkNoise0(th, a)));
}

static void blue_(Thread& th, Prim* prim)
{
	V a = th.popZIn("blue : a");

	th.push(new List(new BlueNoise(th, a)));
}

struct BrownNoise : Gen
{
	ZIn _a;
	Z total_;

	BrownNoise(Thread& th, Arg a)
    : Gen(th, itemTypeZ, a.isFinite()), _a(a)
	{
		total_ = 0;
		RGen& r = th.rgen;
		total_ = r.drand2();

	}

	virtual const char* TypeName() const override { return "BrownNoise"; }

	virtual void pull(Thread& th) override {
		RGen& r = th.rgen;
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		Z z = total_;
		while (framesToFill) {
			int n = framesToFill;
			int astride;
			Z *aa;
			if (_a(th, n,astride, aa)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					z += r.drand16();
					if (z > 1.) z = 2. - z;
					else if (z < -1.) z = -2. - z;
					out[i] = *aa * z;
					aa += astride;
				}
				_a.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		total_ = z;
		produce(framesToFill);
	}
};

static void brown_(Thread& th, Prim* prim)
{
	V a = th.popZIn("brown : a");

	th.push(new List(new BrownNoise(th, a)));
}

struct Dust : Gen
{
	ZIn _density;
	ZIn _amp;
	Z _densmul;

	Dust(Thread& th, Arg density, Arg amp)
    : Gen(th, itemTypeZ, mostFinite(density, amp)), _density(density), _amp(amp), _densmul(th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "Dust"; }

	virtual void pull(Thread& th) override {
		RGen& r = th.rgen;
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int densityStride, ampStride;
			Z *density, *amp;
			if (_density(th, n, densityStride, density) || _amp(th, n, ampStride, amp)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					Z thresh = *density * _densmul;
					Z z = r.drand();
					out[i] = z < thresh ? *amp * z / thresh : 0.;
					density += densityStride;
					amp += ampStride;
				}
				_density.advance(n);
				_amp.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

struct Dust2 : Gen
{
	ZIn _density;
	ZIn _amp;
	Z _densmul;

	Dust2(Thread& th, Arg density, Arg amp)
    : Gen(th, itemTypeZ, mostFinite(density, amp)), _density(density), _amp(amp), _densmul(th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "Dust2"; }

	virtual void pull(Thread& th) override {
		RGen& r = th.rgen;
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int densityStride, ampStride;
			Z *density, *amp;
			if (_density(th, n, densityStride, density) || _amp(th, n, ampStride, amp)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					Z thresh = *density * _densmul;
					Z z = r.drand();
					out[i] = z < thresh ? *amp * (2. * z / thresh - 1.) : 0.;
					density += densityStride;
					amp += ampStride;
				}
				_density.advance(n);
				_amp.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

struct Velvet : Gen
{
	ZIn _density;
	ZIn _amp;
	Z _densmul;

	Velvet(Thread& th, Arg density, Arg amp)
    : Gen(th, itemTypeZ, mostFinite(density, amp)), _density(density), _amp(amp), _densmul(th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "Velvet"; }

	virtual void pull(Thread& th) override {
		RGen& r = th.rgen;
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int densityStride, ampStride;
			Z *density, *amp;
			if (_density(th, n, densityStride, density) || _amp(th, n, ampStride, amp)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					Z thresh = *density * _densmul;
					Z thresh2 = .5 * thresh;
					Z z = r.drand();
					out[i] = z < thresh ? (z<thresh2 ? -*amp : *amp) : 0.;
					density += densityStride;
					amp += ampStride;
				}
				_density.advance(n);
				_amp.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

static void dust_(Thread& th, Prim* prim)
{
	V amp = th.popZIn("dust : amp");
	V density = th.popZIn("dust : density");

	th.push(new List(new Dust(th, density, amp)));
}

static void dust2_(Thread& th, Prim* prim)
{
	V amp = th.popZIn("dust2 : amp");
	V density = th.popZIn("dust2 : density");

	th.push(new List(new Dust2(th, density, amp)));
}

static void velvet_(Thread& th, Prim* prim)
{
	V amp = th.popZIn("velvet : amp");
	V density = th.popZIn("velvet : density");

	th.push(new List(new Velvet(th, density, amp)));
}

static inline double HashRand(int64_t i)
{
	union { uint64_t i; double f; } u;
	u.i = 0x3FF0000000000000LL | ((uint64_t)(Hash64(i) + Hash64bad(i)) & 0x000FffffFFFFffffLL);
	return u.f - 1.;
}

struct Toosh : Gen
{
	ZIn _delay;
	ZIn _amp;
	int64_t _counter;
	Z sampleRate;

	Toosh(Thread& th, Arg delay, Arg amp)
    : Gen(th, itemTypeZ, mostFinite(delay, amp)), _delay(delay), _amp(amp), sampleRate(th.rate.sampleRate)
	{
		_counter = th.rgen.trand();
	}

	virtual const char* TypeName() const override { return "Toosh"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);

		while (framesToFill) {
			int n = framesToFill;
			int delayStride, ampStride;
			Z *delay, *amp;
			if (_delay(th, n, delayStride, delay) || _amp(th, n, ampStride, amp)) {
				setDone();
				break;
			} else {
				if (delayStride) {
					for (int i = 0; i < n; ++i) {
						int64_t delaySamples = (int64_t)floor(sampleRate * *delay + .5);
						Z x = HashRand(_counter);
						Z y = HashRand(_counter - delaySamples);
						out[i] = .5 * *amp * (x - y);
						delay += delayStride;
						amp += ampStride;
						++_counter;
					}
				} else {
					int64_t delaySamples = (int64_t)floor(sampleRate * *delay + .5);
					for (int i = 0; i < n; ++i) {
						Z x = HashRand(_counter);
						Z y = HashRand(_counter - delaySamples);
						out[i] = .5 * *amp * (x - y);
						amp += ampStride;
						++_counter;
					}
				}
				_delay.advance(n);
				_amp.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

struct TooshPlus : Gen
{
	ZIn _delay;
	ZIn _amp;
	int64_t _counter;
	Z sampleRate;

	TooshPlus(Thread& th, Arg delay, Arg amp)
    : Gen(th, itemTypeZ, mostFinite(delay, amp)), _delay(delay), _amp(amp), sampleRate(th.rate.sampleRate)
	{
		_counter = th.rgen.trand();
	}

	virtual const char* TypeName() const override { return "TooshPlus"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);

		while (framesToFill) {
			int n = framesToFill;
			int delayStride, ampStride;
			Z *delay, *amp;
			if (_delay(th, n, delayStride, delay) || _amp(th, n, ampStride, amp)) {
				setDone();
				break;
			} else {
				if (delayStride) {
					for (int i = 0; i < n; ++i) {
						int64_t delaySamples = (int64_t)floor(sampleRate * *delay + .5);
						Z x = HashRand(_counter);
						Z y = HashRand(_counter - delaySamples);
						out[i] = .5 * *amp * (x + y);
						delay += delayStride;
						amp += ampStride;
						++_counter;
					}
				} else {
					int64_t delaySamples = (int64_t)floor(sampleRate * *delay + .5);
					for (int i = 0; i < n; ++i) {
						Z x = HashRand(_counter);
						Z y = HashRand(_counter - delaySamples);
						out[i] = .5 * *amp * (x + y);
						amp += ampStride;
						++_counter;
					}
				}
				_delay.advance(n);
				_amp.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

static void toosh_(Thread& th, Prim* prim)
{
	V amp = th.popZIn("toosh : amp");
	V delay = th.popZIn("toosh : delay");

	th.push(new List(new Toosh(th, delay, amp)));
}

static void tooshp_(Thread& th, Prim* prim)
{
	V amp = th.popZIn("tooshp : amp");
	V delay = th.popZIn("tooshp : delay");

	th.push(new List(new TooshPlus(th, delay, amp)));
}

struct Crackle : Gen
{
	ZIn _param;
	Z _y1, _y2;

	Crackle(Thread& th, Arg param)
    : Gen(th, itemTypeZ, param.isFinite()), _param(param), _y1(th.rgen.drand()), _y2(0.)
	{
	}

	virtual const char* TypeName() const override { return "Crackle"; }

	virtual void pull(Thread& th) override {
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int paramStride;
			Z *param;
			if (_param(th, n, paramStride, param)) {
				setDone();
				break;
			} else {
                Z y1 = _y1;
                Z y2 = _y2;
				for (int i = 0; i < n; ++i) {
                    Z y0 = fabs(y1 * *param - y2 - 0.05);
                    y2 = y1; y1 = y0;
					out[i] = y0;
					param += paramStride;
				}
                _y1 = y1;
                _y2 = y2;
				_param.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
	}
};

static void crackle_(Thread& th, Prim* prim)
{
	V param = th.popZIn("cracke : param");

	th.push(new List(new Crackle(th, param)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma mark ADD RANDOM OPS

#define DEF(NAME, TAKES, LEAVES, HELP) 	vm.def(#NAME, TAKES, LEAVES, NAME##_, HELP);
#define DEFMCX(NAME, N, HELP) 	vm.defmcx(#NAME, N, NAME##_, HELP);
#define DEFAM(NAME, MASK, HELP) 	vm.defautomap(#NAME, #MASK, NAME##_, HELP);
#define DEFnoeach(NAME, TAKES, LEAVES, HELP) 	vm.def(#NAME, TAKES, LEAVES, NAME##_, HELP, V(0.), true);

void AddRandomOps();
void AddRandomOps()
{
	vm.addBifHelp("\n*** random number generation ***");

	DEFnoeach(newseed, 0, 1, "(--> seed) make a new random seed.");
	DEFnoeach(setseed, 1, 0, "(seed -->) set the random seed.");

	vm.addBifHelp("\n*** single random numbers ***");
	DEFAM(rand, kk, "(a b --> r) return a uniformly distributed random real value from a to b.")
	DEFAM(coin, k, "(p --> r) return 1 with probability p, or 0 with probability (1-p).")
	DEFAM(rand2, k, "(a --> r) return a uniformly distributed random real value from -a to +a.")
	DEFAM(irand, kk, "(a b --> r) return a uniformly distributed random integer value from a to b.")
	DEFAM(irand2, k, "(a --> r) return a uniformly distributed random real value from -a to +a.")
	DEFAM(xrand, kk, "(a b --> r) return a exponentially distributed random real value from a to b.")
	DEFAM(linrand, kk, "(a b --> r) return a linearly distributed random real value from a to b.")
	DEFAM(ilinrand, kk, "(a b --> r) return a linearly distributed random integer value from a to b.")
	DEF(wrand, 1, 1, "(w --> r) return a randomly chosen index from a list of probability weights. w should sum to one.")
	DEF(pick, 1, 1, "(a --> r) return a randomly chosen element from the finite list a.")
	DEF(wpick, 2, 1, "(a w --> r) return a randomly chosen element from the finite list a using probability weights from w. w must be the same length as a and should sum to one.")

	vm.addBifHelp("\n*** random streams ***");
	DEFAM(rands, kk, "(a b --> r) return a stream of uniformly distributed random real values from a to b.")
	DEFAM(coins, k, "(p --> r) return a stream of 1 with probability p, or 0 with probability (1-p).")
	DEFAM(eprands, kk, "(a b --> r) return a stream of uniformly distributed random integer values from a to b, excluding the previously returned value.")
	DEFAM(rand2s, k, "(a --> r) return a stream of uniformly distributed random real values from -a to +a.")
	DEFAM(irands, kk, "(a b --> r) return a stream of uniformly distributed random integer values from a to b.")
	DEFAM(irand2s, k, "(a --> r) return a stream of uniformly distributed random real values from -a to +a.")
	DEFAM(xrands, kk, "(a b --> r) return a stream of exponentially distributed random real values from a to b.")
	DEFAM(linrands, kk, "(a b --> r) return a stream of linearly distributed random real values from a to b.")
	DEFAM(ilinrands, kk, "(a b --> r) return a stream of linearly distributed random integer values from a to b.")
	DEF(wrands, 1, 1, "(w --> r) return a stream of randomly chosen indices from a list of probability weights. w should sum to one.")
	DEF(picks, 1, 1, "(a --> r) return a stream of randomly chosen elements from the finite list a.")
	DEF(wpicks, 2, 1, "(a w --> r) return a stream of randomly chosen elements from the finite list a using probability weights from w. w must be the same length as a and should sum to one.")

	vm.addBifHelp("\n*** random signals ***");
	DEFMCX(randz, 2, "(a b --> r) return a signal of uniformly distributed random real values from a to b.")
	DEFMCX(coinz, 1, "(p --> r) return a signal of 1 with probability p, or 0 with probability (1-p).")
	DEFMCX(eprandz, 2, "(a b --> r) return a signal of uniformly distributed random integer values from a to b, excluding the previously returned value")
	DEFMCX(rand2z, 1, "(a --> r) return a signal of uniformly distributed random real values from -a to +a.")
	DEFMCX(irandz, 2, "(a b --> r) return a signal of uniformly distributed random integer values from a to b.")
	DEFMCX(irand2z, 1, "(a --> r) return a signal of uniformly distributed random real values from -a to +a.")
	DEFMCX(xrandz, 2, "(a b --> r) return a signal of exponentially distributed random real values from a to b.")
	DEFMCX(linrandz, 2, "(a b --> r) return a signal of linearly distributed random real values from a to b.")
	DEFMCX(ilinrandz, 2, "(a b --> r) return a signal of linearly distributed random integer values from a to b.")
	DEFMCX(wrandz, 1, "(w --> r) return a signal of randomly chosen indices from a list of probability weights. w should sum to one.")
	DEFMCX(pickz, 1, "(a --> r) return a signal of randomly chosen elements from the finite list a.")
	DEFMCX(wpickz, 2, "(a w --> r) return a signal of randomly chosen elements from the finite list a using probability weights from w. w must be the same length as a and should sum to one.")

	vm.addBifHelp("\n*** finite random streams ***");
	DEFAM(nrands, kkk, "(n a b --> r) return a stream of n uniformly distributed random real values from a to b.")
	DEFAM(ncoins, kk, "(n p --> r) return a stream of n 1 with probability p, or 0 with probability (1-p).")
	DEFAM(neprands, kkk, "(n a b --> r) return a stream of n uniformly distributed random integer values from a to b, excluding the previously returned value.")
	DEFAM(nrand2s, kk, "(n a --> r) return a stream of n uniformly distributed random real values from -a to +a.")
	DEFAM(nirands, kkk, "(n a b --> r) return a stream of n uniformly distributed random integer values from a to b.")
	DEFAM(nirand2s, kk, "(n a --> r) return a stream of n uniformly distributed random real values from -a to +a.")
	DEFAM(nxrands, kkk, "(n a b --> r) return a stream of n exponentially distributed random real values from a to b.")
	DEFAM(nlinrands, kkk, "(n a b --> r) return a stream of n linearly distributed random real values from a to b.")
	DEFAM(nilinrands, kkk, "(n a b --> r) return a stream of n linearly distributed random integer values from a to b.")
	DEFAM(nwrands, ka, "(n w --> r) return a stream of n randomly chosen indices from a list of probability weights. w should sum to one.")
	DEFAM(npicks, ka, "(n a --> r) return a stream of n randomly chosen elements from the finite list a.")
	DEFAM(nwpicks, kaa, "(n a w --> r) return a stream of n randomly chosen elements from the finite list a using probability weights from w. w must be the same length as a and should sum to one.")

	vm.addBifHelp("\n*** finite random signals ***");
	DEFMCX(nrandz, 3, "(n a b --> r) return a signal of n uniformly distributed random real values from a to b.")
	DEFMCX(ncoinz, 2, "(n p --> r) return a signal of n 1 with probability p, or 0 with probability (1-p).")
	DEFMCX(neprandz, 3, "(n a b --> r) return a signal of n uniformly distributed random integer values from a to b, excluding the previously returned value")
	DEFMCX(nrand2z, 2, "(n a --> r) return a signal of n uniformly distributed random real values from -a to +a.")
	DEFMCX(nirandz, 3, "(n a b --> r) return a signal of n uniformly distributed random integer values from a to b.")
	DEFMCX(nirand2z, 2, "(n a --> r) return a signal of n uniformly distributed random real values from -a to +a.")
	DEFMCX(nxrandz, 3, "(n a b --> r) return a signal of n exponentially distributed random real values from a to b.")
	DEFMCX(nlinrandz, 3, "(n a b --> r) return a signal of n linearly distributed random real values from a to b.")
	DEFMCX(nilinrandz, 3, "(n a b --> r) return a signal of n linearly distributed random integer values from a to b.")
	DEFMCX(nwrandz, 2, "(n w --> r) return a signal of n randomly chosen indices from a list of probability weights. w should sum to one.")
	DEFMCX(npickz, 2, "(n a --> r) return a signal of n randomly chosen elements from the finite signal a.")
	DEFMCX(nwpickz, 3, "(n a w --> r) return a signal of n randomly chosen elements from the finite signal a using probability weights from w. w must be the same length as a and should sum to one.")

	vm.addBifHelp("\n*** noise unit generators ***");
	DEFMCX(violet, 1, "(amp --> z) violet noise")
	DEFMCX(blue, 1, "(amp --> z) blue noise")
	DEFMCX(xorwhite, 1, "(amp --> z) white noise")
	DEFMCX(xorwhite2, 1, "(amp --> z) white noise")
	DEFMCX(rawhite, 1, "(amp --> z) white noise based on Cessu's random access random numbers")
	DEFMCX(wangwhite, 1, "(amp --> z) white noise based on Thomas Wang's integer hash")
	DEFMCX(citywhite, 1, "(amp --> z) white noise based on a function from CityHash")
	DEFMCX(white, 1, "(amp --> z) white noise")
	DEFMCX(pink, 1, "(amp --> z) pink noise")
	DEFMCX(pink0, 1, "(amp --> z) pink noise")
	DEFMCX(brown, 1, "(amp --> z) brown noise")
	DEFMCX(gray, 1, "(amp --> z) bit flip noise")
	DEFMCX(gray64, 1, "(amp --> z) bit flip noise")
	DEFMCX(dust, 2, "(density amp --> z) a stream of impulses whose amplitude is random from 0 to a and whose average density is in impulses per second.")
	DEFMCX(dust2, 2, "(density amp --> z) a stream of impulses whose amplitude is random from -a to +a and whose average density is in impulses per second.")
	DEFMCX(velvet, 2, "(density amp --> z) a stream of impulses whose amplitude is randomly either -a or +a and whose average density is in impulses per second.")
	DEFMCX(toosh, 2, "(delay amp --> z) flanged noise. difference of two white noise sources with a delay.")
	DEFMCX(tooshp, 2, "(delay amp--> z) flanged noise. sum of two white noise sources with a delay. no null at delay == 0. ")
	DEFMCX(crackle, 1, "(param --> z) a chaotic generator.")
}

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "FilterUGens.hpp"
#include "UGen.hpp"

#include "VM.hpp"
#include "clz.hpp"
#include <cmath>
#include <float.h>
#include <vector>
#include <algorithm>
#include <Accelerate/Accelerate.h>

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// classes for specializing feedback

struct NormalFeedback : public Gen
{
	static inline double feedback(double x) { return x; }
};

struct TanhApproximationFeedback : public Gen
{
	static inline double feedback(double x) { return sc_tanh_approx(x); }
};

struct UnityHardClipFeedback : public Gen
{
	static inline double feedback(double x)
	{
		if (x <= -1.) return -1.;
		if (x >=  1.) return  1.;
		return x;
	}
};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Lag : public Gen
{
	ZIn _in;
	ZIn _lagTime;
	Z _y1;
	Z _lagmul;
	bool once;

	Lag(Thread& th, Arg in, Arg lagTime)
		: Gen(th, itemTypeZ, mostFinite(in, lagTime)), _in(in), _lagTime(lagTime), _y1(0.), _lagmul(log001 * th.rate.invSampleRate), once(true)
	{
	}

	virtual const char* TypeName() const override { return "Lag"; }

	virtual void pull(Thread& th) override
	{
		if (once) {
			once = false;
			_in.peek(th, _y1);
		}
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z y1 = _y1;
		Z lagmul = _lagmul;
		while (framesToFill) {
			Z *in, *lagTime;
			int n, inStride, lagTimeStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _lagTime(th, n, lagTimeStride, lagTime)) {
				setDone();
				break;
			}

			if (lagTimeStride == 0) {
				Z b1 = std::max(0., 1. + lagmul / *lagTime); // this is an approximation to exp(log001 * th.rate.invSampleRate / *lagTime)
				for (int i = 0; i < n; ++i) {
					Z y0 = *in;
					out[i] = y1 = y0 + b1 * (y1 - y0);
					in += inStride;
				}
			} else {
				for (int i = 0; i < n; ++i) {
					Z b1 = std::max(0., 1. + lagmul / *lagTime); // this is an approximation to exp(log001 * th.rate.invSampleRate / *lagTime)
					Z y0 = *in;
					out[i] = y1 = y0 + b1 * (y1 - y0);
					in += inStride;
					lagTime += lagTimeStride;
				}
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_lagTime.advance(n);
		}

		_y1 = y1;
		produce(framesToFill);
	}

};

static void lag_(Thread& th, Prim* prim)
{
	V lagTime = th.popZIn("lag : lagTime");
	V in = th.popZIn("lag : in");

	th.push(new List(new Lag(th, in, lagTime)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Lag2 : public Gen
{
	ZIn _in;
	ZIn _lagTime;
	Z _y1a;
	Z _y1b;
	Z _lagmul;
	bool once;

	Lag2(Thread& th, Arg in, Arg lagTime)
		: Gen(th, itemTypeZ, mostFinite(in, lagTime)), _in(in), _lagTime(lagTime), _y1a(0.), _y1b(0.), _lagmul(log001 * th.rate.invSampleRate), once(true)
	{
	}

	virtual const char* TypeName() const override { return "Lag2"; }

	virtual void pull(Thread& th) override
	{
		if (once) {
			once = false;
			_in.peek(th, _y1a);
			_y1b = _y1a;
		}
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z y1a = _y1a;
		Z y1b = _y1b;
		Z lagmul = _lagmul;
		while (framesToFill) {
			Z *in, *lagTime;
			int n, inStride, lagTimeStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _lagTime(th, n, lagTimeStride, lagTime)) {
				setDone();
				break;
			}

			if (lagTimeStride == 0) {
				Z b1 = std::max(0., 1. + lagmul / *lagTime); // this is an approximation to exp(log001 * th.rate.invSampleRate / *lagTime)
				for (int i = 0; i < n; ++i) {
					Z y0a = *in;
					y1a = y0a + b1 * (y1a - y0a);
					y1b = y1a + b1 * (y1b - y1a);
					out[i] = y1b;
					in += inStride;
				}
			} else {
				for (int i = 0; i < n; ++i) {
					Z b1 = std::max(0., 1. + lagmul / *lagTime); // this is an approximation to exp(log001 * th.rate.invSampleRate / *lagTime)
					Z y0a = *in;
					y1a = y0a + b1 * (y1a - y0a);
					y1b = y1a + b1 * (y1b - y1a);
					out[i] = y1b;
					in += inStride;
					lagTime += lagTimeStride;
				}
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_lagTime.advance(n);
		}

		_y1a = y1a;
		_y1b = y1b;
		produce(framesToFill);
	}

};

static void lag2_(Thread& th, Prim* prim)
{
	V lagTime = th.popZIn("lag2 : lagTime");
	V in = th.popZIn("lag2 : in");

	th.push(new List(new Lag2(th, in, lagTime)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Lag3 : public Gen
{
	ZIn _in;
	ZIn _lagTime;
	Z _y1a;
	Z _y1b;
	Z _y1c;
	Z _lagmul;
	bool once;

	Lag3(Thread& th, Arg in, Arg lagTime)
		: Gen(th, itemTypeZ, mostFinite(in, lagTime)), _in(in), _lagTime(lagTime), _y1a(0.), _y1b(0.), _y1c(0.), _lagmul(log001 * th.rate.invSampleRate), once(true)
	{
	}

	virtual const char* TypeName() const override { return "Lag3"; }

	virtual void pull(Thread& th) override
	{
		if (once) {
			once = false;
			_in.peek(th, _y1a);
			_y1b = _y1a;
			_y1c = _y1a;
		}
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z y1a = _y1a;
		Z y1b = _y1b;
		Z y1c = _y1c;
		Z lagmul = _lagmul;
		while (framesToFill) {
			Z *in, *lagTime;
			int n, inStride, lagTimeStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _lagTime(th, n, lagTimeStride, lagTime)) {
				setDone();
				break;
			}

			if (lagTimeStride == 0) {
				Z b1 = std::max(0., 1. + lagmul / *lagTime); // this is an approximation to exp(log001 * th.rate.invSampleRate / *lagTime)
				for (int i = 0; i < n; ++i) {
					Z y0a = *in;
					y1a = y0a + b1 * (y1a - y0a);
					y1b = y1a + b1 * (y1b - y1a);
					y1c = y1b + b1 * (y1c - y1b);
					out[i] = y1c;
					in += inStride;
				}
			} else {
				for (int i = 0; i < n; ++i) {
					Z b1 = std::max(0., 1. + lagmul / *lagTime); // this is an approximation to exp(log001 * th.rate.invSampleRate / *lagTime)
					Z y0a = *in;
					y1a = y0a + b1 * (y1a - y0a);
					y1b = y1a + b1 * (y1b - y1a);
					y1c = y1b + b1 * (y1c - y1b);
					out[i] = y1c;
					in += inStride;
					lagTime += lagTimeStride;
				}
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_lagTime.advance(n);
		}

		_y1a = y1a;
		_y1b = y1b;
		_y1c = y1c;
		produce(framesToFill);
	}

};

static void lag3_(Thread& th, Prim* prim)
{
	V lagTime = th.popZIn("lag3 : lagTime");
	V in = th.popZIn("lag3 : in");

	th.push(new List(new Lag3(th, in, lagTime)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
struct LagUD : public Gen
{
	ZIn _in;
	ZIn _riseTime;
	ZIn _fallTime;
	Z _y1;
	Z _lagmul;
	bool once;

	LagUD(Thread& th, Arg in, Arg riseTime, Arg fallTime)
		: Gen(th, itemTypeZ, mostFinite(in, riseTime, fallTime)), _in(in), _riseTime(riseTime), _fallTime(fallTime), _y1(0.), _lagmul(log001 * th.rate.invSampleRate), once(true)
	{
	}

	virtual const char* TypeName() const override { return "LagUD"; }

	virtual void pull(Thread& th) override
	{
		if (once) {
			once = false;
			_in.peek(th, _y1);
		}
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z y1 = _y1;
		Z lagmul = _lagmul;
		while (framesToFill) {
			Z *in, *riseTime, *fallTime;
			int n, inStride, riseTimeStride, fallTimeStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _riseTime(th, n, riseTimeStride, riseTime) || _fallTime(th, n, fallTimeStride, fallTime)) {
				setDone();
				break;
			}

			if (riseTimeStride == 0 && fallTimeStride == 0) {
				Z b1r = std::max(0., 1. + lagmul / *riseTime); // this is an approximation to exp(log001 * th.rate.invSampleRate / *riseTime)
				Z b1f = std::max(0., 1. + lagmul / *fallTime);
				for (int i = 0; i < n; ++i) {
					Z y0 = *in;
					Z b1 = y0 > y1 ? b1r : b1f;
					out[i] = y1 = y0 + b1 * (y1 - y0);

					in += inStride;
				}
			} else {
				for (int i = 0; i < n; ++i) {
					Z y0 = *in;
					Z lagTime = y0 > y1 ? *riseTime : *fallTime;
					Z b1 = std::max(0., 1. + lagmul / lagTime); // this is an approximation to exp(log001 * th.rate.invSampleRate / *riseTime)
					out[i] = y1 = y0 + b1 * (y1 - y0);

					in += inStride;
					riseTime += riseTimeStride;
					fallTime += fallTimeStride;
				}
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_riseTime.advance(n);
			_fallTime.advance(n);
		}

		_y1 = y1;
		produce(framesToFill);
	}

};

static void lagud_(Thread& th, Prim* prim)
{
	V fallTime = th.popZIn("lagud : fallTime");
	V riseTime = th.popZIn("lagud : riseTime");
	V in = th.popZIn("lagud : in");

	th.push(new List(new LagUD(th, in, riseTime, fallTime)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct LagUD2 : public Gen
{
	ZIn _in;
	ZIn _riseTime;
	ZIn _fallTime;
	Z _y1a;
	Z _y1b;
	Z _lagmul;
	bool once;

	LagUD2(Thread& th, Arg in, Arg riseTime, Arg fallTime)
		: Gen(th, itemTypeZ, mostFinite(in, riseTime, fallTime)), _in(in), _riseTime(riseTime), _fallTime(fallTime),
			_y1a(0.), _y1b(0.), _lagmul(log001 * th.rate.invSampleRate), once(true)
	{
	}

	virtual const char* TypeName() const override { return "LagUD2"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		if (once) {
			once = false;
			_in.peek(th, _y1a);
			_y1b = _y1a;
		}

		Z* out = mOut->fulfillz(framesToFill);
		Z y1a = _y1a;
		Z y1b = _y1b;
		Z lagmul = _lagmul;
		while (framesToFill) {
			Z *in, *riseTime, *fallTime;
			int n, inStride, riseTimeStride, fallTimeStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _riseTime(th, n, riseTimeStride, riseTime) || _fallTime(th, n, fallTimeStride, fallTime)) {
				setDone();
				break;
			}

			if (riseTimeStride == 0 && fallTimeStride == 0) {
				Z b1r = std::max(0., 1. + lagmul / *riseTime); // this is an approximation to exp(log001 * th.rate.invSampleRate / *riseTime)
				Z b1f = std::max(0., 1. + lagmul / *fallTime);
				for (int i = 0; i < n; ++i) {
					Z y0a = *in;
					y1a = y0a + (y0a > y1a ? b1r : b1f) * (y1a - y0a);
					y1b = y1a + (y1a > y1b ? b1r : b1f) * (y1b - y1a);
					out[i] = y1b;

					in += inStride;
				}
			} else {
				for (int i = 0; i < n; ++i) {
					Z b1r = std::max(0., 1. + lagmul / *riseTime); // this is an approximation to exp(log001 * th.rate.invSampleRate / *riseTime)
					Z b1f = std::max(0., 1. + lagmul / *fallTime);

					Z y0a = *in;
					y1a = y0a + (y0a > y1a ? b1r : b1f) * (y1a - y0a);
					y1b = y1a + (y1a > y1b ? b1r : b1f) * (y1b - y1a);
					out[i] = y1b;

					in += inStride;
					riseTime += riseTimeStride;
					fallTime += fallTimeStride;
				}
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_riseTime.advance(n);
			_fallTime.advance(n);
		}

		_y1a = y1a;
		_y1b = y1b;
		produce(framesToFill);
	}

};

static void lagud2_(Thread& th, Prim* prim)
{
	V fallTime = th.popZIn("lagud2 : fallTime");
	V riseTime = th.popZIn("lagud2 : riseTime");
	V in = th.popZIn("lagud2 : in");

	th.push(new List(new LagUD2(th, in, riseTime, fallTime)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct LagUD3 : public Gen
{
	ZIn _in;
	ZIn _riseTime;
	ZIn _fallTime;
	Z _y1a;
	Z _y1b;
	Z _y1c;
	Z _lagmul;
	bool once;

	LagUD3(Thread& th, Arg in, Arg riseTime, Arg fallTime)
		: Gen(th, itemTypeZ, mostFinite(in, riseTime, fallTime)), _in(in), _riseTime(riseTime), _fallTime(fallTime),
			_y1a(0.), _y1b(0.), _y1c(0.), _lagmul(log001 * th.rate.invSampleRate), once(true)
	{
	}

	virtual const char* TypeName() const override { return "LagUD3"; }

	virtual void pull(Thread& th) override
	{
		if (once) {
			once = false;
			_in.peek(th, _y1a);
			_y1b = _y1a;
			_y1c = _y1a;
		}
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z y1a = _y1a;
		Z y1b = _y1b;
		Z y1c = _y1c;
		Z lagmul = _lagmul;
		while (framesToFill) {
			Z *in, *riseTime, *fallTime;
			int n, inStride, riseTimeStride, fallTimeStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _riseTime(th, n, riseTimeStride, riseTime) || _fallTime(th, n, fallTimeStride, fallTime)) {
				setDone();
				break;
			}

			if (riseTimeStride == 0 && fallTimeStride == 0) {
				Z b1r = std::max(0., 1. + lagmul / *riseTime); // this is an approximation to exp(log001 * th.rate.invSampleRate / *riseTime)
				Z b1f = std::max(0., 1. + lagmul / *fallTime);
				for (int i = 0; i < n; ++i) {
					Z y0a = *in;
					y1a = y0a + (y0a > y1a ? b1r : b1f) * (y1a - y0a);
					y1b = y1a + (y1a > y1b ? b1r : b1f) * (y1b - y1a);
					y1c = y1b + (y1b > y1c ? b1r : b1f) * (y1c - y1b);
					out[i] = y1c;

					in += inStride;
				}
			} else {
				for (int i = 0; i < n; ++i) {
					Z b1r = std::max(0., 1. + lagmul / *riseTime); // this is an approximation to exp(log001 * th.rate.invSampleRate / *riseTime)
					Z b1f = std::max(0., 1. + lagmul / *fallTime);

					Z y0a = *in;
					y1a = y0a + (y0a > y1a ? b1r : b1f) * (y1a - y0a);
					y1b = y1a + (y1a > y1b ? b1r : b1f) * (y1b - y1a);
					y1c = y1b + (y1b > y1c ? b1r : b1f) * (y1c - y1b);
					out[i] = y1c;

					in += inStride;
					riseTime += riseTimeStride;
					fallTime += fallTimeStride;
				}
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_riseTime.advance(n);
			_fallTime.advance(n);
		}

		_y1a = y1a;
		_y1b = y1b;
		_y1c = y1c;
		produce(framesToFill);
	}

};

static void lagud3_(Thread& th, Prim* prim)
{
	V fallTime = th.popZIn("lagud3 : fallTime");
	V riseTime = th.popZIn("lagud3 : riseTime");
	V in = th.popZIn("lagud3 : in");

	th.push(new List(new LagUD3(th, in, riseTime, fallTime)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct FirstOrderLPF : public Gen
{
	ZIn _in;
	ZIn _freq;
	Z _x1, _y1;
	Z _freqmul;

	FirstOrderLPF(Thread& th, Arg in, Arg freq)
		: Gen(th, itemTypeZ, mostFinite(in, freq)), _in(in), _freq(freq),
			_x1(0.), _y1(0.), _freqmul(th.rate.invNyquistRate * kFirstOrderCoeffScale)
	{
	}

	virtual const char* TypeName() const override { return "FirstOrderLPF"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z y1 = _y1;
		Z freqmul = _freqmul;
		while (framesToFill) {
			Z *in, *freq;
			int n, inStride, freqStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq)) {
				setDone();
				break;
			}

			if (freqStride == 0) {
				Z a1 = t_firstOrderCoeff(*freq * freqmul);
				Z scale = .5 * (1. - a1);
				for (int i = 0; i < n; ++i) {
					Z x0 = scale * *in;
					Z y0 = x0 + x1 + a1 * y1;

					out[i] = y0;
					y1 = y0;
					x1 = x0;

					in += inStride;
				}

				_in.advance(n);
			} else {
				for (int i = 0; i < n; ++i) {
					Z a1 = t_firstOrderCoeff(*freq * freqmul);
					Z scale = .5 * (1. - a1);

					Z x0 = scale * *in;
					Z y0 = x0 + x1 + a1 * y1;

					out[i] = y0;
					y1 = y0;
					x1 = x0;

					in += inStride;
					freq += freqStride;
				}

				_in.advance(n);
				_freq.advance(n);
			}
			framesToFill -= n;
			out += n;
		}

		_x1 = x1;
		_y1 = y1;
		produce(framesToFill);
	}

};

struct FirstOrderHPF : public Gen
{
	ZIn _in;
	ZIn _freq;
	Z _x1, _y1;
	Z _freqmul;

	FirstOrderHPF(Thread& th, Arg in, Arg freq)
		: Gen(th, itemTypeZ, mostFinite(in, freq)), _in(in), _freq(freq),
			_x1(0.), _y1(0.), _freqmul(th.rate.invNyquistRate * kFirstOrderCoeffScale)
	{
	}

	virtual const char* TypeName() const override { return "FirstOrderHPF"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z y1 = _y1;
		Z freqmul = _freqmul;
		while (framesToFill) {
			Z *in, *freq;
			int n, inStride, freqStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq)) {
				setDone();
				break;
			}

			if (freqStride == 0) {
				Z a1 = t_firstOrderCoeff(*freq * freqmul);
				Z scale = .5 * (1. + a1);
				for (int i = 0; i < n; ++i) {
					Z x0 = scale * *in;
					Z y0 = x0 - x1 + a1 * y1;

					out[i] = y0;
					y1 = y0;
					x1 = x0;

					in += inStride;
				}

				_in.advance(n);
			} else {
				for (int i = 0; i < n; ++i) {
					Z a1 = t_firstOrderCoeff(*freq * freqmul);
					Z scale = .5 * (1. + a1);

					Z x0 = scale * *in;
					Z y0 = x0 - x1 + a1 * y1;

					out[i] = y0;
					y1 = y0;
					x1 = x0;

					in += inStride;
					freq += freqStride;
				}

				_in.advance(n);
				_freq.advance(n);
			}
			framesToFill -= n;
			out += n;
		}

		_x1 = x1;
		_y1 = y1;
		produce(framesToFill);
	}
};

static void lpf1_(Thread& th, Prim* prim)
{
	V freq = th.popZIn("lpf1 : freq");
	V in   = th.popZIn("lpf1 : in");

	th.push(new List(new FirstOrderLPF(th, in, freq)));
}

static void hpf1_(Thread& th, Prim* prim)
{
	V freq = th.popZIn("hpf1 : freq");
	V in   = th.popZIn("hpf1 : in");

	th.push(new List(new FirstOrderHPF(th, in, freq)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct LPF : public Gen
{
	ZIn _in;
	ZIn _freq;
	Z _x1, _x2, _y1, _y2;
	Z _freqmul;
	Z _alphamul;

	LPF(Thread& th, Arg in, Arg freq)
		: Gen(th, itemTypeZ, mostFinite(in, freq)), _in(in), _freq(freq),
			_x1(0.), _x2(0.), _y1(0.), _y2(0.), _freqmul(th.rate.radiansPerSample * gInvSineTableOmega), _alphamul(.5 * M_SQRT2)
	{
	}

	virtual const char* TypeName() const override { return "LPF"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z x2 = _x2;
		Z y1 = _y1;
		Z y2 = _y2;
		Z freqmul = _freqmul;
		Z alphamul = _alphamul;
		while (framesToFill) {
			Z *in, *freq;
			int n, inStride, freqStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq)) {
				setDone();
				break;
			}

			if (freqStride == 0) {
				Z w0 = std::max(1e-3, *freq) * freqmul;
				Z sn, cs;
				tsincosx(w0, sn, cs);
				Z alpha = sn * alphamul;
				Z a0 = 1. + alpha;
				Z a0inv = 1. / a0;
				Z a1 = a0inv * (-2. * cs);
				Z a2 = a0inv * (1. - alpha);
				Z b1 = a0inv * (1. - cs);
				Z b0 = a0inv * (.5 * b1);
				Z b2 = a0inv * b0;
				for (int i = 0; i < n; ++i) {

					Z x0 = *in;
					Z y0 = b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2;

					out[i] = y0;
					y2 = y1;
					y1 = y0;
					x2 = x1;
					x1 = x0;

					in += inStride;
				}

				_in.advance(n);
			} else {
				for (int i = 0; i < n; ++i) {
					Z w0 = std::max(1e-3, *freq) * freqmul;
					Z sn, cs;
					tsincosx(w0, sn, cs);
					Z alpha = sn * alphamul;
					Z a0 = 1. + alpha;
					Z a1 = -2. * cs;
					Z a2 = 1. - alpha;
					Z b1 = 1. - cs;
					Z b0 = .5 * b1;
					Z b2 = b0;

					Z x0 = *in;
					Z y0 = (b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2)/a0;

					out[i] = y0;
					y2 = y1;
					y1 = y0;
					x2 = x1;
					x1 = x0;

					in += inStride;
					freq += freqStride;
				}

				_in.advance(n);
				_freq.advance(n);
			}
			framesToFill -= n;
			out += n;
		}

		_x1 = x1;
		_x2 = x2;
		_y1 = y1;
		_y2 = y2;
		produce(framesToFill);
	}

};

struct LPF2 : public Gen
{
	ZIn _in;
	ZIn _freq;
	Z _x1, _x2, _y1, _y2, _z1, _z2;
	Z _freqmul;
	Z _alphamul;

	LPF2(Thread& th, Arg in, Arg freq)
		: Gen(th, itemTypeZ, mostFinite(in, freq)), _in(in), _freq(freq),
			_x1(0.), _x2(0.), _y1(0.), _y2(0.), _z1(0.), _z2(0.), _freqmul(th.rate.radiansPerSample * gInvSineTableOmega), _alphamul(.5 * M_SQRT2)
	{
	}

	virtual const char* TypeName() const override { return "LPF"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z x2 = _x2;
		Z y1 = _y1;
		Z y2 = _y2;
		Z z1 = _z1;
		Z z2 = _z2;
		Z freqmul = _freqmul;
		Z alphamul = _alphamul;

		while (framesToFill) {
			Z *in, *freq;
			int n, inStride, freqStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq)) {
				setDone();
				break;
			}

			if (freqStride == 0) {
				Z w0 = std::max(1e-3, *freq) * freqmul;
				Z sn, cs;
				tsincosx(w0, sn, cs);
				Z alpha = sn * alphamul;
				Z a0 = 1. + alpha;
				Z a0r = 1./a0;
				Z a1 = -2. * cs;
				Z a2 = 1. - alpha;
				Z b1 = 1. - cs;
				Z b0 = .5 * b1;
				Z b2 = b0;
				for (int i = 0; i < n; ++i) {

					Z x0 = *in;
					Z y0 = (b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2) * a0r;
					Z z0 = (b0 * y0 + b1 * y1 + b2 * y2 - a1 * z1 - a2 * z2) * a0r;

					out[i] = z0;
					z2 = z1;
					z1 = z0;
					y2 = y1;
					y1 = y0;
					x2 = x1;
					x1 = x0;

					in += inStride;
					freq += freqStride;
				}

				framesToFill -= n;
				out += n;
				_in.advance(n);
			} else {
				for (int i = 0; i < n; ++i) {
					Z w0 = std::max(1e-3, *freq) * freqmul;
					Z sn, cs;
					tsincosx(w0, sn, cs);
					Z alpha = sn * alphamul;
					Z a0 = 1. + alpha;
					Z a0r = 1./a0;
					Z a1 = -2. * cs;
					Z a2 = 1. - alpha;
					Z b1 = 1. - cs;
					Z b0 = .5 * b1;
					Z b2 = b0;

					Z x0 = *in;
					Z y0 = (b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2) * a0r;
					Z z0 = (b0 * y0 + b1 * y1 + b2 * y2 - a1 * z1 - a2 * z2) * a0r;

					out[i] = z0;
					z2 = z1;
					z1 = z0;
					y2 = y1;
					y1 = y0;
					x2 = x1;
					x1 = x0;

					in += inStride;
					freq += freqStride;
				}

				framesToFill -= n;
				out += n;
				_in.advance(n);
				_freq.advance(n);
			}
		}
		_x1 = x1;
		_x2 = x2;
		_y1 = y1;
		_y2 = y2;
		_z1 = z1;
		_z2 = z2;
		produce(framesToFill);
	}

};

struct HPF : public Gen
{
	ZIn _in;
	ZIn _freq;
	Z _x1, _x2, _y1, _y2;
	Z _freqmul;
	Z _alphamul;

	HPF(Thread& th, Arg in, Arg freq)
		: Gen(th, itemTypeZ, mostFinite(in, freq)), _in(in), _freq(freq),
			_x1(0.), _x2(0.), _y1(0.), _y2(0.), _freqmul(th.rate.radiansPerSample * gInvSineTableOmega), _alphamul(.5 * M_SQRT2)
	{
	}

	virtual const char* TypeName() const override { return "HPF"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z x2 = _x2;
		Z y1 = _y1;
		Z y2 = _y2;
		Z freqmul = _freqmul;
		Z alphamul = _alphamul;
		while (framesToFill) {
			Z *in, *freq;
			int n, inStride, freqStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq)) {
				setDone();
				break;
			}

			if (freqStride == 0) {
				Z w0 = *freq * freqmul;
				Z sn, cs;
				tsincosx(w0, sn, cs);
				Z alpha = sn * alphamul;
				Z a0 = 1. + alpha;
				Z a1 = -2. * cs;
				Z a2 = 1. - alpha;
				Z b1 = -1. - cs;
				Z b0 = -.5 * b1;
				Z b2 = b0;
				for (int i = 0; i < n; ++i) {

					Z x0 = *in;
					Z y0 = (b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2) / a0;

					out[i] = y0;
					y2 = y1;
					y1 = y0;
					x2 = x1;
					x1 = x0;

					in += inStride;
					freq += freqStride;
				}
				framesToFill -= n;
				out += n;
				_in.advance(n);
			} else {
				for (int i = 0; i < n; ++i) {
					Z w0 = *freq * freqmul;
					Z sn, cs;
					tsincosx(w0, sn, cs);
					Z alpha = sn * alphamul;
					Z a0 = 1. + alpha;
					Z a1 = -2. * cs;
					Z a2 = 1. - alpha;
					Z b1 = -1. - cs;
					Z b0 = -.5 * b1;
					Z b2 = b0;

					Z x0 = *in;
					Z y0 = (b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2) / a0;

					out[i] = y0;
					y2 = y1;
					y1 = y0;
					x2 = x1;
					x1 = x0;

					in += inStride;
					freq += freqStride;
				}
				framesToFill -= n;
				out += n;
				_in.advance(n);
				_freq.advance(n);
			}
		}

		_x1 = x1;
		_x2 = x2;
		_y1 = y1;
		_y2 = y2;
		produce(framesToFill);
	}

};

struct HPF2 : public Gen
{
	ZIn _in;
	ZIn _freq;
	Z _x1, _x2, _y1, _y2, _z1, _z2;
	Z _freqmul;
	Z _alphamul;

	HPF2(Thread& th, Arg in, Arg freq)
		: Gen(th, itemTypeZ, mostFinite(in, freq)), _in(in), _freq(freq),
			_x1(0.), _x2(0.), _y1(0.), _y2(0.), _z1(0.), _z2(0.), _freqmul(th.rate.radiansPerSample * gInvSineTableOmega), _alphamul(.5 * M_SQRT2)
	{
	}

	virtual const char* TypeName() const override { return "HPF2"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z x2 = _x2;
		Z y1 = _y1;
		Z y2 = _y2;
		Z z1 = _z1;
		Z z2 = _z2;
		Z freqmul = _freqmul;
		Z alphamul = _alphamul;
		while (framesToFill) {
			Z *in, *freq;
			int n, inStride, freqStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq)) {
				setDone();
				break;
			}

			if (freqStride == 0) {
				Z w0 = *freq * freqmul;
				Z sn, cs;
				tsincosx(w0, sn, cs);
				Z alpha = sn * alphamul;
				Z a0 = 1. + alpha;
				Z a0r = 1./a0;
				Z a1 = -2. * cs;
				Z a2 = 1. - alpha;
				Z b1 = -1. - cs;
				Z b0 = -.5 * b1;
				Z b2 = b0;
				for (int i = 0; i < n; ++i) {

					Z x0 = *in;
					Z y0 = (b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2) * a0r;
					Z z0 = (b0 * y0 + b1 * y1 + b2 * y2 - a1 * z1 - a2 * z2) * a0r;

					out[i] = z0;
					z2 = z1;
					z1 = z0;
					y2 = y1;
					y1 = y0;
					x2 = x1;
					x1 = x0;

					in += inStride;
					freq += freqStride;
				}

				framesToFill -= n;
				out += n;
				_in.advance(n);
			} else {
				for (int i = 0; i < n; ++i) {
					Z w0 = *freq * freqmul;
					Z sn, cs;
					tsincosx(w0, sn, cs);
					Z alpha = sn * alphamul;
					Z a0 = 1. + alpha;
					Z a0r = 1./a0;
					Z a1 = -2. * cs;
					Z a2 = 1. - alpha;
					Z b1 = -1. - cs;
					Z b0 = -.5 * b1;
					Z b2 = b0;

					Z x0 = *in;
					Z y0 = (b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2) * a0r;
					Z z0 = (b0 * y0 + b1 * y1 + b2 * y2 - a1 * z1 - a2 * z2) * a0r;

					out[i] = z0;
					z2 = z1;
					z1 = z0;
					y2 = y1;
					y1 = y0;
					x2 = x1;
					x1 = x0;

					in += inStride;
					freq += freqStride;
				}

				framesToFill -= n;
				out += n;
				_in.advance(n);
				_freq.advance(n);
			}
		}

		_x1 = x1;
		_x2 = x2;
		_y1 = y1;
		_y2 = y2;
		_z1 = z1;
		_z2 = z2;
		produce(framesToFill);
	}

};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

template <typename Feedback>
struct RLPF : public Gen
{
	ZIn _in;
	ZIn _freq;
	ZIn _rq;
	Z _x1, _x2, _y1, _y2;
	Z _freqmul;

	RLPF(Thread& th, Arg in, Arg freq, Arg rq)
		: Gen(th, itemTypeZ, mostFinite(in, freq, rq)), _in(in), _freq(freq), _rq(rq),
			_x1(0.), _x2(0.), _y1(0.), _y2(0.), _freqmul(th.rate.radiansPerSample * gInvSineTableOmega)
	{
	}

	virtual const char* TypeName() const override { return "RLPF"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z x2 = _x2;
		Z y1 = _y1;
		Z y2 = _y2;
		Z freqmul = _freqmul;
		while (framesToFill) {
			Z *in, *freq, *rq;
			int n, inStride, freqStride, rqStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq) || _rq(th, n, rqStride, rq)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				Z w0 = *freq * freqmul;
				Z sn, cs;
				tsincosx(w0, sn, cs);
				Z alpha = sn * *rq * .5;
				Z a0 = 1. + alpha;
				Z a1 = -2. * cs;
				Z a2 = 1. - alpha;
				Z b1 = 1. - cs;
				Z b0 = .5 * b1;
				Z b2 = b0;

				Z x0 = *in;
				Z y0 = (b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2)/a0;
				y0 = Feedback::feedback(y0);

				out[i] = y0;
				y2 = y1;
				y1 = y0;
				x2 = x1;
				x1 = x0;

				in += inStride;
				freq += freqStride;
				rq += rqStride;
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_freq.advance(n);
			_rq.advance(n);
		}

		_x1 = x1;
		_x2 = x2;
		_y1 = y1;
		_y2 = y2;
		produce(framesToFill);
	}

};

template <typename Feedback>
struct RLPF2 : public Gen
{
	ZIn _in;
	ZIn _freq;
	ZIn _rq;
	Z _x1, _x2, _y1, _y2, _z1, _z2;
	Z _freqmul;

	RLPF2(Thread& th, Arg in, Arg freq, Arg rq)
		: Gen(th, itemTypeZ, mostFinite(in, freq, rq)), _in(in), _freq(freq), _rq(rq),
			_x1(0.), _x2(0.), _y1(0.), _y2(0.), _z1(0.), _z2(0.), _freqmul(th.rate.radiansPerSample * gInvSineTableOmega)
	{
	}

	virtual const char* TypeName() const override { return "RLPF"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z x2 = _x2;
		Z y1 = _y1;
		Z y2 = _y2;
		Z z1 = _z1;
		Z z2 = _z2;
		Z freqmul = _freqmul;
		while (framesToFill) {
			Z *in, *freq, *rq;
			int n, inStride, freqStride, rqStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq) || _rq(th, n, rqStride, rq)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				Z w0 = *freq * freqmul;
				Z sn, cs;
				tsincosx(w0, sn, cs);
				Z alpha = sn * *rq * .5;
				Z a0 = 1. + alpha;
				Z a0r = 1./a0;
				Z a1 = -2. * cs;
				Z a2 = 1. - alpha;
				Z b1 = 1. - cs;
				Z b0 = .5 * b1;
				Z b2 = b0;

				Z x0 = *in;
				Z y0 = (b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2) * a0r;
				y0 = Feedback::feedback(y0);
				Z z0 = (b0 * y0 + b1 * y1 + b2 * y2 - a1 * z1 - a2 * z2) * a0r;
				z0 = Feedback::feedback(z0);

				out[i] = z0;
				z2 = z1;
				z1 = z0;
				y2 = y1;
				y1 = y0;
				x2 = x1;
				x1 = x0;

				in += inStride;
				freq += freqStride;
				rq += rqStride;
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_freq.advance(n);
			_rq.advance(n);
		}

		_x1 = x1;
		_x2 = x2;
		_y1 = y1;
		_y2 = y2;
		_z1 = z1;
		_z2 = z2;
		produce(framesToFill);
	}

};

template <typename Feedback>
struct RHPF : public Gen
{
	ZIn _in;
	ZIn _freq;
	ZIn _rq;
	Z _x1, _x2, _y1, _y2;
	Z _freqmul;

	RHPF(Thread& th, Arg in, Arg freq, Arg rq)
		: Gen(th, itemTypeZ, mostFinite(in, freq, rq)), _in(in), _freq(freq), _rq(rq),
			_x1(0.), _x2(0.), _y1(0.), _y2(0.), _freqmul(th.rate.radiansPerSample * gInvSineTableOmega)
	{
	}

	virtual const char* TypeName() const override { return "RHPF"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z x2 = _x2;
		Z y1 = _y1;
		Z y2 = _y2;
		Z freqmul = _freqmul;
		while (framesToFill) {
			Z *in, *freq, *rq;
			int n, inStride, freqStride, rqStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq) || _rq(th, n, rqStride, rq)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				Z w0 = *freq * freqmul;
				Z sn, cs;
				tsincosx(w0, sn, cs);
				Z alpha = sn * *rq * .5;
				Z a0 = 1. + alpha;
				Z a1 = -2. * cs;
				Z a2 = 1. - alpha;
				Z b1 = -1. - cs;
				Z b0 = -.5 * b1;
				Z b2 = b0;

				Z x0 = *in;
				Z y0 = (b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2)/a0;
				y0 = Feedback::feedback(y0);

				out[i] = y0;
				y2 = y1;
				y1 = y0;
				x2 = x1;
				x1 = x0;

				in += inStride;
				freq += freqStride;
				rq += rqStride;
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_freq.advance(n);
			_rq.advance(n);
		}

		_x1 = x1;
		_x2 = x2;
		_y1 = y1;
		_y2 = y2;
		produce(framesToFill);
	}

};

template <typename Feedback>
struct RHPF2 : public Gen
{
	ZIn _in;
	ZIn _freq;
	ZIn _rq;
	Z _x1, _x2, _y1, _y2, _z1, _z2;
	Z _freqmul;

	RHPF2(Thread& th, Arg in, Arg freq, Arg rq)
		: Gen(th, itemTypeZ, mostFinite(in, freq, rq)), _in(in), _freq(freq), _rq(rq),
			_x1(0.), _x2(0.), _y1(0.), _y2(0.), _z1(0.), _z2(0.), _freqmul(th.rate.radiansPerSample * gInvSineTableOmega)
	{
	}

	virtual const char* TypeName() const override { return "RHPF2"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z x2 = _x2;
		Z y1 = _y1;
		Z y2 = _y2;
		Z z1 = _z1;
		Z z2 = _z2;
		Z freqmul = _freqmul;
		while (framesToFill) {
			Z *in, *freq, *rq;
			int n, inStride, freqStride, rqStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq) || _rq(th, n, rqStride, rq)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				Z w0 = *freq * freqmul;
				Z sn, cs;
				tsincosx(w0, sn, cs);
				Z alpha = sn * *rq * .5;
				Z a0 = 1. + alpha;
				Z a0r = 1./a0;
				Z a1 = -2. * cs;
				Z a2 = 1. - alpha;
				Z b1 = -1. - cs;
				Z b0 = -.5 * b1;
				Z b2 = b0;

				Z x0 = *in;
				Z y0 = (b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2) * a0r;
				y0 = Feedback::feedback(y0);
				Z z0 = (b0 * y0 + b1 * y1 + b2 * y2 - a1 * z1 - a2 * z2) * a0r;
				z0 = Feedback::feedback(z0);

				out[i] = z0;
				z2 = z1;
				z1 = z0;
				y2 = y1;
				y1 = y0;
				x2 = x1;
				x1 = x0;

				in += inStride;
				freq += freqStride;
				rq += rqStride;
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_freq.advance(n);
			_rq.advance(n);
		}

		_x1 = x1;
		_x2 = x2;
		_y1 = y1;
		_y2 = y2;
		_z1 = z1;
		_z2 = z2;
		produce(framesToFill);
	}

};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct BPF : public Gen
{
	ZIn _in;
	ZIn _freq;
	ZIn _bw;
	Z _x1, _x2, _y1, _y2;
	Z _freqmul;

	BPF(Thread& th, Arg in, Arg freq, Arg bw)
		: Gen(th, itemTypeZ, mostFinite(in, freq, bw)), _in(in), _freq(freq), _bw(bw),
			_x1(0.), _x2(0.), _y1(0.), _y2(0.), _freqmul(th.rate.radiansPerSample * gInvSineTableOmega)
	{
	}

	virtual const char* TypeName() const override { return "BPF"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z x2 = _x2;
		Z y1 = _y1;
		Z y2 = _y2;
		Z freqmul = _freqmul;
		while (framesToFill) {
			Z *in, *freq, *bw;
			int n, inStride, freqStride, bwStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq) || _bw(th, n, bwStride, bw)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				Z w0 = *freq * freqmul;
				Z sn, cs;
				tsincosx(w0, sn, cs);

				// bw * log(2) is the first term of the taylor series for 2*sinh(log(2)/2 * bw) == 1/Q.
				// the log(2) is combined with the .5 term in the formula for alpha.
				Z alpha = sn * *bw * log2o2;

				Z a0 = 1. + alpha;
				Z a1 = -2. * cs;
				Z a2 = 1. - alpha;
				Z b0 = alpha;

				Z x0 = *in;
				Z y0 = (b0 * (x0 - x2) - a1 * y1 - a2 * y2) / a0;

				out[i] = y0;
				y2 = y1;
				y1 = y0;
				x2 = x1;
				x1 = x0;

				in += inStride;
				freq += freqStride;
				bw += bwStride;
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_freq.advance(n);
			_bw.advance(n);
		}

		_x1 = x1;
		_x2 = x2;
		_y1 = y1;
		_y2 = y2;
		produce(framesToFill);
	}

};

struct BSF : public Gen
{
	ZIn _in;
	ZIn _freq;
	ZIn _bw;
	Z _x1, _x2, _y1, _y2;
	Z _freqmul;

	BSF(Thread& th, Arg in, Arg freq, Arg bw)
		: Gen(th, itemTypeZ, mostFinite(in, freq, bw)), _in(in), _freq(freq), _bw(bw),
			_x1(0.), _x2(0.), _y1(0.), _y2(0.), _freqmul(th.rate.radiansPerSample * gInvSineTableOmega)
	{
	}

	virtual const char* TypeName() const override { return "BSF"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z x2 = _x2;
		Z y1 = _y1;
		Z y2 = _y2;
		Z freqmul = _freqmul;
		while (framesToFill) {
			Z *in, *freq, *bw;
			int n, inStride, freqStride, bwStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq) || _bw(th, n, bwStride, bw)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				Z w0 = *freq * freqmul;
				Z sn, cs;
				tsincosx(w0, sn, cs);

				// bw * log(2) is the first term of the taylor series for 2*sinh(log(2)/2 * bw) == 1/Q.
				// the log(2) is combined with the .5 term in the formula for alpha.
				Z alpha = sn * *bw * log2o2;

				Z a0 = 1. + alpha;
				Z a0r = 1./a0;
				Z a1 = -2. * cs;
				Z a2 = 1. - alpha;

				Z x0 = *in;
				Z y0 = (x0 + x2 + a1 * (x1 - y1) - a2 * y2) * a0r;

				out[i] = y0;
				y2 = y1;
				y1 = y0;
				x2 = x1;
				x1 = x0;

				in += inStride;
				freq += freqStride;
				bw += bwStride;
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_freq.advance(n);
			_bw.advance(n);
		}

		_x1 = x1;
		_x2 = x2;
		_y1 = y1;
		_y2 = y2;
		produce(framesToFill);
	}

};

struct APF : public Gen
{
	ZIn _in;
	ZIn _freq;
	ZIn _bw;
	Z _x1, _x2, _y1, _y2;
	Z _freqmul;

	APF(Thread& th, Arg in, Arg freq, Arg bw)
		: Gen(th, itemTypeZ, mostFinite(in, freq, bw)), _in(in), _freq(freq), _bw(bw),
			_x1(0.), _x2(0.), _y1(0.), _y2(0.), _freqmul(th.rate.radiansPerSample * gInvSineTableOmega)
	{
	}

	virtual const char* TypeName() const override { return "APF"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z x2 = _x2;
		Z y1 = _y1;
		Z y2 = _y2;
		Z freqmul = _freqmul;
		while (framesToFill) {
			Z *in, *freq, *bw;
			int n, inStride, freqStride, bwStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq) || _bw(th, n, bwStride, bw)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				Z w0 = *freq * freqmul;
				Z sn, cs;
				tsincosx(w0, sn, cs);

				// bw * log(2) is the first term of the taylor series for 2*sinh(log(2)/2 * bw) == 1/Q.
				// the log(2) is combined with the .5 term in the formula for alpha.
				Z alpha = sn * *bw * log2o2;

				Z a0 = 1. + alpha;
				Z a0r = 1./a0;
				Z a1 = -2. * cs;
				Z a2 = 1. - alpha;

				Z x0 = *in;
				Z y0 = (a2 * (x0 - y2) + a1 * (x1 - y1)) * a0r + x2;

				out[i] = y0;
				y2 = y1;
				y1 = y0;
				x2 = x1;
				x1 = x0;

				in += inStride;
				freq += freqStride;
				bw += bwStride;
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_freq.advance(n);
			_bw.advance(n);
		}

		_x1 = x1;
		_x2 = x2;
		_y1 = y1;
		_y2 = y2;
		produce(framesToFill);
	}

};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct PEQ : public Gen
{
	ZIn _in;
	ZIn _freq;
	ZIn _bw;
	ZIn _gain;
	Z _x1, _x2, _y1, _y2;
	Z _freqmul;

	PEQ(Thread& th, Arg in, Arg freq, Arg bw, Arg gain)
		: Gen(th, itemTypeZ, mostFinite(in, freq, bw, gain)), _in(in), _freq(freq), _bw(bw), _gain(gain),
			_x1(0.), _x2(0.), _y1(0.), _y2(0.), _freqmul(th.rate.radiansPerSample * gInvSineTableOmega)
	{
	}

	virtual const char* TypeName() const override { return "PEQ"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z x2 = _x2;
		Z y1 = _y1;
		Z y2 = _y2;
		Z freqmul = _freqmul;
		while (framesToFill) {
			Z *in, *freq, *bw, *gain;
			int n, inStride, freqStride, bwStride, gainStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq) || _bw(th, n, bwStride, bw) || _gain(th, n, gainStride, gain)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				Z A = t_dbamp(.5 * *gain);
				Z w0 = *freq * freqmul;
				Z sn, cs;
				tsincosx(w0, sn, cs);

				// bw * log(2) is the first term of the taylor series for 2*sinh(log(2)/2 * bw) == 1/Q.
				// the log(2) is combined with the .5 term in the formula for alpha.
				Z alpha = sn * *bw * log2o2;
				Z alphaA = alpha * A;
				Z alphaOverA = alpha / A;

				Z a0 = 1. + alphaOverA;
				Z a1 = -2. * cs;
				Z a2 = 1. - alphaOverA;

				Z b0 = 1. + alphaA;
				Z b2 = 1. - alphaA;

				Z x0 = *in;
				Z y0 = (b0 * x0 + a1 * (x1 - y1) + b2 * x2 - a2 * y2) / a0;

				out[i] = y0;
				y2 = y1;
				y1 = y0;
				x2 = x1;
				x1 = x0;

				in += inStride;
				freq += freqStride;
				bw += bwStride;
				gain += gainStride;
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_freq.advance(n);
			_bw.advance(n);
			_gain.advance(n);
		}

		_x1 = x1;
		_x2 = x2;
		_y1 = y1;
		_y2 = y2;
		produce(framesToFill);
	}

};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct LowShelf : public Gen
{
	ZIn _in;
	ZIn _freq;
	ZIn _gain;
	Z _x1, _x2, _y1, _y2;
	Z _freqmul;
	Z _alphamul;

	LowShelf(Thread& th, Arg in, Arg freq, Arg gain)
		: Gen(th, itemTypeZ, mostFinite(in, freq, gain)), _in(in), _freq(freq), _gain(gain),
			_x1(0.), _x2(0.), _y1(0.), _y2(0.), _freqmul(th.rate.radiansPerSample * gInvSineTableOmega), _alphamul(.5 * M_SQRT2)
	{
	}

	virtual const char* TypeName() const override { return "LowShelf"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z x2 = _x2;
		Z y1 = _y1;
		Z y2 = _y2;
		Z freqmul = _freqmul;
		Z alphamul = _alphamul;
		while (framesToFill) {
			Z *in, *freq, *gain;
			int n, inStride, freqStride, gainStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq) || _gain(th, n, gainStride, gain)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				Z A = t_dbamp(.5 * *gain);
				Z Ap1 = A + 1.;
				Z Am1 = A - 1.;
				Z Asqrt = t_dbamp(.25 * *gain);
				Z w0 = *freq * freqmul;
				Z sn, cs;
				tsincosx(w0, sn, cs);
				Z alpha = sn * alphamul;
				Z alpha2Asqrt = 2. * alpha * Asqrt;
				Z Am1cs = Am1*cs;
				Z Ap1cs = Ap1*cs;

				Z b0 =       ( Ap1 - Am1cs + alpha2Asqrt );
				Z b1 =    2.*( Am1 - Ap1cs               );
				Z b2 =       ( Ap1 - Am1cs - alpha2Asqrt );
				Z a0 =         Ap1 + Am1cs + alpha2Asqrt;
				Z a1 =   -2.*( Am1 + Ap1cs               );
				Z a2 =         Ap1 + Am1cs - alpha2Asqrt;

				Z x0 = *in;
				Z y0 = (A*(b0 * x0 + b1 * x1 + b2 * x2) - a1 * y1 - a2 * y2) / a0;

				out[i] = y0;
				y2 = y1;
				y1 = y0;
				x2 = x1;
				x1 = x0;

				in += inStride;
				freq += freqStride;
				gain += gainStride;
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_freq.advance(n);
			_gain.advance(n);
		}

		_x1 = x1;
		_x2 = x2;
		_y1 = y1;
		_y2 = y2;
		produce(framesToFill);
	}

};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct HighShelf : public Gen
{
	ZIn _in;
	ZIn _freq;
	ZIn _gain;
	Z _x1, _x2, _y1, _y2;
	Z _freqmul;
	Z _alphamul;

	HighShelf(Thread& th, Arg in, Arg freq, Arg gain)
		: Gen(th, itemTypeZ, mostFinite(in, freq, gain)), _in(in), _freq(freq), _gain(gain),
			_x1(0.), _x2(0.), _y1(0.), _y2(0.), _freqmul(th.rate.radiansPerSample * gInvSineTableOmega), _alphamul(.5 * M_SQRT2)
	{
	}

	virtual const char* TypeName() const override { return "HighShelf"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z x2 = _x2;
		Z y1 = _y1;
		Z y2 = _y2;
		Z freqmul = _freqmul;
		Z alphamul = _alphamul;
		while (framesToFill) {
			Z *in, *freq, *gain;
			int n, inStride, freqStride, gainStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq) || _gain(th, n, gainStride, gain)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				Z A = t_dbamp(.5 * *gain);
				Z Ap1 = A + 1.;
				Z Am1 = A - 1.;
				Z Asqrt = t_dbamp(.25 * *gain);
				Z w0 = *freq * freqmul;
				Z sn, cs;
				tsincosx(w0, sn, cs);
				Z alpha = sn * alphamul;
				Z alpha2Asqrt = 2. * alpha * Asqrt;
				Z Am1cs = Am1*cs;
				Z Ap1cs = Ap1*cs;

				Z b0 =       ( Ap1 + Am1cs + alpha2Asqrt );
				Z b1 =   -2.*( Am1 + Ap1cs               );
				Z b2 =       ( Ap1 + Am1cs - alpha2Asqrt );
				Z a0 =         Ap1 - Am1cs + alpha2Asqrt;
				Z a1 =    2.*( Am1 - Ap1cs               );
				Z a2 =         Ap1 - Am1cs - alpha2Asqrt;

				Z x0 = *in;
				Z y0 = (A*(b0 * x0 + b1 * x1 + b2 * x2) - a1 * y1 - a2 * y2) / a0;

				out[i] = y0;
				y2 = y1;
				y1 = y0;
				x2 = x1;
				x1 = x0;

				in += inStride;
				freq += freqStride;
				gain += gainStride;
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_freq.advance(n);
			_gain.advance(n);
		}

		_x1 = x1;
		_x2 = x2;
		_y1 = y1;
		_y2 = y2;
		produce(framesToFill);
	}

};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct LowShelf1 : public Gen
{
	ZIn _in;
	ZIn _freq;
	ZIn _gain;
	Z _x1, _y1;
	Z _freqmul;

	LowShelf1(Thread& th, Arg in, Arg freq, Arg gain)
		: Gen(th, itemTypeZ, mostFinite(in, freq, gain)), _in(in), _freq(freq), _gain(gain),
			_x1(0.), _y1(0.), _freqmul(th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "LowShelf1"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z y1 = _y1;
		Z freqmul = _freqmul;
		while (framesToFill) {
			Z *in, *freq, *gain;
			int n, inStride, freqStride, gainStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq) || _gain(th, n, gainStride, gain)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				Z sqrt_g = t_dbamp(.25 * *gain);
				Z d = *freq * freqmul;

				Z p = 1. - d*sqrt_g;
				Z q = 1. - d/sqrt_g;

					Z x0 = *in;
					Z y0 = x0 + q * x1 - p * y1;

					out[i] = y0;
					y1 = y0;
					x1 = x0;

				in += inStride;
				freq += freqStride;
				gain += gainStride;
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_freq.advance(n);
			_gain.advance(n);
		}

		_x1 = x1;
		_y1 = y1;
		produce(framesToFill);
	}

};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct RLowShelf : public Gen
{
	ZIn _in;
	ZIn _freq;
	ZIn _bw;
	ZIn _gain;
	Z _x1, _x2, _y1, _y2;
	Z _freqmul;

	RLowShelf(Thread& th, Arg in, Arg freq, Arg bw, Arg gain)
		: Gen(th, itemTypeZ, mostFinite(in, freq, gain)), _in(in), _freq(freq), _bw(bw), _gain(gain),
			_x1(0.), _x2(0.), _y1(0.), _y2(0.), _freqmul(th.rate.radiansPerSample * gInvSineTableOmega)
	{
	}

	virtual const char* TypeName() const override { return "RLowShelf"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z x2 = _x2;
		Z y1 = _y1;
		Z y2 = _y2;
		Z freqmul = _freqmul;
		while (framesToFill) {
			Z *in, *freq, *bw, *gain;
			int n, inStride, freqStride, bwStride, gainStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq) || _bw(th, n, bwStride, bw) || _gain(th, n, gainStride, gain)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				Z A = t_dbamp(.5 * *gain);
				Z Ap1 = A + 1.;
				Z Am1 = A - 1.;
				Z Asqrt = t_dbamp(.25 * *gain);
				Z w0 = *freq * freqmul;
				Z sn, cs;
				tsincosx(w0, sn, cs);

				// bw * log(2) is the first term of the taylor series for 2*sinh(log(2)/2 * bw) == 1/Q.
				// the log(2) is combined with the .5 term in the formula for alpha.
				Z alpha = sn * *bw * log2o2;
				Z alpha2Asqrt = 2. * alpha * Asqrt;
				Z Am1cs = Am1*cs;
				Z Ap1cs = Ap1*cs;

				Z b0 =     A*( Ap1 - Am1cs + alpha2Asqrt );
				Z b1 =  2.*A*( Am1 - Ap1cs               );
				Z b2 =     A*( Ap1 - Am1cs - alpha2Asqrt );
				Z a0 =         Ap1 + Am1cs + alpha2Asqrt;
				Z a1 =   -2.*( Am1 + Ap1cs               );
				Z a2 =         Ap1 + Am1cs - alpha2Asqrt;

				Z x0 = *in;
				Z y0 = (b0 * x0 + b1 * x1 + b2 * x2 - a1 * y1 - a2 * y2) / a0;

				out[i] = y0;
				y2 = y1;
				y1 = y0;
				x2 = x1;
				x1 = x0;

				in += inStride;
				freq += freqStride;
				bw += bwStride;
				gain += gainStride;
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_freq.advance(n);
			_bw.advance(n);
			_gain.advance(n);
		}

		_x1 = x1;
		_x2 = x2;
		_y1 = y1;
		_y2 = y2;
		produce(framesToFill);
	}

};

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

static void lpf_(Thread& th, Prim* prim)
{
	V freq = th.popZIn("lpf : freq");
	V in   = th.popZIn("lpf : in");

	th.push(new List(new LPF(th, in, freq)));
}

static void lpf2_(Thread& th, Prim* prim)
{
	V freq = th.popZIn("lpf2 : freq");
	V in   = th.popZIn("lpf2 : in");

	th.push(new List(new LPF2(th, in, freq)));
}

static void hpf_(Thread& th, Prim* prim)
{
	V freq = th.popZIn("hpf : freq");
	V in   = th.popZIn("hpf : in");

	th.push(new List(new HPF(th, in, freq)));
}

static void hpf2_(Thread& th, Prim* prim)
{
	V freq = th.popZIn("hpf2 : freq");
	V in   = th.popZIn("hpf2 : in");

	th.push(new List(new HPF2(th, in, freq)));
}

static void rlpf_(Thread& th, Prim* prim)
{
	V rq    = th.popZIn("rlpf : rq");
	V freq  = th.popZIn("rlpf : freq");
	V in    = th.popZIn("rlpf : in");

	th.push(new List(new RLPF<NormalFeedback>(th, in, freq, rq)));
}

static void rlpf2_(Thread& th, Prim* prim)
{
	V rq    = th.popZIn("rlpf2 : rq");
	V freq  = th.popZIn("rlpf2 : freq");
	V in    = th.popZIn("rlpf2 : in");

	th.push(new List(new RLPF2<NormalFeedback>(th, in, freq, rq)));
}

static void rhpf_(Thread& th, Prim* prim)
{
	V rq    = th.popZIn("rhpf : rq");
	V freq  = th.popZIn("rhpf : freq");
	V in    = th.popZIn("rhpf : in");

	th.push(new List(new RHPF<NormalFeedback>(th, in, freq, rq)));
}

static void rhpf2_(Thread& th, Prim* prim)
{
	V rq    = th.popZIn("rhpf2 : rq");
	V freq  = th.popZIn("rhpf2 : freq");
	V in    = th.popZIn("rhpf2 : in");

	th.push(new List(new RHPF2<NormalFeedback>(th, in, freq, rq)));
}

static void rlpfc_(Thread& th, Prim* prim)
{
	V rq    = th.popZIn("rlpfc : rq");
	V freq  = th.popZIn("rlpfc : freq");
	V in    = th.popZIn("rlpfc : in");

	th.push(new List(new RLPF<UnityHardClipFeedback>(th, in, freq, rq)));
}

static void rlpf2c_(Thread& th, Prim* prim)
{
	V rq    = th.popZIn("rlpf2c : rq");
	V freq  = th.popZIn("rlpf2c : freq");
	V in    = th.popZIn("rlpf2c : in");

	th.push(new List(new RLPF2<UnityHardClipFeedback>(th, in, freq, rq)));
}

static void rhpfc_(Thread& th, Prim* prim)
{
	V rq    = th.popZIn("rhpfc : rq");
	V freq  = th.popZIn("rhpfc : freq");
	V in    = th.popZIn("rhpfc : in");

	th.push(new List(new RHPF<UnityHardClipFeedback>(th, in, freq, rq)));
}

static void rhpf2c_(Thread& th, Prim* prim)
{
	V rq    = th.popZIn("rhpf2c : rq");
	V freq  = th.popZIn("rhpf2c : freq");
	V in    = th.popZIn("rhpf2c : in");

	th.push(new List(new RHPF2<UnityHardClipFeedback>(th, in, freq, rq)));
}

static void bpf_(Thread& th, Prim* prim)
{
	V bw   = th.popZIn("bpf : bw");
	V freq = th.popZIn("bpf : freq");
	V in   = th.popZIn("bpf : in");

	th.push(new List(new BPF(th, in, freq, bw)));
}

static void bsf_(Thread& th, Prim* prim)
{
	V bw   = th.popZIn("bsf : bw");
	V freq = th.popZIn("bsf : freq");
	V in   = th.popZIn("bsf : in");

	th.push(new List(new BSF(th, in, freq, bw)));
}

static void apf_(Thread& th, Prim* prim)
{
	V bw   = th.popZIn("apf : bw");
	V freq = th.popZIn("apf : freq");
	V in   = th.popZIn("apf : in");

	th.push(new List(new APF(th, in, freq, bw)));
}

static void peq_(Thread& th, Prim* prim)
{
	V gain   = th.popZIn("peq : gain");
	V bw   = th.popZIn("peq : bw");
	V freq = th.popZIn("peq : freq");
	V in   = th.popZIn("peq : in");

	th.push(new List(new PEQ(th, in, freq, bw, gain)));
}

static void lsf_(Thread& th, Prim* prim)
{
	V gain   = th.popZIn("lsf : gain");
	V freq = th.popZIn("lsf : freq");
	V in   = th.popZIn("lsf : in");

	th.push(new List(new LowShelf(th, in, freq, gain)));
}

static void hsf_(Thread& th, Prim* prim)
{
	V gain   = th.popZIn("hsf : gain");
	V freq = th.popZIn("hsf : freq");
	V in   = th.popZIn("hsf : in");

	th.push(new List(new HighShelf(th, in, freq, gain)));
}

static void lsf1_(Thread& th, Prim* prim)
{
	V gain   = th.popZIn("lsf : gain");
	V freq = th.popZIn("lsf : freq");
	V in   = th.popZIn("lsf : in");

	th.push(new List(new LowShelf1(th, in, freq, gain)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Resonz : public Gen
{
	ZIn _in;
	ZIn _freq;
	ZIn _rq;
	Z _x1, _x2, _y1, _y2;
	Z _freqmul;

	Resonz(Thread& th, Arg in, Arg freq, Arg rq)
		: Gen(th, itemTypeZ, mostFinite(in, freq, rq)), _in(in), _freq(freq), _rq(rq),
			_x1(0.), _x2(0.), _y1(0.), _y2(0.), _freqmul(th.rate.radiansPerSample)
	{
	}

	virtual const char* TypeName() const override { return "Resonz"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z x2 = _x2;
		Z y1 = _y1;
		Z y2 = _y2;
		Z freqmul = _freqmul;
		while (framesToFill) {
			Z *in, *freq, *rq;
			int n, inStride, freqStride, rqStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq) || _rq(th, n, rqStride, rq)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				Z w0 = *freq * freqmul;
				Z R = 1. - .5 * w0 * *rq;
				Z cs = tcos(w0);
				Z a1 = 2. * R * cs;
				Z a2 = -(R * R);
				Z b0 = .5;

				Z x0 = *in;
				Z y0 = b0 * (x0 - x2) + a1 * y1 + a2 * y2;

				out[i] = y0;
				y2 = y1;
				y1 = y0;
				x2 = x1;
				x1 = x0;

				in += inStride;
				freq += freqStride;
				rq += rqStride;
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_freq.advance(n);
			_rq.advance(n);
		}

		_x1 = x1;
		_x2 = x2;
		_y1 = y1;
		_y2 = y2;
		produce(framesToFill);
	}
};

struct Ringz : public Gen
{
	ZIn _in;
	ZIn _freq;
	ZIn _ringTime;
	Z _x1, _x2, _y1, _y2;
	Z _freqmul, _K;

	Ringz(Thread& th, Arg in, Arg freq, Arg ringTime)
		: Gen(th, itemTypeZ, mostFinite(in, freq, ringTime)), _in(in), _freq(freq), _ringTime(ringTime),
			_x1(0.), _x2(0.), _y1(0.), _y2(0.), _freqmul(th.rate.radiansPerSample),
			_K(log001 * th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "Ringz"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z x2 = _x2;
		Z y1 = _y1;
		Z y2 = _y2;
		Z freqmul = _freqmul;
		Z K = _K;

		while (framesToFill) {
			Z *in, *freq, *ringTime;
			int n, inStride, freqStride, ringTimeStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq) || _ringTime(th, n, ringTimeStride, ringTime)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				Z w0 = *freq * freqmul;
				Z R = 1. + K / *ringTime;
				Z cs = tcos(w0);
				Z a1 = 2. * R * cs;
				Z a2 = -(R * R);
				Z b0 = .5;

				Z x0 = *in;
				Z y0 = b0 * (x0 - x2) + a1 * y1 + a2 * y2;

				out[i] = y0;
				y2 = y1;
				y1 = y0;
				x2 = x1;
				x1 = x0;

				in += inStride;
				freq += freqStride;
				ringTime += ringTimeStride;
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_freq.advance(n);
			_ringTime.advance(n);
		}

		_x1 = x1;
		_x2 = x2;
		_y1 = y1;
		_y2 = y2;
		produce(framesToFill);
	}
};

struct Formlet : public Gen
{
	ZIn _in;
	ZIn _freq;
	ZIn _atkTime;
	ZIn _dcyTime;
	Z _x1a, _x2a, _y1a, _y2a;
	Z _x1b, _x2b, _y1b, _y2b;
	Z _freqmul, _K;

	Formlet(Thread& th, Arg in, Arg freq, Arg atkTime, Arg dcyTime)
		: Gen(th, itemTypeZ, mostFinite(in, freq, atkTime, dcyTime)),
			_in(in), _freq(freq), _atkTime(atkTime), _dcyTime(dcyTime),
			_x1a(0.), _x2a(0.), _y1a(0.), _y2a(0.),
			_x1b(0.), _x2b(0.), _y1b(0.), _y2b(0.),
			_freqmul(th.rate.radiansPerSample),
			_K(log001 * th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "Formlet"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1a = _x1a;
		Z x2a = _x2a;
		Z y1a = _y1a;
		Z y2a = _y2a;
		Z x1b = _x1b;
		Z x2b = _x2b;
		Z y1b = _y1b;
		Z y2b = _y2b;
		Z freqmul = _freqmul;
		Z K = _K;

		while (framesToFill) {
			Z *in, *freq, *atkTime, *dcyTime;
			int n, inStride, freqStride, atkTimeStride, dcyTimeStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _freq(th, n, freqStride, freq) || _atkTime(th, n, atkTimeStride, atkTime) || _dcyTime(th, n, dcyTimeStride, dcyTime)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				Z w0 = *freq * freqmul;
				Z Ra = 1. + K / *atkTime;
				Z Rb = 1. + K / *dcyTime;
				Z cs = tcos(w0);
				Z a1a = 2. * Ra * cs;
				Z a2a = -(Ra * Ra);
				Z a1b = 2. * Rb * cs;
				Z a2b = -(Rb * Rb);
				Z b0 = .5;

				Z x0a = *in;
				Z y0a = b0 * (x0a - x2a) + a1a * y1a + a2a * y2a;

				Z x0b = *in;
				Z y0b = b0 * (x0b - x2b) + a1b * y1b + a2b * y2b;

				out[i] = y0b - y0a;
				y2a = y1a;
				y1a = y0a;
				x2a = x1a;
				x1a = x0a;

				y2b = y1b;
				y1b = y0b;
				x2b = x1b;
				x1b = x0b;

				in += inStride;
				freq += freqStride;
				atkTime += atkTimeStride;
				dcyTime += dcyTimeStride;
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_freq.advance(n);
			_atkTime.advance(n);
			_dcyTime.advance(n);
		}

		_x1a = x1a;
		_x2a = x2a;
		_y1a = y1a;
		_y2a = y2a;

		_x1b = x1b;
		_x2b = x2b;
		_y1b = y1b;
		_y2b = y2b;
		produce(framesToFill);
	}
};

struct KlankFilter
{
	KlankFilter(V f, V a, V r) :
		freq(f), amp(a), ringTime(r),
		x1(0.), x2(0.), y1(0.), y2(0.) {}

	ZIn freq, amp, ringTime;
	Z x1, x2, y1, y2;

};

struct Klank : public Gen
{
	ZIn _in;
	std::vector<KlankFilter> _filters;
	Z _freqmul, _K;
	Z* inputBuffer;

	Klank(Thread& th, Arg in, V freqs, V amps, V ringTimes)
		: Gen(th, itemTypeZ, in.isFinite()), _in(in),
			_freqmul(th.rate.radiansPerSample),
			_K(log001 * th.rate.invSampleRate)
	{
		inputBuffer = new Z[mBlockSize];

		int64_t numFilters = LONG_MAX;
		if (freqs.isVList()) {
			freqs = ((List*)freqs.o())->pack(th);
			numFilters = std::min(numFilters, freqs.length(th));
		}
		if (amps.isVList()) {
			amps = ((List*)amps.o())->pack(th);
			numFilters = std::min(numFilters, amps.length(th));
		}
		if (ringTimes.isVList()) {
			ringTimes = ((List*)ringTimes.o())->pack(th);
			numFilters = std::min(numFilters, ringTimes.length(th));
		}

		if (numFilters == LONG_MAX) numFilters = 1;

		for (ssize_t i = 0; i < numFilters; ++i) {
			KlankFilter kf(freqs.at(i), amps.at(i), ringTimes.at(i));
			_filters.push_back(kf);
		}

	}

	virtual ~Klank()
	{
		delete [] inputBuffer;
	}

	virtual const char* TypeName() const override { return "Klank"; }

	virtual void pull(Thread& th) override
	{

		// copy input
		int numInputFrames = mBlockSize;
		if (_in.fill(th, numInputFrames, inputBuffer, 1))
		{
			end();
			return;
		}
		int maxToFill = 0;

		Z* out0 = mOut->fulfillz(numInputFrames);
		memset(out0, 0, numInputFrames * sizeof(Z));

		Z freqmul = _freqmul;
		Z K = log001 * th.rate.invSampleRate;

		for (size_t filter = 0; filter < _filters.size(); ++filter) {
			int framesToFill = numInputFrames;
			KlankFilter& kf = _filters[filter];

			Z x1 = kf.x1;
			Z x2 = kf.x2;
			Z y1 = kf.y1;
			Z y2 = kf.y2;

			Z* in = inputBuffer;
			Z* out = out0;
			while (framesToFill) {
				Z *freq, *amp, *ringTime;
				int n, freqStride, ampStride, ringTimeStride;
				n = framesToFill;
				if (kf.freq(th, n, freqStride, freq) || kf.amp(th, n, ampStride, amp) || kf.ringTime(th, n, ringTimeStride, ringTime)) {
					setDone();
					maxToFill = std::max(maxToFill, framesToFill);
					break;
				}

				if (freqStride == 0) {
					if (ringTimeStride == 0) {
						Z w0 = *freq * freqmul;
						Z R = 1. + K / *ringTime;
						Z cs = tcos(w0);
						Z a1 = 2. * R * cs;
						Z a2 = -(R * R);
						Z b0 = .5;
						for (int i = 0; i < n; ++i) {
							Z x0 = *in;
							Z y0 = *amp * b0 * (x0 - x2) + a1 * y1 + a2 * y2;

							*out += y0;
							y2 = y1;
							y1 = y0;
							x2 = x1;
							x1 = x0;

							++in;
							++out;
							amp += ampStride;
						}
					} else {
						Z w0 = *freq * freqmul;
						Z cs = tcos(w0);
						Z b0 = .5;
						for (int i = 0; i < n; ++i) {
							Z R = 1. + K / *ringTime;
							Z a1 = 2. * R * cs;
							Z a2 = -(R * R);
							Z x0 = *in;
							Z y0 = *amp * b0 * (x0 - x2) + a1 * y1 + a2 * y2;

							*out += y0;
							y2 = y1;
							y1 = y0;
							x2 = x1;
							x1 = x0;

							++in;
							++out;
							amp += ampStride;
						}
					}
				} else {
					Z b0 = .5;
					for (int i = 0; i < n; ++i) {
						Z w0 = *freq * freqmul;
						Z R = 1. + K / *ringTime;
						Z cs = tcos(w0);
						Z a1 = 2. * R * cs;
						Z a2 = -(R * R);

						Z x0 = *in;
						Z y0 = *amp * b0 * (x0 - x2) + a1 * y1 + a2 * y2;

						*out += y0;
						y2 = y1;
						y1 = y0;
						x2 = x1;
						x1 = x0;

						++in;
						++out;
						freq += freqStride;
						amp += ampStride;
						ringTime += ringTimeStride;
					}
				}

				framesToFill -= n;
				out += n;
				kf.freq.advance(n);
				kf.amp.advance(n);
				kf.ringTime.advance(n);
			}
			kf.x1 = x1;
			kf.x2 = x2;
			kf.y1 = y1;
			kf.y2 = y2;
		}
		produce(maxToFill);
	}
};

static void resonz_(Thread& th, Prim* prim)
{
	V rq   = th.popZIn("resonz : rq");
	V freq = th.popZIn("resonz : freq");
	V in   = th.popZIn("resonz : in");

	th.push(new List(new Resonz(th, in, freq, rq)));
}

static void ringz_(Thread& th, Prim* prim)
{
	V ringTime = th.popZIn("ringz : ringTime");
	V freq     = th.popZIn("ringz : freq");
	V in       = th.popZIn("ringz : in");

	th.push(new List(new Ringz(th, in, freq, ringTime)));
}

static void formlet_(Thread& th, Prim* prim)
{
	V dcyTime = th.popZIn("formlet : dcyTime");
	V atkTime = th.popZIn("formlet : atkTime");
	V freq    = th.popZIn("formlet : freq");
	V in      = th.popZIn("formlet : in");

	th.push(new List(new Formlet(th, in, freq, atkTime, dcyTime)));
}

static void klank_(Thread& th, Prim* prim)
{
	V ringTimes = th.popZInList("klank : ringTimes");
	V amps		= th.popZInList("klank : amps");
	V freqs     = th.popZInList("klank : freqs");
	V in		= th.popZIn("klank : in");

	if (freqs.isVList() && !freqs.isFinite())
		indefiniteOp("klank : freqs", "");

	if (amps.isVList() && !amps.isFinite())
		indefiniteOp("klank : amps", "");

	if (ringTimes.isVList() && !ringTimes.isFinite())
		indefiniteOp("klank : ringTimes", "");

	th.push(new List(new Klank(th, in, freqs, amps, ringTimes)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct LeakDC : public Gen
{
	ZIn _in;
	ZIn _leak;
	Z _x1, _y1;

	LeakDC(Thread& th, Arg in, Arg leak)
		: Gen(th, itemTypeZ, mostFinite(in, leak)), _in(in), _leak(leak),
			_x1(0.), _y1(0.)
	{
	}

	virtual const char* TypeName() const override { return "LeakDC"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z x1 = _x1;
		Z y1 = _y1;
		while (framesToFill) {
			Z *in, *leak;
			int n, inStride, leakStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _leak(th, n, leakStride, leak)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				Z x0 = *in;
				y1 = x0 - x1 + *leak * y1;
				out[i] = y1;
				x1 = x0;
				in += inStride;
				leak += leakStride;
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_leak.advance(n);
		}

		_x1 = x1;
		_y1 = y1;
		produce(framesToFill);
	}

};

static void leakdc_(Thread& th, Prim* prim)
{
	V leak = th.popZIn("leakdc : leak");
	V in   = th.popZIn("leakdc : in");

	th.push(new List(new LeakDC(th, in, leak)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct LeakyIntegrator : public Gen
{
	ZIn _in;
	ZIn _leak;
	Z _y1;

	LeakyIntegrator(Thread& th, Arg in, Arg leak)
		: Gen(th, itemTypeZ, mostFinite(in, leak)), _in(in), _leak(leak),
			_y1(0.)
	{
	}

	virtual const char* TypeName() const override { return "LeakyIntegrator"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z y1 = _y1;
		while (framesToFill) {
			Z *in, *leak;
			int n, inStride, leakStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _leak(th, n, leakStride, leak)) {
				setDone();
				break;
			}

			for (int i = 0; i < n; ++i) {
				Z x0 = *in;
				y1 = x0 + *leak * y1;
				out[i] = y1;
				in += inStride;
				leak += leakStride;
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_leak.advance(n);
		}

		_y1 = y1;
		produce(framesToFill);
	}

};

static void leaky_(Thread& th, Prim* prim)
{
	V leak = th.popZIn("leaky : leak");
	V in   = th.popZIn("leaky : in");

	th.push(new List(new LeakyIntegrator(th, in, leak)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Decay : public Gen
{
	ZIn _in;
	ZIn _decayTime;
	Z _y1;
	Z _lagmul;

	Decay(Thread& th, Arg in, Arg decayTime)
		: Gen(th, itemTypeZ, mostFinite(in, decayTime)), _in(in), _decayTime(decayTime),
			_y1(0.), _lagmul(log001 * th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "Decay"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z y1 = _y1;
		Z lagmul = _lagmul;
		while (framesToFill) {
			Z *in, *decayTime;
			int n, inStride, decayTimeStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _decayTime(th, n, decayTimeStride, decayTime)) {
				setDone();
				break;
			}

			if (decayTimeStride == 0) {
				Z b1 = std::max(0., 1. + lagmul / *decayTime); // this is an approximation to exp(log001 * th.rate.invSampleRate / *lagTime)
				for (int i = 0; i < n; ++i) {
					Z x0 = *in;
					out[i] = y1 = x0 + b1 * y1;
					in += inStride;
				}
			} else {
				for (int i = 0; i < n; ++i) {
					Z b1 = std::max(0., 1. + lagmul / *decayTime); // this is an approximation to exp(log001 * th.rate.invSampleRate / *lagTime)
					Z x0 = *in;
					out[i] = y1 = x0 + b1 * y1;
					in += inStride;
					decayTime += decayTimeStride;
				}
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_decayTime.advance(n);
		}

		_y1 = y1;
		produce(framesToFill);
	}

};

static void decay_(Thread& th, Prim* prim)
{
	V decayTime = th.popZIn("decay : decayTime");
	V in   = th.popZIn("decay : in");

	th.push(new List(new Decay(th, in, decayTime)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Decay2 : public Gen
{
	ZIn _in;
	ZIn _attackTime;
	ZIn _decayTime;
	Z _y1a;
	Z _y1b;
	Z _lagmul;

	Decay2(Thread& th, Arg in, Arg attackTime, Arg decayTime)
		: Gen(th, itemTypeZ, mostFinite(in, attackTime, decayTime)), _in(in), _attackTime(attackTime), _decayTime(decayTime),
			_y1a(0.), _y1b(0.), _lagmul(log001 * th.rate.invSampleRate)
	{
	}

	virtual const char* TypeName() const override { return "Decay2"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;

		Z* out = mOut->fulfillz(framesToFill);
		Z y1a = _y1a;
		Z y1b = _y1b;
		Z lagmul = _lagmul;
		while (framesToFill) {
			Z *in, *attackTime, *decayTime;
			int n, inStride, attackTimeStride, decayTimeStride;
			n = framesToFill;
			if (_in(th, n, inStride, in) || _attackTime(th, n, attackTimeStride, attackTime) || _decayTime(th, n, decayTimeStride, decayTime)) {
				setDone();
				break;
			}

			if (attackTimeStride == 0 && decayTimeStride == 0) {
				Z b1a = std::max(0., 1. + lagmul / *attackTime); // this is an approximation to exp(log001 * th.rate.invSampleRate / *lagTime)
				Z b1b = std::max(0., 1. + lagmul / *decayTime); // this is an approximation to exp(log001 * th.rate.invSampleRate / *lagTime)
				for (int i = 0; i < n; ++i) {
					Z x0 = *in;
					y1a = x0 + b1a * y1a;
					y1b = x0 + b1b * y1b;
					out[i] = y1b - y1a;
					in += inStride;
				}
			} else {
				for (int i = 0; i < n; ++i) {
					Z b1a = std::max(0., 1. + lagmul / *attackTime); // this is an approximation to exp(log001 * th.rate.invSampleRate / *lagTime)
					Z b1b = std::max(0., 1. + lagmul / *decayTime); // this is an approximation to exp(log001 * th.rate.invSampleRate / *lagTime)
					Z x0 = *in;
					y1a = x0 + b1a * y1a;
					y1b = x0 + b1b * y1b;
					out[i] = y1b - y1a;
					in += inStride;
					attackTime += attackTimeStride;
					decayTime += decayTimeStride;
				}
			}

			framesToFill -= n;
			out += n;
			_in.advance(n);
			_attackTime.advance(n);
			_decayTime.advance(n);
		}

		_y1a = y1a;
		_y1b = y1b;
		produce(framesToFill);
	}

};

static void decay2_(Thread& th, Prim* prim)
{
	V decayTime = th.popZIn("decay2 : decayTime");
	V attackTime = th.popZIn("decay2 : attackTime");
	V in   = th.popZIn("decay2 : in");

	th.push(new List(new Decay2(th, in, attackTime, decayTime)));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

struct Phase90A : public OneInputUGen<Phase90A>
{
	// filters by Olli Niemitalo
	constexpr static Z c1_ = 0.47940086558884; // sq(.6923878);
	constexpr static Z c2_ = 0.87621849353931; // sq(.9360654322959);
	constexpr static Z c3_ = 0.9765975895082;  // sq(.9882295226860);
	constexpr static Z c4_ = 0.99749925593555; // sq(.9987488452737);
	Z v1_ = 0.;
	Z v2_ = 0.;
	Z w1_ = 0.;
	Z w2_ = 0.;
	Z x1_ = 0.;
	Z x2_ = 0.;
	Z y1_ = 0.;
	Z y2_ = 0.;
	Z z1_ = 0.;
	Z z2_ = 0.;

	Phase90A(Thread& th, Arg in) : OneInputUGen<Phase90A>(th, in)
	{
	}

	virtual const char* TypeName() const override { return "Phase90A"; }

	void calc(int n, Z* out, Z* in, int inStride)
	{
		Z c1 = c1_;
		Z c2 = c2_;
		Z c3 = c3_;
		Z c4 = c4_;

		Z v1 = v1_;
		Z v2 = v2_;
		Z w1 = w1_;
		Z w2 = w2_;
		Z x1 = x1_;
		Z x2 = x2_;
		Z y1 = y1_;
		Z y2 = y2_;
		Z z1 = z1_;
		Z z2 = z2_;
		for (int i = 0; i < n; ++i) {
			Z v0 = *in; in += inStride;
			Z w0 = c1 * (v0 + w2) - v2;
			Z x0 = c2 * (w0 + x2) - w2;
			Z y0 = c3 * (x0 + y2) - x2;
			Z z0 = c4 * (y0 + z2) - y2;
			out[i] = z1;
			v2 = v1;
			w2 = w1;
			x2 = x1;
			y2 = y1;
			z2 = z1;
			v1 = v0;
			w1 = w0;
			x1 = x0;
			y1 = y0;
			z1 = z0;
		}
		v1_ = v1;
		v2_ = v2;
		w1_ = w1;
		w2_ = w2;
		x1_ = x1;
		x2_ = x2;
		y1_ = y1;
		y2_ = y2;
		z1_ = z1;
		z2_ = z2;
	}
};

struct Phase90B : public OneInputUGen<Phase90B>
{
	// filters by Olli Niemitalo
	constexpr static Z c1_ = 0.1617584983677;  // sc_squared(.4021921162426);
	constexpr static Z c2_ = 0.73302893234149; // sc_squared(.8561710882420);
	constexpr static Z c3_ = 0.94534970032911; // sc_squared(.9722909545651);
	constexpr static Z c4_ = 0.99059915668453; // sc_squared(.9952884791278);
	Z v1_ = 0.;
	Z v2_ = 0.;
	Z w1_ = 0.;
	Z w2_ = 0.;
	Z x1_ = 0.;
	Z x2_ = 0.;
	Z y1_ = 0.;
	Z y2_ = 0.;
	Z z1_ = 0.;
	Z z2_ = 0.;

	Phase90B(Thread& th, Arg in) : OneInputUGen<Phase90B>(th, in)
	{
	}

	virtual const char* TypeName() const override { return "Phase90B"; }

	void calc(int n, Z* out, Z* in, int inStride)
	{
		Z c1 = c1_;
		Z c2 = c2_;
		Z c3 = c3_;
		Z c4 = c4_;

		Z v1 = v1_;
		Z v2 = v2_;
		Z w1 = w1_;
		Z w2 = w2_;
		Z x1 = x1_;
		Z x2 = x2_;
		Z y1 = y1_;
		Z y2 = y2_;
		Z z1 = z1_;
		Z z2 = z2_;
		for (int i = 0; i < n; ++i) {
			Z v0 = *in; in += inStride;
			Z w0 = c1 * (v0 + w2) - v2;
			Z x0 = c2 * (w0 + x2) - w2;
			Z y0 = c3 * (x0 + y2) - x2;
			Z z0 = c4 * (y0 + z2) - y2;
			out[i] = z0;
			v2 = v1;
			w2 = w1;
			x2 = x1;
			y2 = y1;
			z2 = z1;
			v1 = v0;
			w1 = w0;
			x1 = x0;
			y1 = y0;
			z1 = z0;
		}
		v1_ = v1;
		v2_ = v2;
		w1_ = w1;
		w2_ = w2;
		x1_ = x1;
		x2_ = x2;
		y1_ = y1;
		y2_ = y2;
		z1_ = z1;
		z2_ = z2;
	}
};

struct AmpFollow : public OneInputUGen<AmpFollow>
{
	Z _y1a;
	Z _y1b;
	Z _lagmul;
	bool once;

	// filters by Olli Niemitalo
	constexpr static Z c1a_ = 0.47940086558884; // sq(.6923878);
	constexpr static Z c2a_ = 0.87621849353931; // sq(.9360654322959);
	constexpr static Z c3a_ = 0.9765975895082;  // sq(.9882295226860);
	constexpr static Z c4a_ = 0.99749925593555; // sq(.9987488452737);
	Z v1a_ = 0.;
	Z v2a_ = 0.;
	Z w1a_ = 0.;
	Z w2a_ = 0.;
	Z x1a_ = 0.;
	Z x2a_ = 0.;
	Z y1a_ = 0.;
	Z y2a_ = 0.;
	Z z1a_ = 0.;
	Z z2a_ = 0.;

	constexpr static Z c1b_ = 0.1617584983677;  // sc_squared(.4021921162426);
	constexpr static Z c2b_ = 0.73302893234149; // sc_squared(.8561710882420);
	constexpr static Z c3b_ = 0.94534970032911; // sc_squared(.9722909545651);
	constexpr static Z c4b_ = 0.99059915668453; // sc_squared(.9952884791278);
	Z v1b_ = 0.;
	Z v2b_ = 0.;
	Z w1b_ = 0.;
	Z w2b_ = 0.;
	Z x1b_ = 0.;
	Z x2b_ = 0.;
	Z y1b_ = 0.;
	Z y2b_ = 0.;
	Z z1b_ = 0.;
	Z z2b_ = 0.;

	Z b1r_;
	Z b1f_;

	Z l1a_ = 0.;
	Z l1b_ = 0.;

	AmpFollow(Thread& th, Arg in, Z atk, Z dcy)
		: OneInputUGen<AmpFollow>(th, in),
		_y1a(0.), _y1b(0.), _lagmul(log001 * th.rate.invSampleRate), once(true)
	{
		b1r_ = atk == 0. ? 0. : std::max(0., 1. + _lagmul / atk); // this is an approximation to exp(log001 * th.rate.invSampleRate / *riseTime)
		b1f_ = dcy == 0. ? 0. : std::max(0., 1. + _lagmul / dcy);
	}

	virtual const char* TypeName() const override { return "AmpFollow"; }

	void calc(int n, Z* out, Z* in, int inStride)
	{
		Z c1a = c1a_;
		Z c2a = c2a_;
		Z c3a = c3a_;
		Z c4a = c4a_;

		Z v1a = v1a_;
		Z v2a = v2a_;
		Z w1a = w1a_;
		Z w2a = w2a_;
		Z x1a = x1a_;
		Z x2a = x2a_;
		Z y1a = y1a_;
		Z y2a = y2a_;
		Z z1a = z1a_;
		Z z2a = z2a_;

		Z c1b = c1b_;
		Z c2b = c2b_;
		Z c3b = c3b_;
		Z c4b = c4b_;

		Z v1b = v1b_;
		Z v2b = v2b_;
		Z w1b = w1b_;
		Z w2b = w2b_;
		Z x1b = x1b_;
		Z x2b = x2b_;
		Z y1b = y1b_;
		Z y2b = y2b_;
		Z z1b = z1b_;
		Z z2b = z2b_;

		Z l1a = l1a_;
		Z l1b = l1b_;

		for (int i = 0; i < n; ++i) {
			Z v0 = *in; in += inStride;
			{
				Z w0 = c1a * (v0 + w2a) - v2a;
				Z x0 = c2a * (w0 + x2a) - w2a;
				Z y0 = c3a * (x0 + y2a) - x2a;
				Z z0 = c4a * (y0 + z2a) - y2a;
				v2a = v1a;
				w2a = w1a;
				x2a = x1a;
				y2a = y1a;
				z2a = z1a;
				v1a = v0;
				w1a = w0;
				x1a = x0;
				y1a = y0;
				z1a = z0;
			}

			{
				Z w0 = c1b * (v0 + w2b) - v2b;
				Z x0 = c2b * (w0 + x2b) - w2b;
				Z y0 = c3b * (x0 + y2b) - x2b;
				Z z0 = c4b * (y0 + z2b) - y2b;
				v2b = v1b;
				w2b = w1b;
				x2b = x1b;
				y2b = y1b;
				z2b = z1b;
				v1b = v0;
				w1b = w0;
				x1b = x0;
				y1b = y0;
				z1b = z0;
			}

			Z l0a = hypot(z1a, z1b); // vectorize this

			l1a = l0a + (l0a > l1a ? b1r_ : b1f_) * (l1a - l0a);
			l1b = l1a + (l1a > l1b ? b1r_ : b1f_) * (l1b - l1a);
			out[i] = l1b;
		}
		v1a_ = v1a;
		v2a_ = v2a;
		w1a_ = w1a;
		w2a_ = w2a;
		x1a_ = x1a;
		x2a_ = x2a;
		y1a_ = y1a;
		y2a_ = y2a;
		z1a_ = z1a;
		z2a_ = z2a;

		v1b_ = v1b;
		v2b_ = v2b;
		w1b_ = w1b;
		w2b_ = w2b;
		x1b_ = x1b;
		x2b_ = x2b;
		y1b_ = y1b;
		y2b_ = y2b;
		z1b_ = z1b;
		z2b_ = z2b;

		l1a_ = l1a;
		l1b_ = l1b;
	}
};

static void hilbert_(Thread& th, Prim* prim)
{
	V in   = th.popZIn("hilbert : in");

	th.push(new List(new Phase90A(th, in)));
	th.push(new List(new Phase90B(th, in)));
}

static void ampf_(Thread& th, Prim* prim)
{
	Z dcy  = th.popFloat("ampf : dcyTime");
	Z atk  = th.popFloat("ampf : atkTime");
	V in   = th.popZIn("ampf : in");

	th.push(new List(new AmpFollow(th, in, atk, dcy)));
}

#define DEF(NAME, N, HELP) 	vm.def(#NAME, N, NAME##_, HELP);
#define DEFMCX(NAME, N, HELP) 	vm.defmcx(#NAME, N, NAME##_, HELP);
#define DEFAM(NAME, MASK, HELP) 	vm.defautomap(#NAME, #MASK, NAME##_, HELP);

void AddFilterUGenOps()
{
	vm.addBifHelp("\n*** filter unit generators ***");
	DEFMCX(lag, 2, "(in decayTime --> out) one pole lag filter. decayTime determines rate of convergence.")
	DEFMCX(lag2, 2, "(in decayTime --> out) cascade of two one pole lag filters. decayTime determines rate of convergence.")
	DEFMCX(lag3, 2, "(in decayTime --> out) cascade of three one pole lag filters. decayTime determines rate of convergence.")

	DEFMCX(lagud, 3, "(in upDecayTime, downDecayTime --> out) one pole lag filter. up/down DecayTimes determines rate of convergence up/down.")
	DEFMCX(lagud2, 3, "(in upDecayTime, downDecayTime --> out) cascade of two one pole lag filters. up/down DecayTimes determines rate of convergence up/down.")
	DEFMCX(lagud3, 3, "(in upDecayTime, downDecayTime --> out) cascade of three one pole lag filters. up/down DecayTimes determines rate of convergence up/down.")

	DEFMCX(lpf1, 2, "(in freq --> out) low pass filter. 6 dB/oct.")
	DEFMCX(hpf1, 2, "(in freq --> out) high pass filter. 6 dB/oct.")
	DEFMCX(lpf, 2, "(in freq --> out) low pass filter. 12 dB/oct.")
	DEFMCX(hpf, 2, "(in freq --> out) high pass filter. 12 dB/oct.")
	DEFMCX(lpf2, 2, "(in freq --> out) low pass filter. 24 dB/oct.")
	DEFMCX(hpf2, 2, "(in freq --> out) high pass filter. 24 dB/oct.")

	DEFMCX(rlpf, 3, "(in freq rq --> out) resonant low pass filter. 12 dB/oct slope. rq is 1/Q.")
	DEFMCX(rhpf, 3, "(in freq rq --> out) resonant high pass filter. 12 dB/oct slope. rq is 1/Q.")
	DEFMCX(rlpf2, 3, "(in freq rq --> out) resonant low pass filter. 24 dB/oct slope. rq is 1/Q.")
	DEFMCX(rhpf2, 3, "(in freq rq --> out) resonant high pass filter. 24 dB/oct slope. rq is 1/Q.")

	DEFMCX(rlpfc, 3, "(in freq rq --> out) resonant low pass filter with saturation. 12 dB/oct slope. rq is 1/Q.")
	DEFMCX(rhpfc, 3, "(in freq rq --> out) resonant high pass filter with saturation. 12 dB/oct slope. rq is 1/Q.")
	DEFMCX(rlpf2c, 3, "(in freq rq --> out) resonant low pass filter with saturation. 24 dB/oct slope. rq is 1/Q.")
	DEFMCX(rhpf2c, 3, "(in freq rq --> out) resonant high pass filter with saturation. 24 dB/oct slope. rq is 1/Q.")

	DEFMCX(bpf, 3, "(in freq bw --> out) band pass filter. bw is bandwidth in octaves.")
	DEFMCX(bsf, 3, "(in freq bw --> out) band stop filter. bw is bandwidth in octaves.")
	DEFMCX(apf, 3, "(in freq bw --> out) all pass filter. bw is bandwidth in octaves.")

	DEFMCX(peq, 4, "(in freq bw gain --> out) parametric equalization filter. bw is bandwidth in octaves.")
	DEFMCX(lsf, 3, "(in freq gain --> out) low shelf filter.")
	DEFMCX(hsf, 3, "(in freq gain --> out) high shelf filter.")
	DEFMCX(lsf1, 3, "(in freq gain --> out) low shelf filter.")

	DEFMCX(resonz, 3, "(in freq rq --> out) resonant filter.")
	DEFMCX(ringz, 3, "(in freq ringTime --> out) resonant filter specified by a ring time in seconds.")
	DEFMCX(formlet, 4, "(in freq atkTime dcyTime --> out) a formant filter whose impulse response is a sine grain.")
	DEFAM(klank, zaaa, "(in freqs amps ringTimes --> out) a bank of ringz filters. freqs amps and ringTimes are arrays.")

	DEFMCX(leakdc, 2, "(in coef --> out) leaks away energy at 0 Hz.")
	DEFMCX(leaky, 2, "(in coef --> out) leaky integrator.")
	DEFMCX(decay, 2, "(in decayTime --> out) outputs an exponential decay for impulses at the input.")
	DEFMCX(decay2, 3, "(in atkTime dcyTime --> out) outputs an exponential attack and decay for impulses at the input.")

	DEFMCX(hilbert, 1, "(in --> outA outB) returns two signals that are 90 degrees phase shifted from each other.")
	DEFMCX(ampf, 3, "(in atkTime dcyTime --> out) amplitude follower.")
}

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "Midi.hpp"
#include "VM.hpp"
#include "UGen.hpp"
#include "ErrorCodes.hpp"
#include <CoreMidi/CoreMidi.h>
#include <vector>
#include <mach/mach_time.h>

struct MidiChanState
{
	uint8_t control[128];
	uint8_t polytouch[128];
	uint8_t keyvel[128];
	uint32_t numKeysDown;
	uint16_t bend;
	uint8_t touch;
	uint8_t program;
	uint8_t lastkey;
	uint8_t lastvel;
};

const int kMaxMidiPorts = 16;
MidiChanState gMidiState[kMaxMidiPorts][16];
bool gMidiDebug = false;
MIDIClientRef gMIDIClient = 0;
MIDIPortRef gMIDIInPort[kMaxMidiPorts], gMIDIOutPort[kMaxMidiPorts];
int gNumMIDIInPorts = 0, gNumMIDIOutPorts = 0;
bool gMIDIInitialized = false;

static bool gSysexFlag = false;
static Byte gRunningStatus = 0;
std::vector<Byte> gSysexData;

static void sysexBegin() {
	gRunningStatus = 0; // clear running status
	//gSysexData.clear();
	gSysexFlag = true;
}

static void sysexEnd(int lastUID) {
	gSysexFlag = false;
}

static void sysexEndInvalid() {
	gSysexFlag = false;
}

static int midiProcessSystemPacket(MIDIPacket *pkt, int chan) {
	int index, data;
	switch (chan) {
	case 7: // added cp: Sysex EOX must be taken into account if first on data packet
	case 0:
		{
		int last_uid = 0;
		int m = pkt->length;
		Byte* p_pkt = pkt->data;
		Byte pktval;

		while(m--) {
			pktval = *p_pkt++;
			if(pktval & 0x80) { // status byte
				if(pktval == 0xF7) { // end packet
					gSysexData.push_back(pktval); // add EOX
					if(gSysexFlag)
						sysexEnd(last_uid); // if last_uid != 0 rebuild the VM.
					else
						sysexEndInvalid(); // invalid 1 byte with only EOX can happen
					break;
				}
				else if(pktval == 0xF0) { // new packet
					if(gSysexFlag) {// invalid new one/should not happen -- but handle in case
						// store the last uid value previous to invalid data to rebuild VM after sysexEndInvalid call
						// since it may call sysexEnd() just after it !
						sysexEndInvalid();
					}
					sysexBegin(); // new sysex in
					//gSysexData.push_back(pktval); // add SOX
				}
				else {// abnormal data in middle of sysex packet
					//gSysexData.push_back(pktval); // add it as an abort message
					sysexEndInvalid(); // flush invalid
					m = 0; // discard all packet
					break;
				}
			}
			else if(gSysexFlag) {
				//gSysexData.push_back(pktval); // add Byte
			} else { // garbage - handle in case - discard it
				break;
			}
		}
		return (pkt->length-m);
		}
	break;

	case 1 :
		index = pkt->data[1] >> 4;
		data  = pkt->data[1] & 0xf;
		switch (index) { case 1: case 3: case 5: case 7: { data = data << 4; } }
		return 2;

	case 2 : 	//songptr
		return 3;

	case 3 :	// song select
		return 2;

	case 8 :	//clock
	case 10:	//start
	case 11:	//continue
	case 12: 	//stop
	case 15:	//reset
		gRunningStatus = 0; // clear running status
		return 1;

	default:
		break;
	}

	return (1);
}

static void midiProcessPacket(MIDIPacket *pkt, int srcIndex)
{
	if(pkt) {
		int i = 0;
		while (i < pkt->length) {
			uint8_t status = pkt->data[i] & 0xF0;
			uint8_t chan = pkt->data[i] & 0x0F;
			uint8_t a, b;

			if(status & 0x80) // set the running status for voice messages
				gRunningStatus = ((status >> 4) == 0xF) ? 0 : pkt->data[i]; // keep also additional info
		L:
			switch (status) {
			case 0x80 : //noteOff
				a = pkt->data[i+1];
				b = pkt->data[i+2];
				if (gMidiDebug) printf("midi note off %d %d %d %d\n", srcIndex, chan+1, a, b);
				gMidiState[srcIndex][chan].keyvel[a] = 0;
				--gMidiState[srcIndex][chan].numKeysDown;
				i += 3;
				break;
			case 0x90 : //noteOn
				a = pkt->data[i+1];
				b = pkt->data[i+2];
				if (gMidiDebug) printf("midi note on %d %d %d %d\n", srcIndex, chan+1, a, b);
				if (b) {
					gMidiState[srcIndex][chan].lastkey = a;
					gMidiState[srcIndex][chan].lastvel = b;
					++gMidiState[srcIndex][chan].numKeysDown;
				} else {
					--gMidiState[srcIndex][chan].numKeysDown;
				}
				gMidiState[srcIndex][chan].keyvel[a] = b;
				i += 3;
				break;
			case 0xA0 : //polytouch
				a = pkt->data[i+1];
				b = pkt->data[i+2];
				if (gMidiDebug) printf("midi poly %d %d %d %d\n", srcIndex, chan+1, a, b);
				gMidiState[srcIndex][chan].polytouch[a] = b;
				i += 3;
				break;
			case 0xB0 : //control
				a = pkt->data[i+1];
				b = pkt->data[i+2];
				if (gMidiDebug) printf("midi control %d %d %d %d\n", srcIndex, chan+1, a, b);
				gMidiState[srcIndex][chan].control[a] = b;
				if (a == 120 || (a >= 123 && a <= 127)) {
					// all notes off
					memset(gMidiState[srcIndex][chan].keyvel, 0, 128);
					gMidiState[srcIndex][chan].numKeysDown = 0;
				} else if (a == 121) {
					// reset ALL controls to zero, don't follow MMA recommended practices.
					memset(gMidiState[srcIndex][chan].control, 0, 128);
					gMidiState[srcIndex][chan].bend = 0x4000;
				}
				i += 3;
				break;
			case 0xC0 : //program
				a = pkt->data[i+1];
				gMidiState[srcIndex][chan].program = a;
				if (gMidiDebug) printf("midi program %d %d %d\n", srcIndex, chan+1, a);
				i += 2;
				break;
			case 0xD0 : //touch
				a = pkt->data[i+1];
				printf("midi touch %d %d\n", chan+1, a);
				gMidiState[srcIndex][chan].touch = a;
				i += 2;
				break;
			case 0xE0 : //bend
				a = pkt->data[i+1];
				b = pkt->data[i+2];
				if (gMidiDebug) printf("midi bend %d %d %d %d\n", srcIndex, chan+1, a, b);
				gMidiState[srcIndex][chan].bend = ((b << 7) | a) - 8192;
				i += 3;
				break;
			case 0xF0 :
				i += midiProcessSystemPacket(pkt, chan);
				break;
			default :	// data byte => continuing sysex message
				if(gRunningStatus && !gSysexFlag) { // modified cp: handling running status. may be we should here
					status = gRunningStatus & 0xF0; // accept running status only inside a packet beginning
					chan = gRunningStatus & 0x0F;	// with a valid status byte ?
					--i;
					goto L; // parse again with running status set
				}
				chan = 0;
				i += midiProcessSystemPacket(pkt, chan);
				break;
			}
		}
	}
}

static void midiReadProc(const MIDIPacketList *pktlist, void* readProcRefCon, void* srcConnRefCon)
{
	MIDIPacket *pkt = (MIDIPacket*)pktlist->packet;
	int srcIndex = (int)(size_t) srcConnRefCon;
	for (uint32_t i=0; i<pktlist->numPackets; ++i) {
		midiProcessPacket(pkt, srcIndex);
		pkt = MIDIPacketNext(pkt);
	}
}

static void midiNotifyProc(const MIDINotification *message, void *refCon)
{
	printf("midi notification %d %d\n", (int)message->messageID, (int)message->messageSize);
}

static struct mach_timebase_info machTimebaseInfo() {
    struct mach_timebase_info info;
    mach_timebase_info(&info);
    return info;
}

static MIDITimeStamp midiTime(float latencySeconds)
{
    // add the latency expressed in seconds, to the current host time base.
    static struct mach_timebase_info info = machTimebaseInfo(); // cache the timebase info.
    Float64 latencyNanos = 1000000000 * latencySeconds;
    MIDITimeStamp latencyMIDI = (latencyNanos / (Float64)info.numer) * (Float64)info.denom;
    return (MIDITimeStamp)mach_absolute_time() + latencyMIDI;
}

void sendmidi(int port, MIDIEndpointRef dest, int length, int hiStatus, int loStatus, int aval, int bval, float late);
void sendmidi(int port, MIDIEndpointRef dest, int length, int hiStatus, int loStatus, int aval, int bval, float late)
{
	MIDIPacketList mpktlist;
	MIDIPacketList * pktlist = &mpktlist;
	MIDIPacket * pk = MIDIPacketListInit(pktlist);
	ByteCount nData = (ByteCount) length;
	pk->data[0] = (Byte) (hiStatus & 0xF0) | (loStatus & 0x0F);
	pk->data[1] = (Byte) aval;
	pk->data[2] = (Byte) bval;
	pk = MIDIPacketListAdd(pktlist, sizeof(struct MIDIPacketList) , pk, midiTime(late), nData, pk->data);
	/*OSStatus error =*/ MIDISend(gMIDIOutPort[port],  dest, pktlist );
}

static int midiCleanUp()
{
	/*
	* do not catch errors when disposing ports
	* MIDIClientDispose should normally dispose the ports attached to it
	* but clean up the pointers in case
	*/
	int i = 0;
	for (i=0; i<gNumMIDIOutPorts; ++i) {
		if (gMIDIOutPort[i]) {
			MIDIPortDispose(gMIDIOutPort[i]);
			gMIDIOutPort[i] = 0;
		}
	}
	gNumMIDIOutPorts = 0;

	for (i=0; i<gNumMIDIInPorts; ++i) {
		if (gMIDIInPort[i]) {
			MIDIPortDispose(gMIDIInPort[i]);
			gMIDIInPort[i] = 0;
		}
	}
	gNumMIDIInPorts = 0;

	if (gMIDIClient) {
		if( MIDIClientDispose(gMIDIClient) ) {
			fprintf(stderr, "Error: failed to dispose MIDIClient\n" );
			return errFailed;
		}
		gMIDIClient = 0;
	}
	return errNone;
}

static int prListMIDIEndpoints();

static int midiInit(int numIn, int numOut)
{
	OSStatus err = noErr;

	midiCleanUp();

	memset(gMidiState, 0, sizeof(gMidiState));

	numIn = std::clamp(numIn, 1, kMaxMidiPorts);
	numOut = std::clamp(numOut, 1, kMaxMidiPorts);

	int enc = kCFStringEncodingMacRoman;
	CFAllocatorRef alloc = CFAllocatorGetDefault();

	{
		CFStringRef clientName = CFStringCreateWithCString(alloc, "SAPF", enc);
		CFReleaser clientNameReleaser(clientName);

		__block OSStatus err2 = noErr;
		dispatch_sync(dispatch_get_main_queue(), ^{
			err2 = MIDIClientCreate(clientName, midiNotifyProc, nil, &gMIDIClient);
		});
		printf("gMIDIClient %d\n", (int)gMIDIClient);
		if (err2) {
			fprintf(stderr, "Could not create MIDI client. error %d\n", err);
			return errFailed;
		}
	}

	for (int i=0; i<numIn; ++i) {
		char str[32];
		snprintf(str, 32, "in%d\n", i);
		CFStringRef inputPortName = CFStringCreateWithCString(alloc, str, enc);
		CFReleaser inputPortNameReleaser(inputPortName);

		err = MIDIInputPortCreate(gMIDIClient, inputPortName, midiReadProc, &i, gMIDIInPort+i);
		if (err) {
			gNumMIDIInPorts = i;
			fprintf(stderr, "Could not create MIDI port %s. error %d\n", str, err);
			return errFailed;
		}
	}

	gNumMIDIInPorts = numIn;

	for (int i=0; i<numOut; ++i) {
		char str[32];
		snprintf(str, 32, "out%d\n", i);
		CFStringRef outputPortName = CFStringCreateWithCString(alloc, str, enc);
		CFReleaser outputPortNameReleaser(outputPortName);

		err = MIDIOutputPortCreate(gMIDIClient, outputPortName, gMIDIOutPort+i);
		if (err) {
			gNumMIDIOutPorts = i;
			fprintf(stderr, "Could not create MIDI out port. error %d\n", err);
			return errFailed;
		}
	}
	gNumMIDIOutPorts = numOut;

	prListMIDIEndpoints();

	return errNone;
}

static int prListMIDIEndpoints()
{
	OSStatus error;
	int numSrc = (int)MIDIGetNumberOfSources();
	int numDst = (int)MIDIGetNumberOfDestinations();

	printf("midi sources %d destinations %d\n", (int)numSrc, (int)numDst);

	for (int i=0; i<numSrc; ++i) {
		MIDIEndpointRef src = MIDIGetSource(i);
		SInt32 uid = 0;
		MIDIObjectGetIntegerProperty(src, kMIDIPropertyUniqueID, &uid);

		MIDIEntityRef ent;
		error = MIDIEndpointGetEntity(src, &ent);

		CFStringRef devname, endname;
		char cendname[1024], cdevname[1024];

		// Virtual sources don't have entities
		if(error)
		{
			MIDIObjectGetStringProperty(src, kMIDIPropertyName, &devname);
			MIDIObjectGetStringProperty(src, kMIDIPropertyName, &endname);
			CFStringGetCString(devname, cdevname, 1024, kCFStringEncodingUTF8);
			CFStringGetCString(endname, cendname, 1024, kCFStringEncodingUTF8);
		}
		else
		{
			MIDIDeviceRef dev;

			MIDIEntityGetDevice(ent, &dev);
			MIDIObjectGetStringProperty(dev, kMIDIPropertyName, &devname);
			MIDIObjectGetStringProperty(src, kMIDIPropertyName, &endname);
			CFStringGetCString(devname, cdevname, 1024, kCFStringEncodingUTF8);
			CFStringGetCString(endname, cendname, 1024, kCFStringEncodingUTF8);
		}

		printf("MIDI Source %2d '%s', '%s' UID: %d\n", i, cdevname, cendname, uid);
	}

	for (int i=0; i<numDst; ++i) {
		MIDIEndpointRef dst = MIDIGetDestination(i);
		SInt32 uid = 0;
		MIDIObjectGetIntegerProperty(dst, kMIDIPropertyUniqueID, &uid);

		MIDIEntityRef ent;
		error = MIDIEndpointGetEntity(dst, &ent);

		CFStringRef devname, endname;
		char cendname[1024], cdevname[1024];

		// Virtual destinations don't have entities either
		if(error)
		{
			MIDIObjectGetStringProperty(dst, kMIDIPropertyName, &devname);
			MIDIObjectGetStringProperty(dst, kMIDIPropertyName, &endname);
			CFStringGetCString(devname, cdevname, 1024, kCFStringEncodingUTF8);
			CFStringGetCString(endname, cendname, 1024, kCFStringEncodingUTF8);

		}
		else
		{
			MIDIDeviceRef dev;

			MIDIEntityGetDevice(ent, &dev);
			MIDIObjectGetStringProperty(dev, kMIDIPropertyName, &devname);
			MIDIObjectGetStringProperty(dst, kMIDIPropertyName, &endname);
			CFStringGetCString(devname, cdevname, 1024, kCFStringEncodingUTF8);
			CFStringGetCString(endname, cendname, 1024, kCFStringEncodingUTF8);
		}
		printf("MIDI Destination %2d '%s', '%s' UID: %d\n", i, cdevname, cendname, uid);
	}
	return errNone;
}

static int prConnectMIDIIn(int uid, int inputIndex)
{
	if (inputIndex < 0 || inputIndex >= gNumMIDIInPorts) return errOutOfRange;

	MIDIEndpointRef src=0;
	MIDIObjectType mtype;
	MIDIObjectFindByUniqueID(uid, (MIDIObjectRef*)&src, &mtype);
	if (mtype != kMIDIObjectType_Source) return errFailed;

	//pass the uid to the midiReadProc to identify the src
	void* p = (void*)(uintptr_t)inputIndex;
	MIDIPortConnectSource(gMIDIInPort[inputIndex], src, p);

	return errNone;
}

static int prDisconnectMIDIIn(int uid, int inputIndex)
{
	if (inputIndex < 0 || inputIndex >= gNumMIDIInPorts) return errOutOfRange;

	MIDIEndpointRef src=0;
	MIDIObjectType mtype;
	MIDIObjectFindByUniqueID(uid, (MIDIObjectRef*)&src, &mtype);
	if (mtype != kMIDIObjectType_Source) return errFailed;

	MIDIPortDisconnectSource(gMIDIInPort[inputIndex], src);

	return errNone;
}

static void midiStart_(Thread& th, Prim* prim)
{
	midiInit(16, 19);
}

static void midiRestart_(Thread& th, Prim* prim)
{
	MIDIRestart();
}

static void midiStop_(Thread& th, Prim* prim)
{
	midiCleanUp();
}

static void midiList_(Thread& th, Prim* prim)
{
	prListMIDIEndpoints();
}

static void midiConnectInput_(Thread& th, Prim* prim)
{
	int index = (int)th.popInt("midiConnectInput : port");
	int uid = (int)th.popInt("midiConnectInput : sourceUID");
	prConnectMIDIIn(uid, index);
}

static void midiDisconnectInput_(Thread& th, Prim* prim)
{
	int index = (int)th.popInt("midiDisconnectInput : port");
	int uid = (int)th.popInt("midiDisconnectInput : sourceUID");
	prDisconnectMIDIIn(uid, index);
}

static void midiDebug_(Thread& th, Prim* prim)
{
    gMidiDebug = th.popFloat("midiDebug : onoff") != 0.;
}

const Z kOneOver127 = 1./127.;
const Z kOneOver8191 = 1./8191.;

static void mctl1_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("mctl1 : hi");
	Z lo   = th.popFloat("mctl1 : lo");

	int cnum = th.popInt("mctl1 : ctlNum") & 127;
	int chan = (th.popInt("mctl1 : chan") - 1) & 15;
	int srcIndex = th.popInt("mctl1 : srcIndex") & 15;
	Z z = kOneOver127 * gMidiState[srcIndex][chan].control[cnum];
	th.push(lo + z * (hi - lo));
}

static void xmctl1_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("xmctl1 : hi");
	Z lo   = th.popFloat("xmctl1 : lo");

	int cnum = th.popInt("xmctl1 : ctlNum") & 127;
	int chan = (th.popInt("xmctl1 : chan") - 1) & 15;
	int srcIndex = th.popInt("mctl1 : srcIndex") & 15;
	Z z = kOneOver127 * gMidiState[srcIndex][chan].control[cnum];
	th.push(lo * pow(hi / lo, z));
}

static void mpoly1_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("mpoly1 : hi");
	Z lo   = th.popFloat("mpoly1 : lo");

	int key = th.popInt("mpoly1 : key") & 127;
	int chan = (th.popInt("mpoly1 : chan") - 1) & 15;
	int srcIndex = th.popInt("mpoly1 : srcIndex") & 15;

	Z z = kOneOver127 * gMidiState[srcIndex][chan].polytouch[key];
	th.push(lo + z * (hi - lo));
}

static void xmpoly1_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("xmpoly1 : hi");
	Z lo   = th.popFloat("xmpoly1 : lo");

	int key = th.popInt("xmpoly1 : key") & 127;
	int chan = (th.popInt("xmpoly1 : chan") - 1) & 15;
	int srcIndex = th.popInt("xmpoly1 : srcIndex") & 15;

	Z z = kOneOver127 * gMidiState[srcIndex][chan].polytouch[key];
	th.push(lo * pow(hi / lo, z));
}

static void mgate1_(Thread& th, Prim* prim)
{
	int key = th.popInt("mgate1 : key") & 127;
	int chan = (th.popInt("mgate1 : chan") - 1) & 15;
	int srcIndex = th.popInt("mgate1 : srcIndex") & 15;

	th.pushBool(gMidiState[srcIndex][chan].keyvel[key] > 0);
}

static void mtouch1_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("mtouch1 : hi");
	Z lo   = th.popFloat("mtouch1 : lo");

	int chan = (th.popInt("mtouch1 : chan") - 1) & 15;
	int srcIndex = th.popInt("mtouch1 : srcIndex") & 15;

	Z z = kOneOver127 * gMidiState[srcIndex][chan].touch;
	th.push(lo + z * (hi - lo));
}

static void xmtouch1_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("xmtouch1 : hi");
	Z lo   = th.popFloat("xmtouch1 : lo");

	int chan = (th.popInt("xmtouch1 : chan") - 1) & 15;
	int srcIndex = th.popInt("xmtouch1 : srcIndex") & 15;

	Z z = kOneOver127 * gMidiState[srcIndex][chan].touch;
	th.push(lo * pow(hi / lo, z));
}

static void mprog1_(Thread& th, Prim* prim)
{
	int chan = (th.popInt("mprog1 : chan") - 1) & 15;
	int srcIndex = th.popInt("mprog1 : srcIndex") & 15;

	th.push(gMidiState[srcIndex][chan].touch);
}

static void mlastkey1_(Thread& th, Prim* prim)
{
	int chan = (th.popInt("mlastkey1 : chan") - 1) & 15;
	int srcIndex = th.popInt("mlastkey1 : srcIndex") & 15;

	th.push(gMidiState[srcIndex][chan].lastkey);
}

static void mlastvel1_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("mlastvel1 : hi");
	Z lo   = th.popFloat("mlastvel1 : lo");

	int chan = (th.popInt("mlastvel1 : chan") - 1) & 15;
	int srcIndex = th.popInt("mlastvel1 : srcIndex") & 15;

	Z z = kOneOver127 * gMidiState[srcIndex][chan].lastvel;
	th.push(lo + z * (hi - lo));
}

static void xmlastvel1_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("xmlastvel1 : hi");
	Z lo   = th.popFloat("xmlastvel1 : lo");

	int chan = (th.popInt("xmlastvel1 : chan") - 1) & 15;
	int srcIndex = th.popInt("xmlastvel1 : srcIndex") & 15;

	Z z = kOneOver127 * gMidiState[srcIndex][chan].lastvel;
	th.push(lo * pow(hi / lo, z));
}

static void mbend1_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("mbend1 : hi");
	Z lo   = th.popFloat("mbend1 : lo");

	int chan = (th.popInt("mbend1 : chan") - 1) & 15;
	int srcIndex = th.popInt("mbend1 : srcIndex") & 15;

	Z z = kOneOver8191 * gMidiState[srcIndex][chan].bend;
	th.push(lo + z * (hi - lo));
}
static void xmbend1_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("xmbend1 : hi");
	Z lo   = th.popFloat("xmbend1 : lo");

	int chan = (th.popInt("mbend1 : chan") - 1) & 15;
	int srcIndex = th.popInt("xmbend1 : srcIndex") & 15;

	Z z = kOneOver8191 * gMidiState[srcIndex][chan].bend;
	th.push(lo * pow(hi / lo, z));
}

Z gMidiLagTime = .1;
Z gMidiLagMul = log001 / gMidiLagTime;

struct MCtl : public TwoInputUGen<MCtl>
{
	Z _b1;
	Z _y1;
	Z _lagmul;
    int _srcIndex;
    int _chan;
    int _cnum;

	MCtl(Thread& th, int srcIndex, int chan, int cnum, Arg lo, Arg hi)
        : TwoInputUGen<MCtl>(th, lo, hi), _b1(1. + gMidiLagMul * th.rate.invSampleRate),
        _srcIndex(srcIndex), _chan(chan), _cnum(cnum)
	{
	}

	virtual const char* TypeName() const override { return "MCtl"; }

	void calc(int n, Z* out, Z* lo, Z* hi, int loStride, int hiStride)
	{
		Z y1 = _y1;
		Z b1 = _b1;
        uint8_t& ctl = gMidiState[_srcIndex][_chan].control[_cnum];
		for (int i = 0; i < n; ++i) {
            Z z = kOneOver127 * ctl;
			Z y0 = *lo + z * (*hi - *lo);
			out[i] = y1 = y0 + b1 * (y1 - y0);
			lo += loStride;
			hi += hiStride;
		}
		_y1 = y1;
	}
};

struct XMCtl : public TwoInputUGen<XMCtl>
{
	Z _b1;
	Z _y1;
	Z _lagmul;
    int _srcIndex;
    int _chan;
    int _cnum;

	XMCtl(Thread& th, int srcIndex, int chan, int cnum, Arg lo, Arg hi)
        : TwoInputUGen<XMCtl>(th, lo, hi), _b1(1. + gMidiLagMul * th.rate.invSampleRate),
        _srcIndex(srcIndex), _chan(chan), _cnum(cnum)
	{
	}

	virtual const char* TypeName() const override { return "XMCtl"; }

	void calc(int n, Z* out, Z* lo, Z* hi, int loStride, int hiStride)
	{
		Z y1 = _y1;
		Z b1 = _b1;
        uint8_t& ctl = gMidiState[_srcIndex][_chan].control[_cnum];
		for (int i = 0; i < n; ++i) {
            Z z = kOneOver127 * ctl;
			Z y0 = *lo * pow(*hi / *lo, z);
			out[i] = y1 = y0 + b1 * (y1 - y0);
			lo += loStride;
			hi += hiStride;
		}
		_y1 = y1;
	}
};

struct MPoly : public TwoInputUGen<MPoly>
{
	Z _b1;
	Z _y1;
	Z _lagmul;
    int _srcIndex;
    int _chan;
    int _cnum;

	MPoly(Thread& th, int srcIndex, int chan, int cnum, Arg lo, Arg hi)
        : TwoInputUGen<MPoly>(th, lo, hi), _b1(1. + gMidiLagMul * th.rate.invSampleRate),
        _srcIndex(srcIndex), _chan(chan), _cnum(cnum)
	{
	}

	virtual const char* TypeName() const override { return "MPoly"; }

	void calc(int n, Z* out, Z* lo, Z* hi, int loStride, int hiStride)
	{
		Z y1 = _y1;
		Z b1 = _b1;
        uint8_t& ctl = gMidiState[_srcIndex][_chan].polytouch[_cnum];
		for (int i = 0; i < n; ++i) {
            Z z = kOneOver127 * ctl;
			Z y0 = *lo + z * (*hi - *lo);
			out[i] = y1 = y0 + b1 * (y1 - y0);
			lo += loStride;
			hi += hiStride;
		}
		_y1 = y1;
	}
};

struct XMPoly : public TwoInputUGen<XMPoly>
{
	Z _b1;
	Z _y1;
	Z _lagmul;
    int _srcIndex;
    int _chan;
    int _cnum;

	XMPoly(Thread& th, int srcIndex, int chan, int cnum, Arg lo, Arg hi)
        : TwoInputUGen<XMPoly>(th, lo, hi), _b1(1. + gMidiLagMul * th.rate.invSampleRate),
        _srcIndex(srcIndex), _chan(chan), _cnum(cnum)
	{
	}

	virtual const char* TypeName() const override { return "XMPoly"; }

	void calc(int n, Z* out, Z* lo, Z* hi, int loStride, int hiStride)
	{
		Z y1 = _y1;
		Z b1 = _b1;
        uint8_t& ctl = gMidiState[_srcIndex][_chan].polytouch[_cnum];
		for (int i = 0; i < n; ++i) {
            Z z = kOneOver127 * ctl;
			Z y0 = *lo * pow(*hi / *lo, z);
			out[i] = y1 = y0 + b1 * (y1 - y0);
			lo += loStride;
			hi += hiStride;
		}
		_y1 = y1;
	}
};

struct MTouch : public TwoInputUGen<MTouch>
{
	Z _b1;
	Z _y1;
	Z _lagmul;
    int _srcIndex;
    int _chan;

	MTouch(Thread& th, int srcIndex, int chan, Arg lo, Arg hi)
        : TwoInputUGen<MTouch>(th, lo, hi), _b1(1. + gMidiLagMul * th.rate.invSampleRate),
        _srcIndex(srcIndex), _chan(chan)
	{
	}

	virtual const char* TypeName() const override { return "MTouch"; }

	void calc(int n, Z* out, Z* lo, Z* hi, int loStride, int hiStride)
	{
		Z y1 = _y1;
		Z b1 = _b1;
        uint8_t& ctl = gMidiState[_srcIndex][_chan].touch;
		for (int i = 0; i < n; ++i) {
            Z z = kOneOver127 * ctl;
			Z y0 = *lo + z * (*hi - *lo);
			out[i] = y1 = y0 + b1 * (y1 - y0);
			lo += loStride;
			hi += hiStride;
		}
		_y1 = y1;
	}
};

struct XMTouch : public TwoInputUGen<XMTouch>
{
	Z _b1;
	Z _y1;
	Z _lagmul;
    int _srcIndex;
    int _chan;

	XMTouch(Thread& th, int srcIndex, int chan, Arg lo, Arg hi)
        : TwoInputUGen<XMTouch>(th, lo, hi), _b1(1. + gMidiLagMul * th.rate.invSampleRate),
        _srcIndex(srcIndex), _chan(chan)
	{
	}

	virtual const char* TypeName() const override { return "XMTouch"; }

	void calc(int n, Z* out, Z* lo, Z* hi, int loStride, int hiStride)
	{
		Z y1 = _y1;
		Z b1 = _b1;
        uint8_t& ctl = gMidiState[_srcIndex][_chan].touch;
		for (int i = 0; i < n; ++i) {
            Z z = kOneOver127 * ctl;
			Z y0 = *lo * pow(*hi / *lo, z);
			out[i] = y1 = y0 + b1 * (y1 - y0);
			lo += loStride;
			hi += hiStride;
		}
		_y1 = y1;
	}
};

struct MBend : public TwoInputUGen<MBend>
{
	Z _b1;
	Z _y1;
	Z _lagmul;
    int _srcIndex;
    int _chan;

	MBend(Thread& th, int srcIndex, int chan, Arg lo, Arg hi)
        : TwoInputUGen<MBend>(th, lo, hi), _b1(1. + gMidiLagMul * th.rate.invSampleRate),
        _srcIndex(srcIndex), _chan(chan)
	{
	}

	virtual const char* TypeName() const override { return "MBend"; }

	void calc(int n, Z* out, Z* lo, Z* hi, int loStride, int hiStride)
	{
		Z y1 = _y1;
		Z b1 = _b1;
        uint16_t& ctl = gMidiState[_srcIndex][_chan].bend;
		for (int i = 0; i < n; ++i) {
            Z z = kOneOver8191 * ctl;
			Z y0 = *lo + z * (*hi - *lo);
			out[i] = y1 = y0 + b1 * (y1 - y0);
			lo += loStride;
			hi += hiStride;
		}
		_y1 = y1;
	}
};

struct XMBend : public TwoInputUGen<XMBend>
{
	Z _b1;
	Z _y1;
	Z _lagmul;
    int _srcIndex;
    int _chan;

	XMBend(Thread& th, int srcIndex, int chan, Arg lo, Arg hi)
        : TwoInputUGen<XMBend>(th, lo, hi), _b1(1. + gMidiLagMul * th.rate.invSampleRate),
        _srcIndex(srcIndex), _chan(chan)
	{
	}

	virtual const char* TypeName() const override { return "XMBend"; }

	void calc(int n, Z* out, Z* lo, Z* hi, int loStride, int hiStride)
	{
		Z y1 = _y1;
		Z b1 = _b1;
        uint16_t& ctl = gMidiState[_srcIndex][_chan].bend;
		for (int i = 0; i < n; ++i) {
            Z z = kOneOver8191 * ctl;
			Z y0 = *lo * pow(*hi / *lo, z);
			out[i] = y1 = y0 + b1 * (y1 - y0);
			lo += loStride;
			hi += hiStride;
		}
		_y1 = y1;
	}
};

struct MLastVel : public TwoInputUGen<MLastVel>
{
	Z _b1;
	Z _y1;
	Z _lagmul;
    int _srcIndex;
    int _chan;

	MLastVel(Thread& th, int srcIndex, int chan, Arg lo, Arg hi)
        : TwoInputUGen<MLastVel>(th, lo, hi), _b1(1. + gMidiLagMul * th.rate.invSampleRate),
        _srcIndex(srcIndex), _chan(chan)
	{
	}

	virtual const char* TypeName() const override { return "MLastVel"; }

	void calc(int n, Z* out, Z* lo, Z* hi, int loStride, int hiStride)
	{
		Z y1 = _y1;
		Z b1 = _b1;
        uint8_t& ctl = gMidiState[_srcIndex][_chan].lastvel;
		for (int i = 0; i < n; ++i) {
            Z z = kOneOver127 * ctl;
			Z y0 = *lo + z * (*hi - *lo);
			out[i] = y1 = y0 + b1 * (y1 - y0);
			lo += loStride;
			hi += hiStride;
		}
		_y1 = y1;
	}
};

struct XMLastVel : public TwoInputUGen<XMLastVel>
{
	Z _b1;
	Z _y1;
	Z _lagmul;
    int _srcIndex;
    int _chan;

	XMLastVel(Thread& th, int srcIndex, int chan, Arg lo, Arg hi)
        : TwoInputUGen<XMLastVel>(th, lo, hi), _b1(1. + gMidiLagMul * th.rate.invSampleRate),
        _srcIndex(srcIndex), _chan(chan)
	{
	}

	virtual const char* TypeName() const override { return "XMLastVel"; }

	void calc(int n, Z* out, Z* lo, Z* hi, int loStride, int hiStride)
	{
		Z y1 = _y1;
		Z b1 = _b1;
        uint8_t& ctl = gMidiState[_srcIndex][_chan].lastvel;
		for (int i = 0; i < n; ++i) {
            Z z = kOneOver127 * ctl;
			Z y0 = *lo * pow(*hi / *lo, z);
			out[i] = y1 = y0 + b1 * (y1 - y0);
			lo += loStride;
			hi += hiStride;
		}
		_y1 = y1;
	}
};

struct MLastKey : public ZeroInputUGen<MLastKey>
{
    int _srcIndex;
    int _chan;

	MLastKey(Thread& th, int srcIndex, int chan)
        : ZeroInputUGen<MLastKey>(th, false),
        _srcIndex(srcIndex), _chan(chan)
	{
	}

	virtual const char* TypeName() const override { return "MLastKey"; }

	void calc(int n, Z* out)
	{
        uint8_t& ctl = gMidiState[_srcIndex][_chan].lastkey;
		for (int i = 0; i < n; ++i) {
			out[i] = ctl;
		}
	}
};

struct MProg : public ZeroInputUGen<MProg>
{
    int _srcIndex;
    int _chan;

	MProg(Thread& th, int srcIndex, int chan)
        : ZeroInputUGen<MProg>(th, false),
        _srcIndex(srcIndex), _chan(chan)
	{
	}

	virtual const char* TypeName() const override { return "MProg"; }

	void calc(int n, Z* out)
	{
        uint8_t& ctl = gMidiState[_srcIndex][_chan].program;
		for (int i = 0; i < n; ++i) {
			out[i] = ctl;
		}
	}
};

struct MGate : public ZeroInputUGen<MGate>
{
    int _srcIndex;
    int _chan;
    int _key;

	MGate(Thread& th, int srcIndex, int chan, int key)
        : ZeroInputUGen<MGate>(th, false),
        _srcIndex(srcIndex), _chan(chan), _key(key)
	{
	}

	virtual const char* TypeName() const override { return "MGate"; }

	void calc(int n, Z* out)
	{
        uint8_t& ctl = gMidiState[_srcIndex][_chan].keyvel[_key];
		for (int i = 0; i < n; ++i) {
			out[i] = ctl > 0 ? 1. : 0.;
		}
	}
};

struct ZCtl : public ZeroInputUGen<ZCtl>
{
	Z _b1;
	Z _y1;
	Z _lagmul;
    P<ZRef> zref;

	ZCtl(Thread& th, P<ZRef> const& inZRef)
        : ZeroInputUGen<ZCtl>(th, false), _b1(1. + gMidiLagMul * th.rate.invSampleRate),
        zref(inZRef)
	{
	}

	virtual const char* TypeName() const override { return "ZCtl"; }

	void calc(int n, Z* out)
	{
		Z y1 = _y1;
		Z b1 = _b1;
        Z& ctl = zref->z;
		for (int i = 0; i < n; ++i) {
			Z y0 = ctl;
			out[i] = y1 = y0 + b1 * (y1 - y0);
		}
		_y1 = y1;
	}
};

static void zctl_(Thread& th, Prim* prim)
{
	P<ZRef> zref = th.popZRef("mctl : zref");

	th.push(new List(new ZCtl(th, zref)));
}

static void mctl_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("mctl : hi");
	Z lo   = th.popFloat("mctl : lo");

	int cnum = th.popInt("mctl : ctlNum") & 127;
	int chan = (th.popInt("mctl : chan") - 1) & 15;
	int srcIndex = th.popInt("mctl : srcIndex") & 15;
	th.push(new List(new MCtl(th, srcIndex, chan, cnum, lo, hi)));
}

static void xmctl_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("xmctl : hi");
	Z lo   = th.popFloat("xmctl : lo");

	int cnum = th.popInt("xmctl : ctlNum") & 127;
	int chan = (th.popInt("xmctl : chan") - 1) & 15;
	int srcIndex = th.popInt("mctl : srcIndex") & 15;
	th.push(new List(new XMCtl(th, srcIndex, chan, cnum, lo, hi)));
}

static void mpoly_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("mpoly : hi");
	Z lo   = th.popFloat("mpoly : lo");

	int key = th.popInt("mpoly : key") & 127;
	int chan = (th.popInt("mpoly : chan") - 1) & 15;
	int srcIndex = th.popInt("mctl : srcIndex") & 15;
	th.push(new List(new MPoly(th, srcIndex, chan, key, lo, hi)));
}

static void xmpoly_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("xmpoly : hi");
	Z lo   = th.popFloat("xmpoly : lo");

	int key = th.popInt("xmpoly : key") & 127;
	int chan = (th.popInt("xmpoly : chan") - 1) & 15;
	int srcIndex = th.popInt("mctl : srcIndex") & 15;
	th.push(new List(new XMPoly(th, srcIndex, chan, key, lo, hi)));
}

static void mtouch_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("mtouch : hi");
	Z lo   = th.popFloat("mtouch : lo");

	int chan = (th.popInt("mtouch : chan") - 1) & 15;
	int srcIndex = th.popInt("mctl : srcIndex") & 15;
	th.push(new List(new MTouch(th, srcIndex, chan, lo, hi)));
}

static void xmtouch_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("xmtouch : hi");
	Z lo   = th.popFloat("xmtouch : lo");

	int chan = (th.popInt("xmtouch : chan") - 1) & 15;
	int srcIndex = th.popInt("mctl : srcIndex") & 15;
	th.push(new List(new XMTouch(th, srcIndex, chan, lo, hi)));
}

static void mbend_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("mbend : hi");
	Z lo   = th.popFloat("mbend : lo");

	int chan = (th.popInt("mbend : chan") - 1) & 15;
	int srcIndex = th.popInt("mctl : srcIndex") & 15;
	th.push(new List(new MBend(th, srcIndex, chan, lo, hi)));
}

static void xmbend_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("xmbend : hi");
	Z lo   = th.popFloat("xmbend : lo");

	int chan = (th.popInt("xmbend : chan") - 1) & 15;
	int srcIndex = th.popInt("mctl : srcIndex") & 15;
	th.push(new List(new XMBend(th, srcIndex, chan, lo, hi)));
}

static void mprog_(Thread& th, Prim* prim)
{
	int chan = (th.popInt("mprog : chan") - 1) & 15;
	int srcIndex = th.popInt("mctl : srcIndex") & 15;
	th.push(new List(new MProg(th, srcIndex, chan)));
}

static void mgate_(Thread& th, Prim* prim)
{
	int key = th.popInt("mgate : key") & 127;
	int chan = (th.popInt("mgate : chan") - 1) & 15;
	int srcIndex = th.popInt("mctl : srcIndex") & 15;
	th.push(new List(new MGate(th, srcIndex, chan, key)));
}

static void mlastvel_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("mlastvel : hi");
	Z lo   = th.popFloat("mlastvel : lo");

	int chan = (th.popInt("mlastvel : chan") - 1) & 15;
	int srcIndex = th.popInt("mctl : srcIndex") & 15;
	th.push(new List(new MLastVel(th, srcIndex, chan, lo, hi)));
}

static void mlastkey_(Thread& th, Prim* prim)
{
	int chan = (th.popInt("mlastkey : chan") - 1) & 15;
	int srcIndex = th.popInt("mctl : srcIndex") & 15;
	th.push(new List(new MLastKey(th, srcIndex, chan)));
}

static void xmlastvel_(Thread& th, Prim* prim)
{
	Z hi   = th.popFloat("xmlastvel : hi");
	Z lo   = th.popFloat("xmlastvel : lo");

	int chan = (th.popInt("xmlastvel : chan") - 1) & 15;
	int srcIndex = th.popInt("mctl : srcIndex") & 15;
	th.push(new List(new XMLastVel(th, srcIndex, chan, lo, hi)));
}

#define DEF(NAME, TAKES, LEAVES, HELP) 	vm.def(#NAME, TAKES, LEAVES, NAME##_, HELP);
#define DEFMCX(NAME, N, HELP) 	vm.defmcx(#NAME, N, NAME##_, HELP);
#define DEFAM(NAME, MASK, HELP) 	vm.defautomap(#NAME, #MASK, NAME##_, HELP);

void AddMidiOps()
{
	vm.addBifHelp("\n*** MIDI control ***");
	DEF(midiStart, 0, 0, "(-->) start up MIDI services");
	DEF(midiRestart, 0, 0, "(-->) rescan MIDI services");
	DEF(midiStop, 0, 0, "(-->) stop MIDI services");
	DEF(midiList, 0, 0, "(-->) list MIDI endpoints");
	DEF(midiConnectInput, 2, 0, "(sourceUID index -->) connect a MIDI source");
	DEF(midiDisconnectInput, 2, 0, "(sourceUID index -->) disconnect a MIDI source");
	DEF(midiDebug, 1, 0, "(onoff -->) turn on or off midi input monitoring");

	vm.addBifHelp("\n*** MIDI instantaneous value ***");
	DEFMCX(mctl1, 5, "(srcIndex chan ctlnum lo hi --> out) value of midi controller mapped to the linear range [lo,hi].");
	DEFMCX(mpoly1, 5, "(srcIndex chan key lo hi --> out) value of midi poly key pressure mapped to the linear range [lo,hi].");
	DEFMCX(mtouch1, 4, "(srcIndex chan lo hi --> out) value of midi channel pressure mapped to the linear range [lo,hi].");
	DEFMCX(mbend1, 4, "(srcIndex chan lo hi --> out) value of midi pitch bend mapped to the linear range [lo,hi].");
	DEFMCX(mprog1, 2, "(srcIndex chan --> out) value of midi channel program 0-127.");
	DEFMCX(mgate1, 3, "(srcIndex chan key --> out) value of midi key state. 1 if key is down, 0 if key is up.");
	DEFMCX(mlastkey1, 2, "(srcIndex chan --> out) value of key of most recent midi note on.");
	DEFMCX(mlastvel1, 4, "(srcIndex chan lo hi --> out) value of velocity of most recent midi note on mapped to the linear range [lo,hi].");

	DEFMCX(xmctl1, 5, "(srcIndex chan ctlnum lo hi --> out) value of midi controller mapped to the exponential range [lo,hi].");
	DEFMCX(xmpoly1, 5, "(srcIndex chan key lo hi --> out) value of midi poly key pressure mapped to the exponential range [lo,hi].");
	DEFMCX(xmtouch1, 4, "(srcIndex chan lo hi --> out) value of midi channel pressure mapped to the exponential range [lo,hi].");
	DEFMCX(xmbend1, 4, "(srcIndex chan lo hi --> out) value of midi pitch bend mapped to the exponential range [lo,hi].");
	DEFMCX(xmlastvel1, 4, "(srcIndex chan lo hi --> out) value of velocity of most recent midi note on mapped to the exponential range [lo,hi].");

	vm.addBifHelp("\n*** MIDI control signal ***");
	DEFMCX(mctl, 5, "(srcIndex chan ctlnum lo hi --> out) signal of midi controller mapped to the linear range [lo,hi].");
	DEFMCX(mpoly, 5, "(srcIndex chan key lo hi --> out) signal of midi poly key pressure mapped to the linear range [lo,hi].");
	DEFMCX(mtouch, 4, "(srcIndex chan lo hi --> out) signal of midi channel pressure mapped to the linear range [lo,hi].");
	DEFMCX(mbend, 4, "(srcIndex chan lo hi --> out) signal of midi pitch bend mapped to the linear range [lo,hi].");
	DEFMCX(mlastkey, 2, "(srcIndex chan --> out) signal of key of most recent midi note on.");
	DEFMCX(mlastvel, 4, "(srcIndex chan lo hi --> out) signal of velocity of most recent midi note on mapped to the linear range [lo,hi].");

	DEFMCX(mprog, 2, "(srcIndex chan --> out) signal of midi channel program 0-127.");
	DEFMCX(mgate, 3, "(srcIndex chan key --> out) signal of midi key state. 1 if key is down, 0 if key is up.");

	DEFMCX(xmctl, 5, "(srcIndex chan ctlnum lo hi --> out) signal of midi controller mapped to the exponential range [lo,hi].");
	DEFMCX(xmpoly, 5, "(srcIndex chan key lo hi --> out) signal of midi poly key pressure mapped to the exponential range [lo,hi].");
	DEFMCX(xmtouch, 4, "(srcIndex chan lo hi --> out) signal of midi channel pressure mapped to the exponential range [lo,hi].");
	DEFMCX(xmbend, 4, "(srcIndex chan lo hi --> out) signal of midi pitch bend mapped to the exponential range [lo,hi].");
	DEFMCX(xmlastvel, 4, "(srcIndex chan lo hi --> out) signal of velocity of most recent midi note on mapped to the exponential range [lo,hi].");

	vm.addBifHelp("\n*** ZRef control signal ***");
	DEFMCX(zctl, 1, "(zref --> out) makes a smoothed control signal from a zref.");
}

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "VM.hpp"
#include "Parser.hpp"
#include "clz.hpp"
#include <string>
#include <unistd.h>

////////////////////////////////////////////////////////////////////////////////////////////////////////

// stack shufflers
#pragma mark STACK OPS

static void clear_(Thread& th, Prim* prim)
{
	th.clearStack();
}

static void cleard_(Thread& th, Prim* prim)
{
	V v = th.top();
	th.clearStack();
	th.push(v);
}

static void stackDepth_(Thread& th, Prim* prim)
{
	th.push(th.stackDepth());
}

static void ba_(Thread& th, Prim* prim)
{
	if (th.stackDepth() < 2)
		throw errStackUnderflow;
	V* sp = &th.top();
	V b = sp[0];
	sp[0] = sp[-1];
	sp[-1] = b;
}

static void bac_(Thread& th, Prim* prim)
{
	// swapd
	if (th.stackDepth() < 3)
		throw errStackUnderflow;
	V* sp = &th.top();
	V a = sp[-2];
	V b = sp[-1];
	V c = sp[0];
	sp[-2] = b;
	sp[-1] = a;
	sp[0] = c;
}

static void cab_(Thread& th, Prim* prim)
{
	// rrot
	if (th.stackDepth() < 3)
		throw errStackUnderflow;
	V* sp = &th.top();
	V a = sp[-2];
	V b = sp[-1];
	V c = sp[0];
	sp[-2] = c;
	sp[-1] = a;
	sp[0] = b;
}

static void bca_(Thread& th, Prim* prim)
{
	// rot
	if (th.stackDepth() < 3)
		throw errStackUnderflow;
	V* sp = &th.top();
	V a = sp[-2];
	V b = sp[-1];
	V c = sp[0];
	sp[-2] = b;
	sp[-1] = c;
	sp[0] = a;
}

static void cba_(Thread& th, Prim* prim)
{
	// reverse top 3
	if (th.stackDepth() < 3)
		throw errStackUnderflow;
	V* sp = &th.top();
	V a = sp[-2];
	V b = sp[-1];
	V c = sp[0];
	sp[-2] = c;
	sp[-1] = b;
	sp[0] = a;
}

static void aa_(Thread& th, Prim* prim)
{
	// dup
	V v = th.top();
	th.push(v);
}

static void aaa_(Thread& th, Prim* prim)
{
	// dup
	V v = th.top();
	th.push(v);
	th.push(v);
}

static void aba_(Thread& th, Prim* prim)
{
	// over
	if (th.stackDepth() < 2)
		throw errStackUnderflow;

	V* sp = &th.top();
	th.push(sp[-1]);
}

static void bab_(Thread& th, Prim* prim)
{
	// tuck
	if (th.stackDepth() < 2)
		throw errStackUnderflow;

	V* sp = &th.top();
	V a = sp[-1];
	V b = sp[0];
	th.push(b);

	sp[-1] = b;
	sp[0] = a;
}

static void aab_(Thread& th, Prim* prim)
{
	// tuck
	if (th.stackDepth() < 2)
		throw errStackUnderflow;

	V* sp = &th.top();
	V a = sp[-1];
	V b = sp[0];
	th.push(b);
	sp[0] = a;
}

static void aabb_(Thread& th, Prim* prim)
{
	// tuck
	if (th.stackDepth() < 2)
		throw errStackUnderflow;

	V* sp = &th.top();
	V a = sp[-1];
	V b = sp[0];
	th.push(b);
	sp[0] = a;
	th.push(b);
}

static void abab_(Thread& th, Prim* prim)
{
	if (th.stackDepth() < 2)
		throw errStackUnderflow;

	V* sp = &th.top();
	V a = sp[-1];
	V b = sp[0];
	th.push(a);
	th.push(b);
}

static void nip_(Thread& th, Prim* prim)
{
	if (th.stackDepth() < 2)
		throw errStackUnderflow;

	V* sp = &th.top();
	V b = sp[0];
	sp[-1] = b;
	th.pop();
}

static void pop_(Thread& th, Prim* prim)
{
	th.pop();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma mark INHERIT

static bool hasItem(int64_t size, Table** a, Table* item)
{
	for (int64_t i = 0; i<size; ++i) {
		if (a[i]->Identical(item))
			return true;
	}
	return false;
}

static void Envir_merge2(Thread& th, int64_t asize, Table** a, int64_t bsize, Table** b, int64_t& csize, Table** c0)
{

	Table** c = c0;
	Table** aend = a + asize;
	Table** bend = b + bsize;
	while (a < aend && b < bend) {
		if ((*a)->Identical(*b)) {
			*c++ = *a++;
			b++;
		} else if (!hasItem(bend-b-1, b+1, *a)) {
			*c++ = *a++;
		} else if (!hasItem(aend-a-1, a+1, *b)) {
			*c++ = *b++;
		} else {
			throw errInconsistentInheritance;
		}
	}
	while (a < aend) { *c++ = *a++; }
	while (b < bend) { *c++ = *b++; }
	csize = c - c0;
}

static int64_t Envir_toVec(O list, int64_t maxSize, Table** vec)
{
	int64_t i = 0;
	for (; list && i < maxSize-1;) {
		vec[i++] = ((Form*)list)->mTable();
		list = ((Form*)list)->mNextForm();
	}
	return i;
}

static P<Form> Envir_fromVec(int64_t size, Table** a)
{
	if (size == 0) return vm._ee;

	P<Form> list;
	for (int64_t i = size-1; i >= 0; --i) {
		list = consForm(a[i], list);
	}
	return list;
}

P<Form> linearizeInheritance(Thread& th, size_t numArgs, V* args)
{
	if (numArgs == 0) return vm._ee;
	if (numArgs == 1) {
		if (args[0].isForm()) {
			return (Form*)args[0].asObj();
		} else {
			return vm._ee;
		}
	}

	const size_t maxSize = 1024;
	Table* t[3][maxSize];

	int ai = 0;
	int bi = 1;
	int ci = 2;

	int64_t asize = Envir_toVec(args[0].asObj(), maxSize, t[ai]);
	for (size_t i = 1; i < numArgs; ++i) {
		int64_t bsize = Envir_toVec(args[i].asObj(), maxSize, t[bi]);
		int64_t csize;
		Envir_merge2(th, asize, t[ai], bsize, t[bi], csize, t[ci]);
		int temp = ci;
		ci = ai;
		ai = temp;
		asize = csize;
	}
	return Envir_fromVec(asize, t[ai]);
}

P<Form> asParent(Thread& th, V& v)
{
	P<Form> parent;
	if (v.isReal()) {
		parent = nullptr;
	} else if (v.isForm()) {
		if (v.o() == vm._ee()) parent = nullptr;
		else parent = (Form*)v.o();
	} else if (v.isFunOrPrim()) {
		SaveStack save(th);
		v.apply(th);

		size_t n = th.stackDepth();
		V* args = &th.top() - (n - 1);

		parent = linearizeInheritance(th, n, args);

		th.popn(n);
	} else if (v.isVList()) {
		if (!v.isFinite())
			indefiniteOp("", "{} : parent");

		P<Array> const& a = ((List*)v.o())->mArray;
		size_t n = a->size();
		parent = linearizeInheritance(th, n, a->v());
	} else {
		wrongType("new : parent", "Form, Fun or VList", v);
		return NULL; // never gets here, but otherwise gcc warns about parent uninitialized.
	}
	return parent;
}

struct Binding
{
	V key;
	BothIn value;
};

struct Bind : public Gen
{
	P<TableMap> mMap;
	P<Form> mParent;
	std::vector<Binding> _bindings;

	Bind(Thread& th, P<Form>& parent, P<List> const& bindings, bool inIsFinite)
		: Gen(th, itemTypeV, inIsFinite), mParent(parent)
	{
		int64_t m = bindings->length(th);
		for (int64_t i = 0; i+1 < m; i += 2) {
			Binding b;
			b.key = bindings->at(i);
			b.value.set(bindings->at(i+1));
			_bindings.push_back(b);
		}
	}

	const char* TypeName() const override { return "Bind"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		int n = framesToFill;
		for (int i = 0; i < n; ++i) {
			P<Form> e = consForm(new Table(mMap), mParent);

			int64_t m = _bindings.size();
			for (int64_t j = 0; j < m; ++j) {
				Binding& b = _bindings[j];
				V val;
				if (b.value.one(th, val)) {
					setDone();
					goto leave;
				}
				e->put(j, val); // ok, single threaded mutation
			}

			out[i] = e;
			--framesToFill;
		}
leave:
		produce(framesToFill);
	}
};

////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma mark REF OPS

static void ref_(Thread& th, Prim* prim)
{
	V value = th.pop();
	V ref = new Ref(value);
	th.push(ref);
}

static void zref_(Thread& th, Prim* prim)
{
	Z z = th.popFloat("zref : value");
	th.push(new ZRef(z));
}

static void set_(Thread& th, Prim* prim)
{
	V ref = th.pop();
	if (ref.isRef()) {
		V value = th.pop();
		((Ref*)ref.o())->set(value);
	} else if (ref.isZRef()) {
		Z value = th.popFloat("set : value");
		((ZRef*)ref.o())->set(value);
	} else if (ref.isPlug()) {
		V value = th.pop();
		((Plug*)ref.o())->setPlug(value);
	} else if (ref.isZPlug()) {
		V value = th.popZIn("set : value");
		((ZPlug*)ref.o())->setPlug(value);
	} else if (ref.isVList() && ref.isFinite()) {
		V value = th.pop();
		P<List> refList = ((List*)ref.o())->pack(th);
		P<Array> refArray = refList->mArray;
		V* refs = refArray->v();
		if (value.isVList() && value.isFinite()) {
			P<List> valueList = ((List*)value.o())->pack(th);
			P<Array> valueArray = valueList->mArray;
			V* vals = valueArray->v();
			size_t n = std::min(refArray->size(), valueArray->size());
			for (size_t i = 0; i < n; ++i) {
				SaveStack ss(th);
				th.push(vals[i]);
				th.push(refs[i]);
				set_(th, prim);
			}
		} else {
			size_t n = refArray->size();
			for (size_t i = 0; i < n; ++i) {
				SaveStack ss(th);
				th.push(value);
				th.push(refs[i]);
				set_(th, prim);
			}
		}
	} else {
		wrongType("set : ref", "Ref, ZRef, Plug or ZPlug", ref);
	}
}

static void get_(Thread& th, Prim* prim)
{
	V ref = th.pop();
	th.push(ref.deref());
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

// printing ops
#pragma mark PRINTING

static void pr_(Thread& th, Prim* prim)
{
	std::string s;
	th.pop().print(th, s);
	post("%s", s.c_str());
}

static void prdebug_(Thread& th, Prim* prim)
{
	std::string s;
	th.pop().printDebug(th, s);
	post("%s", s.c_str());
}

static void cr_(Thread& th, Prim* prim)
{
	post("\n");
}

static void tab_(Thread& th, Prim* prim)
{
	post("\t");
}

static void sp_(Thread& th, Prim* prim)
{
	post(" ");
}

static void prstk_(Thread& th, Prim* prim)
{
	post("stack : "); th.printStack(); post("\n");
}

static void printLength_(Thread& th, Prim* prim)
{
	th.push(vm.printLength);
}

static void printDepth_(Thread& th, Prim* prim)
{
	th.push(vm.printDepth);
}

static void printTotalItems_(Thread& th, Prim* prim)
{
	th.push(vm.printTotalItems);
}

static void setPrintLength_(Thread& th, Prim* prim)
{
	vm.printLength = (int)th.popInt("setPrintLength : length");
}

static void setPrintDepth_(Thread& th, Prim* prim)
{
	vm.printDepth = (int)th.popInt("setPrintDepth : depth");
}

static void setPrintTotalItems_(Thread& th, Prim* prim)
{
	vm.printTotalItems = (int)th.popInt("setPrintTotalItems : numItems");
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

// string ops
#pragma mark STRINGS

static void str_(Thread& th, Prim* prim)
{
	V v = th.pop();
	std::string s;
	v.print(th, s);
	th.push(new String(s.c_str()));
}

static void debugstr_(Thread& th, Prim* prim)
{
	V v = th.pop();
	std::string s;
	v.printDebug(th, s);
	th.push(new String(s.c_str()));
}

static void strcat_(Thread& th, Prim* prim)
{
	P<String> sep = th.popString("strcat : separator");
	P<List> list = th.popVList("strcat : list");
	if (!list->isFinite())
		indefiniteOp("strcat : list", "");

	std::string s;

	list = list->pack(th);
	P<Array> array = list->mArray;

	for (int i = 0; i < array->size(); ++i) {
		if (i != 0) s += sep->s;
		V v = array->at(i);
		v.print(th, s);
	}

	th.push(new String(s.c_str()));
}

static void strlines_(Thread& th, Prim* prim)
{
	P<List> list = th.popVList("strlines : list");
	if (!list->isFinite())
		indefiniteOp("strlines : list", "");

	std::string s;

	list = list->pack(th);
	P<Array> array = list->mArray;

	for (int i = 0; i < array->size(); ++i) {
		V v = array->at(i);
		v.print(th, s);
		s += "\n";
	}

	th.push(new String(s.c_str()));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

// loops
#pragma mark LOOPS

static void while_(Thread& th, Prim* prim)
{
	V body = th.pop();
	V test = th.pop();
	while (1) {
		{
			SaveStack ss(th);
			test.apply(th);
			if (th.pop().isTrue()) break;
		}
		{
			SaveStack ss(th);
			body.apply(th);
		}
	}
}

static void eachDoer(Thread& th, int level, uint32_t mask, BothIn& in, V& fun)
{
	int nextLevel = level - 1;
	if (level == 0) {
		while (1) {
			SaveStack ss(th);
			V v;
			if (in.one(th, v))
				return;

			th.push(v);
			fun.apply(th);
		}
	} else {
		int bit = 1 << level;

		while (1) {
			V argv;
			if (in.one(th, argv))
				return;

			bool isConstant =  !(argv.isList() && (mask & bit));

			if (isConstant) {
				SaveStack ss(th);
				th.push(argv);
				fun.apply(th);
			} else {
				BothIn subin;
				V v = argv;
				if (mask & bit) {
					if (v.isList() && !v.isFinite())
						indefiniteOp("do : list", "");

					subin.set(v);
				} else {
					subin.setConstant(v);
				}

				eachDoer(th, nextLevel, mask, subin, fun);
			}
		}
	}
};

static void do_(Thread& th, Prim* prim)
{
	V f = th.pop();
	V item = th.pop();

	if (item.isEachOp()) {
		P<EachOp> p = (EachOp*)item.o();
		if (!p->v.isFinite())
			indefiniteOp("do : list", "");

		BothIn in(p->v);
		int numLevels = p->mask <= 1 ? 0 : LOG2CEIL(p->mask) - 1;
		eachDoer(th, numLevels, p->mask, in, f);
	} else if (item.isList()) {

		P<List> s = (List*)item.o();
		if (!s->isFinite())
			indefiniteOp("do", "");

		if (s->isVList()) {
			VIn _a(s());
			while (1) {
				int n = kDefaultVBlockSize;
				int astride;
				V *a;
				if (_a(th, n,astride, a)) {
					break;
				} else {
					for (int i = 0; i < n; ++i) {
						SaveStack save(th);
						th.push(*a);
						a += astride;
						f.apply(th);
					}
					_a.advance(n);
				}
			}
		} else {
			ZIn _a(s());
			while (1) {
				int n = th.rate.blockSize;
				int astride;
				Z *a;
				if (_a(th, n,astride, a)) {
					break;
				} else {
					for (int i = 0; i < n; ++i) {
						SaveStack save(th);
						th.push(*a);
						a += astride;
						f.apply(th);
					}
					_a.advance(n);
				}
			}
		}
	} else {
		wrongType("do : list", "List", item);
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma mark CONDITIONALS

static void equals_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();
	th.pushBool(a.Equals(th, b));
}

static void less_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();
	th.pushBool(Compare(th, a, b) < 0);
}

static void greater_(Thread& th, Prim* prim)
{
	V b = th.pop();
	V a = th.pop();
	th.pushBool(Compare(th, a, b) > 0);
}

static void if_(Thread& th, Prim* prim)
{
	V elseCode = th.pop();
	V thenCode = th.pop();
	V test = th.pop();
	if (test.isTrue()) {
		thenCode.apply(th);
	} else {
		elseCode.apply(th);
	}
}

static void dip_(Thread& th, Prim* prim)
{
    V temp = th.pop();
    V fun = th.pop();
    fun.apply(th);
    th.push(temp);
}

static void not_(Thread& th, Prim* prim)
{
	V p = th.pop();
	th.pushBool(p.isFalse());
}

static void protect_(Thread& th, Prim* prim)
{
	V protectCode = th.pop();
	V tryCode = th.pop();

	try {
		tryCode.apply(th);
	} catch (...) {
		protectCode.apply(th);
		throw;
	}

	protectCode.apply(th);
}

static void try_(Thread& th, Prim* prim)
{
	V catchCode = th.pop();
	V tryCode = th.pop();
	try {
		tryCode.apply(th);
	} catch (...) {
		catchCode.apply(th);
		throw;
	}
}

static void throw_(Thread& th, Prim* prim)
{
	throw -1;
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma mark ENVIR OPS

static void inherit_(Thread& th, Prim* prim)
{
	V v = th.pop();
	th.push(asParent(th, v));
}

static void pushWorkspace_(Thread& th, Prim* prim)
{
	th.mWorkspace = consForm(new GTable(), th.mWorkspace);
}

static void popWorkspace_(Thread& th, Prim* prim)
{
    if (!th.mWorkspace->mNextForm()) {
        post("Must not pop top level workspace!\n");
        return;
    }
	th.mWorkspace = th.mWorkspace->mNextForm;
}

static void has_(Thread& th, Prim* prim)
{
	V key = th.pop();
	V list = th.pop();

	V value;
	bool has = list.get(th, key, value);
	th.pushBool(has);
}

static void keys_(Thread& th, Prim* prim)
{
	P<Table> t = th.popForm("keys : e")->mTable;

	P<Array> a = new Array(itemTypeV, t->mMap->mSize);

	V* keys = t->mMap->mKeys;
	for (size_t i = 0; i < t->mMap->mSize; ++i) {
		a->add(keys[i]);
	}

	th.push(new List(a));
}

static void values_(Thread& th, Prim* prim)
{
	P<Table> t = th.popForm("keys : e")->mTable;

	P<Array> a = new Array(itemTypeV, t->mMap->mSize);

	V* vals = t->mValues;
	for (size_t i = 0; i < t->mMap->mSize; ++i) {
		a->add(vals[i]);
	}

	th.push(new List(a));
}

static void kv_(Thread& th, Prim* prim)
{
	P<Table> t = th.popForm("values : e")->mTable;

	P<Array> ka = new Array(itemTypeV, t->mMap->mSize);
	P<Array> va = new Array(itemTypeV, t->mMap->mSize);

	V* keys = t->mMap->mKeys;
	V* vals = t->mValues;
	for (size_t i = 0; i < t->mMap->mSize; ++i) {
		ka->add(keys[i]);
		va->add(vals[i]);
	}

	th.push(new List(ka));
	th.push(new List(va));
}

static void local_(Thread& th, Prim* prim)
{
	P<Table> t = th.popForm("local : e")->mTable;

	th.push(new Form(t));
}

static void parent_(Thread& th, Prim* prim)
{
	P<Form> form = th.popForm("values : e");

	th.push(form->mNextForm ? form->mNextForm : vm._ee);
}

static void dot_(Thread& th, Prim* prim)
{
	V key = th.pop();
	V e = th.pop();

	if (!key.isVList()) {
		V v;
		e.dot(th, key, v);
		th.push(v);
	} else {
		if (!key.isFinite())
			indefiniteOp("dot : key", "");
		List* ks = (List*)key.o();
		ks = ks->pack(th);

		P<Array> ka = ks->mArray;
		int64_t size = ka->size();
		P<Array> va = new Array(itemTypeV, size);
		va->setSize(size);
		for (int64_t i = 0; i < size; ++i) {
			V v;
			e.dot(th, key, v);
			th.push(v);
		}
		th.push(new List(va));
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma mark APPLY

static void noeach_(Thread& th, Prim* prim)
{
	V fun = th.top();

	fun.SetNoEachOps();
}

static void apply_(Thread& th, Prim* prim)
{
	V v = th.pop();
	v.apply(th);
}

static void applyEvent_(Thread& th, Prim* prim)
{
	P<Fun> fun = th.popFun("!e : fun");
	P<Form> form = th.popForm("!e : form");

	for (auto const& name : fun->mDef->mArgNames) {
		V argValue;
		if (!form->dot(th, name, argValue)) {
			notFound(name);
		}
		th.push(argValue);
	}

	fun->apply(th);
}

static void type_(Thread& th, Prim* prim)
{
	th.push(getsym(th.pop().TypeName()));
}

static void load_(Thread& th, Prim* prim)
{
	P<String> filename = th.popString("load : filename");
	loadFile(th, filename->s);
}

static void compile_(Thread& th, Prim* prim)
{
	P<String> s = th.popString("compile : string");
	const char* ss = s->s;

	P<Fun> fun;
	if (!th.compile(ss, fun, false)) {
		th.push(0.);
	} else {
		th.push(fun);
	}
}

static void y_combinator_call_(Thread& th, Prim* prim)
{
	th.push(prim);
	prim->v.apply(th);
}

static void Y_(Thread& th, Prim* prim)
{
	V f = th.pop();
	if (f.takes() < 1) {
		post("Y : fun. function must take at least one argument.\n");
		throw errFailed;
	}
	th.push(new Prim(y_combinator_call_, f, f.takes()-1, f.leaves(), NULL, NULL));
}

static void* gofun(void* ptr)
{
    Thread* th = (Thread*)ptr;
    th->fun->run(*th);
    delete th;
    return NULL;
}

static void go_(Thread& th, Prim* prim)
{
    P<Fun> fun = th.popFun("go : fun");

    Thread* newThread = new Thread (th, fun);

    pthread_t pt;
    pthread_create(&pt, NULL, gofun, newThread);
}

static void sleep_(Thread& th, Prim* prim)
{
    Z t = th.popFloat("sleep : secs");

    usleep((useconds_t)floor(1e6 * t + .5));
}

#if COLLECT_MINFO
static void minfo_(Thread& th, Prim* prim)
{
	post("signal generators %qd\n", vm.totalSignalGenerators.load());
	post("stream generators %qd\n", vm.totalStreamGenerators.load());
	post("objects live %qd\n", vm.totalObjectsAllocated.load() - vm.totalObjectsFreed.load());
	post("objects allocated %qd\n", vm.totalObjectsAllocated.load());
	post("objects freed %qd\n", vm.totalObjectsFreed.load());
	post("retains %qd\n", vm.totalRetains.load());
	post("releases %qd\n", vm.totalReleases.load());
}
#endif

////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma mark SAMPLE RATES

static void sr_(Thread& th, Prim* prim)
{
	th.push(th.rate.sampleRate);
}

static void nyq_(Thread& th, Prim* prim)
{
	th.push(th.rate.sampleRate * .5);
}

static void isr_(Thread& th, Prim* prim)
{
	th.push(th.rate.invSampleRate);
}

static void rps_(Thread& th, Prim* prim)
{
	th.push(th.rate.radiansPerSample);
}

static void inyq_(Thread& th, Prim* prim)
{
	th.push(th.rate.invNyquistRate);
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma mark HELP

static void listdump_(Thread& th, Prim* prim)
{
	P<List> list = th.popList("listdump : seq");

	post("[\n");
	while (list()) {
		post("list %p %p %d\n", list(), list->mArray(), list->mArray() ? (int)list->mArray->size() : -1);
		list = list->next();
	}
	post("]\n");
}

static void help_(Thread& th, Prim* prim)
{
	V v = th.pop();

	const char* mask = v.GetAutoMapMask();
	const char* help = v.OneLineHelp();

	if (mask) {
		post("@%s ", mask);
	}
	if (help) {
		post("%s\n", help);
	} else {
		post("no help available.\n");
	}

}

static void helpbifs_(Thread& th, Prim* prim)
{
    post("\nBUILT IN FUNCTIONS\n\n");

	for (size_t i = 0; i < vm.bifHelp.size(); ++i) {
		std::string& s = vm.bifHelp[i];
		post(" %s\n", s.c_str());
	}
}

static void helpLine_(Thread& th, Prim* prim)
{
	P<String> str = th.popString("helpLine : string");
	vm.addUdfHelp(str->s);
}

static void helpudfs_(Thread& th, Prim* prim)
{
    post("\nUSER DEFINED FUNCTIONS\n\n");

	for (size_t i = 0; i < vm.udfHelp.size(); ++i) {
		std::string& s = vm.udfHelp[i];
		post(" %s\n", s.c_str());
	}
}

static void helpall_(Thread& th, Prim* prim)
{
    helpbifs_(th, prim);
    helpudfs_(th, prim);
}

static void prelude_(Thread& th, Prim* prim)
{
    static const size_t cmdMaxLen = 2048;
	char cmd[cmdMaxLen];

	if (vm.prelude_file) {
		snprintf(cmd, cmdMaxLen, "open %s\n", vm.prelude_file);
		system(cmd);
	} else {
		printf("no prelude file.\n");
	}
}

static void examples_(Thread& th, Prim* prim)
{
    static const size_t cmdMaxLen = 2048;
	char cmd[cmdMaxLen];

	const char* examples_file = getenv("SAPF_EXAMPLES");
	if (examples_file) {
		snprintf(cmd, cmdMaxLen, "open %s\n", examples_file);
		system(cmd);
	} else {
		printf("no examples file.\n");
	}
}

static void readme_(Thread& th, Prim* prim)
{
    static const size_t cmdMaxLen = 2048;
	char cmd[cmdMaxLen];

	const char* readme_file = getenv("SAPF_README");
	if (readme_file) {
		snprintf(cmd, cmdMaxLen, "open %s\n", readme_file);
		system(cmd);
	} else {
		printf("no readme file.\n");
	}
}

static void logfile_(Thread& th, Prim* prim)
{
    static const size_t cmdMaxLen = 2048;
	char cmd[cmdMaxLen];

	if (vm.log_file) {
		snprintf(cmd, cmdMaxLen, "open %s\n", vm.log_file);
		system(cmd);
	} else {
		printf("no log file.\n");
	}
}

static void trace_(Thread& th, Prim* prim)
{
	vm.traceon = th.pop().isTrue();
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma mark PLUGS

struct PlugOut : Gen
{
	P<Plug> _plug;

	PlugOut(Thread& th, P<Plug>& inPlug) : Gen(th, itemTypeV, false), _plug(inPlug)
	{
	}
	virtual const char* TypeName() const override { return "PlugOut"; }

	virtual void pull(Thread& th) override {
		VIn in;
		int changeCount;
		_plug->getPlug(in, changeCount);
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int astride;
			V *a;
			if (in(th, n, astride, a)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = *a;
					a += astride;
				}
				in.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
		_plug->setPlug(in, changeCount);
	}
};

struct ZPlugOut : Gen
{
	P<ZPlug> _plug;

	ZPlugOut(Thread& th, P<ZPlug>& inPlug) : Gen(th, itemTypeZ, false), _plug(inPlug)
	{
	}
	virtual const char* TypeName() const override { return "ZPlugOut"; }

	virtual void pull(Thread& th) override {
		ZIn in;
		int changeCount;
		_plug->getPlug(in, changeCount);
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int astride;
			Z *a;
			if (in(th, n, astride, a)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = *a;
					a += astride;
				}
				in.advance(n);
				framesToFill -= n;
				out += n;
			}
		}
		produce(framesToFill);
		_plug->setPlug(in, changeCount);
	}
};

static void plug_(Thread& th, Prim* prim)
{
	V in = th.pop();
	P<Plug> plug = new Plug(in);
	th.push(new List(new PlugOut(th, plug)));
	th.push(plug);
}

static void zplug_(Thread& th, Prim* prim)
{
	V value = th.pop();
	if (value.isVList() && value.isFinite()) {
		P<List> valueList = ((List*)value.o())->pack(th);
		P<Array> valueArray = valueList->mArray;
		V* vals = valueArray->v();
		size_t n = valueArray->size();

		P<List> plugList = new List(itemTypeV, n);
		P<List> outList = new List(itemTypeV, n);

		P<Array> plugArray = plugList->mArray;
		P<Array> outArray = outList->mArray;

		plugArray->setSize(n);
		outArray->setSize(n);

		V* plugItems = plugArray->v();
		V* outItems = outArray->v();

		for (size_t i = 0; i < n; ++i) {
			SaveStack ss(th);
			th.push(vals[i]);
			zplug_(th, prim);
			plugItems[i] = th.pop();
			outItems[i] = th.pop();
		}

		th.push(outList);
		th.push(plugList);
	} else if (value.isZIn()) {
		P<ZPlug> plug = new ZPlug(value);
		th.push(new List(new ZPlugOut(th, plug)));
		th.push(plug);
	} else {
		wrongType("zplug : ref", "VList or UGen input", value);
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma mark GLOB

#include <glob.h>

static void glob_(Thread& th, Prim* prim)
{
	P<String> pat = th.popString("glob : pattern");

	glob_t g;
	memset(&g, 0, sizeof(g));
	glob(pat->s, GLOB_MARK, nullptr, &g);

	P<Array> a = new Array(itemTypeV, g.gl_matchc);
	for (int i = 0; i < g.gl_matchc; ++i) {
		a->add(new String(g.gl_pathv[i]));
	}
	globfree(&g);

	th.push(new List(a));
}

////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma mark ADD CORE OPS

#define DEFN(NAME, N, FUN, HELP) 	vm.def(NAME, N, 1, FUN, HELP);
#define DEF(NAME, N, HELP) 	vm.def(#NAME, N, 1, NAME##_, HELP);
#define DEF2(NAME, TAKES, LEAVES, HELP) 	vm.def(#NAME, TAKES, LEAVES, NAME##_, HELP);
#define DEFnoeach(NAME, TAKES, LEAVES, HELP) 	vm.def(#NAME, TAKES, LEAVES, NAME##_, HELP, V(0.), true);
#define DEFAM(NAME, MASK, HELP) 	vm.defautomap(#NAME, #MASK, NAME##_, HELP);

void AddCoreOps();
void AddCoreOps()
{
	// stack ops
	vm.addBifHelp("\n*** stack ops ***");
	DEFnoeach(clear, 0, 0, "(... -->) clears everything off the stack.");
	DEFnoeach(cleard, 0, 1, "(... a --> a) clears all but the top item from the stack.")
	DEFnoeach(stackDepth, 0, 1, "(--> n) returns the size of the stack.")

	DEFnoeach(aa, 1, 2, "(a --> a a) push the top item on stack again.")
	DEFnoeach(aaa, 1, 3, "(a --> a a a) push the top item on stack two more times.")
	DEFnoeach(ba, 2, 2, "(a b --> b a) swap top two items.")

	DEFnoeach(bac, 3, 3, "(a b c --> b a c) reorder items on stack.")
	DEFnoeach(cba, 3, 3, "(a b c --> c b a) reorder items on stack.")
	DEFnoeach(bca, 3, 3, "(a b c --> b c a) reorder items on stack.")
	DEFnoeach(cab, 3, 3, "(a b c --> c a b) reorder items on stack.")

	DEFnoeach(bab, 2, 3, "(a b --> b a b) reorder items on stack.")
	DEFnoeach(aba, 2, 3, "(a b --> a b a) reorder items on stack.")

	DEFnoeach(aab, 2, 3, "(a b --> a a b) reorder items on stack.")
	DEFnoeach(aabb, 2, 4, "(a b --> a a b b) reorder items on stack.")
	DEFnoeach(abab, 2, 4, "(a b --> a b a b) reorder items on stack.")

	DEFnoeach(nip, 2, 1, "(a b --> b) remove second item on stack.")
	DEFnoeach(pop, 1, 0, "(a -->) remove top item on stack.")

	// loops
	vm.addBifHelp("\n*** loops ***");
	//DEFnoeach(while, 2, "(A B --> ..) While applying A returns true, apply B.")
	DEFnoeach(do, 2, 0, "(list \\item[..] -->) applies the function to each item of a finite list. Useful for side effects like printing or file writing.")

	// conditional ops
	vm.addBifHelp("\n*** conditional ops ***");
	DEF(equals, 2, "(a b --> bool) returns 1 if a and b are structurally equivalent. If the data structures are cyclic then this may never terminate.")
	DEF(less, 2, "(a b --> bool) returns 1 if a is less than b structurally. If the data structures are cyclic then this may never terminate.")
	DEF(greater, 2, "(a b --> bool) returns 1 if a is greater than b structurally. If the data structures are cyclic then this may never terminate.")
	DEF2(if, 3, -1, "(A B C --> ..) if A is true then apply B else apply C.")

	DEF(not, 1, "(A --> bool) returns 0 if A is true and 1 if A is false.")
	//DEF2(dip, 1, -1, "(x A --> ..) pops x from stack, applies A, pushes x back on stack.")

	DEFnoeach(try, 2, -1, "(A B --> ..) apply function A. if an exception is thrown, function B is applied.")
	DEFnoeach(throw, 0, 0, "(a -->) throw an exception.")
	DEFnoeach(protect, 2, -1, "(A B --> ..) apply function A. if an exception is thrown, function B is applied and the exception is rethrown. Otherwise function B is applied and control continues as normal.")

	// form ops
	vm.addBifHelp("\n*** form ops ***");
	DEFAM(has, kk, "(form key --> bool) return whether a form contains the key.")

	DEFAM(keys, k, "(form --> keys) return an array of the keys of the form.")
	DEFAM(values, k, "(form --> values) return an array of the values of the form.")
	DEFAM(kv, k, "(form --> keys values) return two arrays of the keys and values of the form.") /// !!!! returns two values. can't be auto mapped.
	DEFAM(local, k, "(form --> local) return the head of the prototype inheritance list.")
	DEFAM(parent, k, "(form --> parent) return the tail of the prototype inheritance list.")
	DEFAM(dot, ka, "(form key --> item) return the value for the key.")

    DEFnoeach(pushWorkspace, 0, 0, "(-->) pushes a new outer scope onto the workspace. New bindings will be made in the new outer scope.");
    DEFnoeach(popWorkspace, 0, 0, "(-->) pops a scope from the workspace. All bindings in the outer scope will be forgotten.");

	vm.addBifHelp("\n*** ref ops ***");
	DEFAM(get, k, "(r --> a) return the value store in a ref.")
	DEFnoeach(set, 1, 0, "(a r -->) store the value a in the ref r.")
	vm.def("R", 1, 1, ref_, "(a --> r) create a new Ref with the inital value a");
	vm.def("ZR", 1, 1, zref_, "(z --> r) create a new ZRef with the inital value z. A ZRefs is a mutable reference to a real number.");
	vm.def("P", 1, 2, plug_, "(a --> out in) create a new stream plug pair with the inital value a");
	vm.def("ZP", 1, 2, zplug_, "(a --> out in) create a new signal plug pair with the inital value a.");

	//DEF(bind, 2, "deprecated")

	// apply ops
	vm.addBifHelp("\n*** function ops ***");
	DEF(Y, 1, "(funA --> funB) Y combinator. funB calls funA with the last argument being funB itself. Currently the only way to do recursion. \n\t\te.g. \\x f [x 2 < \\[1] \\[ x x -- f *] if] Y = factorial    7 factorial --> 5040")
	DEF(noeach, 1, "(fun --> fun) sets a flag in the function so that it will pass through arguments with @ operators without mapping them.")
	vm.def("!", 1, -1, apply_, "(... f --> ...) apply the function to its arguments, observing @ arguments as appropriate.");
	vm.def("!e", 2, -1, applyEvent_, "(form fun --> ...) for each argument in the function, find the same named fields in the form and push those values as arguments to the function.");
	DEF(compile, 1, "(string --> fun) compile the string and return a function.")

	vm.addBifHelp("\n*** printing ops ***");
	DEFnoeach(printLength, 0, 1, "(--> length) return the number of items printed for lists.");
	DEFnoeach(printDepth, 0, 1, "(--> depth) return the number of levels of nesting printed for lists.");
	DEFnoeach(setPrintLength, 1, 0, "(length --> ) set the number of items printed for lists.");
	DEFnoeach(setPrintDepth, 1, 0, "(depth -->) set the number of levels of nesting printed for lists.");

	DEFnoeach(pr, 1, 0, "(A -->) print the top item on the stack. (no space or carriage return is printed)")
	DEFnoeach(prdebug, 1, 0, "(A -->) print debug version of the top item on the stack. (no space or carriage return is printed)")
	DEFnoeach(cr, 0, 0, "(-->) print a carriage return.")
	DEFnoeach(sp, 0, 0, "(-->) print a space character.")
	DEFnoeach(tab, 0, 0, "(-->) print a tab.")
	DEFnoeach(prstk, 0, 0, "(-->) print the stack.")

#if COLLECT_MINFO
	DEFnoeach(minfo, 0, 0, "(-->) print memory management info.")
#endif
	DEFnoeach(listdump, 1, 0, "(list -->) prints information about a list.");

	vm.addBifHelp("\n*** string ops ***");
	DEF(str, 1, "(x --> string) convert x to a string.");
	DEF(debugstr, 1, "(x --> string) convert x to a debug string.");
	DEFAM(strcat, ak, "(list separator --> string) convert elements of list to a string with separator string between each.");
	DEF(strlines, 1, "(list --> string) convert elements of list to a newline separated string.");
	DEFAM(glob, k, "(pattern --> paths) return a list of file path names that match.");

	vm.addBifHelp("\n*** sample rate ops ***");
	DEFnoeach(sr, 0, 1, "(--> sampleRate) returns the sample rate. samples per second. ")
	DEFnoeach(nyq, 0, 1, "(--> sampleRate/2) returns the nyquist rate")
	DEFnoeach(isr, 0, 1, "(--> 1/sampleRate) returns the inverse sample rate")
	DEFnoeach(inyq, 0, 1, "(--> 2/sampleRate) returns the inverse nyquist rate.")
	DEFnoeach(rps, 0, 1, "(--> 2pi/sampleRate) returns the radians per sample")

	vm.addBifHelp("\n*** help ops ***");

	DEFnoeach(help, 1, 0, "(fun -->) prints help for a function.");
	DEFnoeach(helpbifs, 0, 0, "(-->) prints help for all built in functions.");
	DEFnoeach(helpudfs, 0, 0, "(-->) prints help for all user defined functions.");
	DEFnoeach(helpall, 0, 0, "(-->) prints help for all built in and user defined functions.");
    DEF(helpLine, 1, "(string -->) add a line to the user defined function help.");

	vm.addBifHelp("\n*** thread ops ***");
    DEFnoeach(go, 1, 0, "(fun -->) launches the function in a new thread.");
    DEFnoeach(sleep, 1, 0, "(seconds -->) sleeps the current thread for the time given.");

	vm.addBifHelp("\n*** misc ***");
	DEF(type, 1, "(a --> symbol) return a symbol naming the type of the value a.")
	DEFnoeach(trace, 1, 0, "(bool -->) turn tracing on/off in the interpreter.")

	vm.addBifHelp("\n*** text files ***");
	DEFnoeach(load, 1, 0, "(filename -->) compiles and executes a text file.")
	DEFnoeach(prelude, 0, 0, "(-->) opens the prelude file in the default text editor.")
	DEFnoeach(examples, 0, 0, "(-->) opens the examples file in the default text editor.")
	DEFnoeach(logfile, 0, 0, "(-->) opens the log file in the default text editor.")
	DEFnoeach(readme, 0, 0, "(-->) opens the README file in the default text editor.")

}

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "VM.hpp"
#include "Opcode.hpp"
#include "Parser.hpp"
#include "MultichannelExpansion.hpp"
#include "elapsedTime.hpp"
#include <stdexcept>
#include <limits.h>

VM vm;

pthread_mutex_t gHelpMutex = PTHREAD_MUTEX_INITIALIZER;

std::atomic<int32_t> randSeedCounter = 77777;

uint64_t timeseed()
{
	struct timeval tv;
	gettimeofday(&tv, 0);
	int32_t counter = ++randSeedCounter;
	return Hash64(tv.tv_sec) + Hash64(tv.tv_usec) + Hash64(counter);
}

Thread::Thread()
    :rate(vm.ar), stackBase(0), localBase(0),
	mWorkspace(new GForm()),
    parsingWhat(parsingWords),
    fromString(false),
    line(NULL)
{
	rgen.init(timeseed());
}

Thread::Thread(const Thread& inParent)
    :rate(inParent.rate), stackBase(0), localBase(0),
    mWorkspace(inParent.mWorkspace),
    parsingWhat(parsingWords),
    fromString(false),
    line(NULL)
{
	rgen.init(timeseed());
}

Thread::Thread(const Thread& inParent, P<Fun> const& inFun)
    :rate(vm.ar), stackBase(0), localBase(0),
    fun(inFun),
    mWorkspace(inParent.mWorkspace),
    parsingWhat(parsingWords),
    fromString(false),
    line(NULL)
{
	rgen.init(timeseed());
}

Thread::~Thread() {}

//////////////////////

static void inherit_(Thread& th, Prim* prim)
{
	V vparent = th.pop();
	P<Form> form = asParent(th, vparent);
	th.push(form);
}

P<Form> extendFormByOne(Thread& th, P<Form> const& parent, P<TableMap> const& tmap, Arg value)
{
	P<Table> table = new Table(tmap);
	P<Form> form = new Form(table, parent);
	table->put(0, value);
	return form;
}

static void newForm_(Thread& th, Prim* prim)
{
	TableMap* tmap = (TableMap*)th.pop().o();
	size_t numArgs = tmap->mSize;

	V* args = &th.top() - numArgs + 1;

	P<Table> table = new Table(tmap);
	V vparent = args[-1];
	P<Form> parent = asParent(th, vparent);

	P<Form> form = new Form(table, parent);

	for (size_t i = 0; i < numArgs; ++i) {
		table->put(i, args[i]);
	}

	th.popn(numArgs+1);

	th.push(form);
}

static void newVList_(Thread& th, Prim* prim)
{
	size_t n = th.stackDepth();

	P<List> seq;
	if (n == 0) {
		seq = vm._nilv;
	} else {
		seq = new List(itemTypeV, n);
		V* ssp = &th.top() - n + 1;

		for (size_t i = 0; i < n; ++i) {
			seq->add(ssp[i]);
		}
	}

	th.popn(n);
	th.push(seq);
}

static void newZList_(Thread& th, Prim* prim)
{
	size_t n = th.stackDepth();

	P<List> seq;
	if (n == 0) {
		seq = vm._nilz;
	} else {
		seq = new List(itemTypeZ, n);
		V* ssp = &th.top() - n + 1;

		for (size_t i = 0; i < n; ++i) {
			seq->add(ssp[i]);
		}
	}

	th.popn(n);

	th.push(seq);
}

//////////////////////

VM::VM()
	:
	prelude_file(NULL),
	log_file(NULL),
	_ee(0),

	printLength(20),
	printDepth(8),

	ar(kDefaultSampleRate, kDefaultZBlockSize),
	kr(ar, kDefaultControlBlockSize),

	VblockSize(kDefaultVBlockSize),

#if COLLECT_MINFO
	totalRetains(0),
	totalReleases(0),
	totalObjectsAllocated(0),
	totalObjectsFreed(0),
	totalSignalGenerators(0),
	totalStreamGenerators(0)
#endif
{
	initElapsedTime();

	_ee = new Form(0, NULL);

	builtins = new GTable();

	// add built in funs

	_nilz = new List(itemTypeZ);
	_nilv = new List(itemTypeV);

	_anilz = _nilz->mArray;
	_anilv = _nilv->mArray;

	newForm = new Prim(newForm_, 0., 0, 1, NULL, NULL);
	inherit = new Prim(inherit_, 0., 0, 1, NULL, NULL);

	newVList = new Prim(newVList_, 0., 0, 1, NULL, NULL);
	newZList = new Prim(newZList_, 0., 0, 1, NULL, NULL);
}

VM::~VM()
{
}

#if USE_LIBEDIT
static const char* prompt(EditLine *e)
{
  return "sapf> ";
}
static const char* promptParen(EditLine *e)
{
  return "(sapf> ";
}
static const char* promptSquareBracket(EditLine *e)
{
  return "[sapf> ";
}
static const char* promptCurlyBracket(EditLine *e)
{
  return "{sapf> ";
}
static const char* promptLambda(EditLine *e)
{
  return "\\sapf> ";
}
static const char* promptString(EditLine *e)
{
  return "\"sapf> ";
}
#endif

void Thread::getLine()
{
	if (fromString) return;
	switch (parsingWhat) {
		default: case parsingWords : el_set(el, EL_PROMPT, &prompt); break;
		case parsingString : el_set(el, EL_PROMPT, &promptString); break;
		case parsingParens : el_set(el, EL_PROMPT, &promptParen); break;
		case parsingLambda : el_set(el, EL_PROMPT, &promptLambda); break;
		case parsingArray : el_set(el, EL_PROMPT, &promptSquareBracket); break;
		case parsingEnvir : el_set(el, EL_PROMPT, &promptCurlyBracket); break;
	}
	line = el_gets(el, &linelen);
	linepos = 0;
	if (strncmp(line, "quit", 4)==0 || strncmp(line, "..", 2)==0) { line = NULL; throw errUserQuit; }
	if (line && linelen) {
		history(myhistory, &ev, H_ENTER, line);
		history(myhistory, &ev, H_SAVE, historyfilename);
		if (logfilename) {
			FILE* logfile = fopen(logfilename, "a");
			logTimestamp(logfile);
			fwrite(line, 1, strlen(line), logfile);
			fclose(logfile);
		}
	}
}

void Thread::logTimestamp(FILE* logfile)
{
	timeval tv;
	gettimeofday(&tv, NULL);
	if (previousTimeStamp == 0 || tv.tv_sec - previousTimeStamp > 3600) {
		previousTimeStamp = tv.tv_sec;
		char date[32];
		ctime_r(&tv.tv_sec, date);
		fprintf(logfile, ";;;;;;;; %s", date);
		fflush(logfile);
	}
}

char Thread::getc() {
	if (fromString) {
		if (line == NULL) return 0;
		return line[linepos++];
	} else {
		while (1) {
			if (line == NULL) {
				getLine();
				if (line == NULL || linelen == 0) return 0;
			} else if (line[linepos] == 0) {
				if (parsingWhat == parsingWords) {
					return 0;
				} else {
					getLine();
					if (linelen == 0 || strcmp(line, "\n") == 0) {
						line = NULL;
						continue;
					}
					if (line == NULL || linelen == 0) return 0;
				}
			} else {
				return line[linepos++];
			}
		}
	}
	return 0; // never gets here, but compiler too dumb to figure this out.
}

void Thread::repl(FILE* infile, const char* inLogfilename)
{
	Thread& th = *this;

	logfilename = inLogfilename;

	previousTimeStamp = 0;

#if USE_LIBEDIT
	el = el_init("sc", stdin, stdout, stderr);
	el_set(el, EL_PROMPT, &prompt);
	el_set(el, EL_EDITOR, "emacs");
	el_set(el, EL_BIND, "-s", "\t", "    ", NULL);

	myhistory = history_init();
	if (myhistory == 0) {
		post("history could not be initialized\n");
		return;
	}

	const char* envHistoryFileName = getenv("SAPF_HISTORY");
	if (envHistoryFileName) {
		snprintf(historyfilename, PATH_MAX, "%s", envHistoryFileName);
	} else {
		const char* homeDir = getenv("HOME");
		snprintf(historyfilename, PATH_MAX, "%s/sapf-history.txt", homeDir);
	}
	history(myhistory, &ev, H_SETSIZE, 800);
	history(myhistory, &ev, H_LOAD, historyfilename);
	history(myhistory, &ev, H_SETUNIQUE, 1);
	el_set(el, EL_HIST, history, myhistory);
#endif

	fflush(infile);
	bool running = true;

	post("Type 'helpall' to get a list of all built-in functions.\n");
	post("Type 'quit' to quit.\n");

	do {
		try {
			if (stackDepth()) {
				printStack();
				post("\n");
			}
		} catch (int err) {
			if (err <= -1000 && err > -1000 - kNumErrors) {
				post("\nerror: %s\n", errString[-1000 - err]);
			} else {
				post("\nerror: %d\n", err);
			}
		} catch (std::bad_alloc& xerr) {
			post("\nnot enough memory\n");
		} catch (...) {
			post("\nunknown error\n");
		}

		try {
			// PARSE
			{
				P<Fun> compiledFun;
				if (compile(NULL, compiledFun, true)) {
				// EVAL
					compiledFun->runREPL(th);
				}
			}
		} catch (V& v) {
            post("error: ");
            v.print(th);
			post("\n");
		} catch (int err) {
			if (err == errUserQuit) {
				post("good bye\n");
				running = false;
			} else if (err <= -1000 && err > -1000 - kNumErrors) {
				post("error: %s\n", errString[-1000 - err]);
			} else {
				post("error: %d\n", err);
			}
		} catch (std::bad_alloc& xerr) {
			post("not enough memory\n");
		} catch (...) {
			post("unknown error\n");
		}

	} while (running);

#if USE_LIBEDIT
	history(myhistory, &ev, H_SAVE, historyfilename);
	history_end(myhistory);
	el_end(el);
#endif
}

template <typename T>
class AutoFree
{
	T* p;
public:
	AutoFree(T* _p) : p(_p) {}
	~AutoFree() { free(p); }

	T* operator()() { return p; }
	T* operator*() { return p; }
	T* operator->() { return p; }
};

void loadFile(Thread& th, const char* filename)
{
    post("loading file '%s'\n", filename);
	FILE* f = fopen(filename, "r");
	if (!f) {
		post("could not open '%s'\n", filename);
		return;
	}

	fseek(f, 0, SEEK_END);
	int64_t fileSize = ftell(f);
	fseek(f, 0, SEEK_SET);

	AutoFree<char> buf = (char*)malloc(fileSize + 1);
	const char* p = buf();
	fread(buf(), 1, fileSize, f);
	buf()[fileSize - 1] = 0;

	try {
		{
			P<Fun> compiledFun;
			if (th.compile(p, compiledFun, true)) {
				post("compiled OK.\n");
				compiledFun->run(th);
				post("done loading file\n");
			}
		}
    } catch (V& v) {
        post("error: ");
        v.print(th);
        post("\n");
	} catch (int err) {
		if (err <= -1000 && err > -1000 - kNumErrors) {
			post("error: %s\n", errString[-1000 - err]);
		} else {
			post("error: %d\n", err);
		}
	} catch (std::bad_alloc& xerr) {
		post("not enough memory\n");
	} catch (...) {
		post("unknown error\n");
	}
}

void Thread::printStack()
{
	bool between = false;
	size_t n = stackDepth();
	for (size_t i = 0; i < n; ++i) {
		V* s = &stack[stackBase+i];
		if (between) post(" ");
		else between = true;
		std::string cppstring;
		s->print(*this, cppstring);
		post("%s", cppstring.c_str());
	}
}

void Thread::printLocals()
{
	bool between = false;
	size_t n = numLocals();
	for (size_t i = 0; i < n; ++i) {
		V* s = &local[localBase+i];
		if (between) post(" ");
		else between = true;
		std::string cppstring;
		s->print(*this, cppstring);
		post("%s", cppstring.c_str());
	}
}

void VM::setSampleRate(double inSampleRate)
{
	ar = Rate(inSampleRate, ar.blockSize);
	kr = Rate(ar, kDefaultControlBlockSize);
}

V VM::def(Arg key, Arg value)
{
	builtins->putImpure(key, value);
    V dummy;
    assert(builtins->getInner(key, dummy));
	return value;
}

V VM::def(const char* name, Arg value)
{
	def(V(getsym(name)), value);
	return value;
}

V VM::def(const char* name, int takes, int leaves, PrimFun pf, const char* help, Arg value, bool setNoEach)
{
	V aPrim = new Prim(pf, value, takes, leaves, name, help);
	def(name, aPrim);

	if (setNoEach) aPrim.SetNoEachOps();

	if (help)
		addBifHelp(name, aPrim.GetAutoMapMask(), help);

	return aPrim;
}

V VM::defmcx(const char* name, int numArgs, PrimFun pf, const char* help, Arg value)
{
	V aPrim = new Prim(pf, value, numArgs, 1, name, help);
	aPrim = mcx(numArgs, aPrim, name, help);
	def(name, aPrim);

	addBifHelp(name, aPrim.GetAutoMapMask(), help);
	return aPrim;
}

V VM::defautomap(const char* name, const char* mask, PrimFun pf, const char* help, Arg value)
{
	int numArgs = (int)strlen(mask);
	V aPrim = new Prim(pf, value, numArgs, 1, name, help);
	aPrim = automap(mask, numArgs, aPrim, name, help);
	def(name, aPrim);

	addBifHelp(name, aPrim.GetAutoMapMask(), help);
	return aPrim;
}

#pragma mark STACK

#define POPREFTYPEDEF(TYPE) \
P<TYPE> Thread::pop##TYPE(const char* msg) \
{ \
	V v = pop(); \
    ApplyIfFun(v); \
	if (!v.is##TYPE()) wrongType(msg, #TYPE, v); \
	return reinterpret_cast<TYPE*> (v.o()); \
}

#define POPTYPEDEF(TYPE) \
P<TYPE> Thread::pop##TYPE(const char* msg) \
{ \
	V v = pop().deref(); \
    ApplyIfFun(v); \
	if (!v.is##TYPE()) wrongType(msg, #TYPE, v); \
	return reinterpret_cast<TYPE*> (v.o()); \
}

#define POPFUNTYPEDEF(TYPE) \
P<TYPE> Thread::pop##TYPE(const char* msg) \
{ \
	V v = pop().deref(); \
	if (!v.is##TYPE()) wrongType(msg, #TYPE, v); \
	return reinterpret_cast<TYPE*> (v.o()); \
}

POPREFTYPEDEF(Ref);
POPTYPEDEF(ZRef);
POPTYPEDEF(String);
POPTYPEDEF(List);
POPFUNTYPEDEF(Fun);
POPTYPEDEF(Form);

void Thread::ApplyIfFun(V& v)
{
    if (v.isFunOrPrim()) {
        SaveStack ss(*this);
        v.apply(*this);
        v = pop();
    }
}

V Thread::popZInList(const char* msg)
{
	V p = pop();
	if (p.isRef()) p = p.deref();
    ApplyIfFun(p);
	if (!p.isZIn() && !p.isVList()) {
		wrongType(msg, "Real or Signal or List of Reals or Signals", p);
	}
	return p;
}

V Thread::popZIn(const char* msg)
{
	V p = pop();
	if (p.isRef() || p.isZRef()) p = p.deref();
    ApplyIfFun(p);
	if (!p.isZIn()) {
		wrongType(msg, "Real or Signal", p);
	}
	return p;
}

V Thread::popValue()
{
	V p = pop().deref();
    ApplyIfFun(p);
	return p;
}

P<List> Thread::popVList(const char* msg)
{
	V v = pop().deref();
    ApplyIfFun(v);
	if (!v.isVList()) {
		wrongType(msg, "Stream", v);
	}
	return (List*)v.o();
}

P<List> Thread::popZList(const char* msg)
{
	V v = pop().deref();
    ApplyIfFun(v);
	if (!v.isZList()) {
		wrongType(msg, "Signal", v);
	}
	return (List*)v.o();
}

int64_t Thread::popInt(const char* msg)
{
	V v = pop().deref();
    ApplyIfFun(v);
	if (!v.isReal()) wrongType(msg, "Real", v);

	double f = v.f;
	int64_t i;
	if (f >= (double)LLONG_MAX) i = LLONG_MAX;
	else if (f <= (double)LLONG_MIN) i = LLONG_MIN;
	else i = (int64_t)f;
	return i;
}

double Thread::popFloat(const char* msg)
{
	V v = pop().deref();
    ApplyIfFun(v);
	if (!v.isReal()) wrongType(msg, "Float", v);
	return v.f;
}

void Thread::tuck(size_t n, Arg v)
{
	stack.push_back(V(0.));
	V* sp = &stack.back();

	for (size_t i = 0; i < n; ++i)
		sp[-i] = sp[-i-1];

	sp[-n] = v;
}

///////////////////////////////////////

bool Thread::compile(const char* inString, P<Fun>& compiledFun, bool inTopLevel)
{
	Thread& th = *this;
	SaveCompileScope scs(th);
	if (inTopLevel) {
		mCompileScope = new TopCompileScope();
	} else {
		mCompileScope = new InnerCompileScope(new TopCompileScope());
	}

	P<Code> code;
	setParseString(inString);
	getLine();
	bool ok = parseElems(th, code);
	if (!ok || !code) {
		post("parse error. %d\n", ok);
		return false;
	}

	compiledFun = new Fun(*this, new FunDef(*this, code, 0, th.mCompileScope->numLocals(), th.mCompileScope->numVars(), NULL));

	return true;
}

int TopCompileScope::directLookup(Thread& th, P<String> const& inName, size_t& outIndex, V& outBuiltIn)
{
	int scope = CompileScope::directLookup(th, inName, outIndex, outBuiltIn);
	if (scope != scopeUndefined) return scope;

	for (size_t i = 0; i < mWorkspaceVars.size(); ++i) {
		if (mWorkspaceVars[i].mName() == inName()) {
			return scopeWorkspace;
		}
	}

	V value;
	if (th.mWorkspace->get(th, inName(), value)) {
		return scopeWorkspace;
	} else if (vm.builtins->get(th, inName, value)) {
		outBuiltIn = value;
		return scopeBuiltIn;
	}

	return scopeUndefined;
}

int CompileScope::directLookup(Thread& th, P<String> const& inName, size_t& outIndex, V& outBuiltIn)
{
	for (size_t i = 0; i < mLocals.size(); ++i) {
		if (mLocals[i].mName() == inName()) {
			outIndex = i;
			return scopeLocal;
		}
	}
	for (size_t i = 0; i < mVars.size(); ++i) {
		if (mVars[i].mName() == inName()) {
			outIndex = i;
			return scopeFunVar;
		}
	}

	return scopeUndefined;
}

int TopCompileScope::indirectLookup(Thread& th, P<String> const& inName, size_t& outIndex, V& outBuiltIn)
{
	return directLookup(th, inName, outIndex, outBuiltIn);
}

int InnerCompileScope::indirectLookup(Thread& th, P<String> const& inName, size_t& outIndex, V& outBuiltIn)
{
	int scope = directLookup(th, inName, outIndex, outBuiltIn);
	if (scope != scopeUndefined)
		return scope;

	size_t outerIndex;
	scope = mNext->indirectLookup(th, inName, outerIndex, outBuiltIn);
	if (scope == scopeUndefined)
		return scopeUndefined;

	if (scope == scopeLocal || scope == scopeFunVar) {
		VarDef def;
		def.mName = inName;
		def.mIndex = mVars.size();
		def.mFromScope = scope;
		def.mFromIndex = outerIndex;
		outIndex = mVars.size();
		mVars.push_back(def);
		return scopeFunVar;
	}

	return scope;
}

int TopCompileScope::bindVar(Thread& th, P<String> const& inName, size_t& outIndex)
{
	V v;
	int scope = directLookup(th, inName, outIndex, v);
	if (scope != scopeUndefined && scope != scopeBuiltIn)
		return scope; // already defined

	WorkspaceDef def;
	def.mName = inName;
	mWorkspaceVars.push_back(def);

	return scopeWorkspace;
}

int CompileScope::innerBindVar(Thread& th, P<String> const& inName, size_t& outIndex)
{
	V v;
	int scope = directLookup(th, inName, outIndex, v);
	if (scope == scopeFunVar) {
		post("Name %s is already in use in this scope as a free variable.\n", inName->cstr());
		throw errSyntax;
	}

	if (scope == scopeUndefined) {
		LocalDef def;
		def.mName = inName;
		outIndex = def.mIndex = mLocals.size();
		mLocals.push_back(def);
	}
	return scopeLocal;
}

int InnerCompileScope::bindVar(Thread& th, P<String> const& inName, size_t& outIndex)
{
	return innerBindVar(th, inName, outIndex);
}

CompileScope* ParenCompileScope::nextNonParen() const
{
	CompileScope* scope = mNext();
	while (scope->isParen()) {
		scope = scope->mNext();
	}
	return scope;
}

int ParenCompileScope::directLookup(Thread& th, P<String> const& inName, size_t& outIndex, V& outBuiltIn)
{
	return mNext->directLookup(th, inName, outIndex, outBuiltIn);
}

int ParenCompileScope::indirectLookup(Thread& th, P<String> const& inName, size_t& outIndex, V& outBuiltIn)
{
	return mNext->indirectLookup(th, inName, outIndex, outBuiltIn);
}

int ParenCompileScope::innerBindVar(Thread& th, P<String> const& inName, size_t& outIndex)
{
	return mNext->innerBindVar(th, inName, outIndex);
}

int ParenCompileScope::bindVar(Thread& th, P<String> const& inName, size_t& outIndex)
{
	return mNext->innerBindVar(th, inName, outIndex);
}

//////////////////////

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "dsp.hpp"
#include <string.h>
#include <stdio.h>
#include <cmath>

FFTSetupD fftSetups[kMaxFFTLogSize+1];

void initFFT()
{
	for (int i = kMinFFTLogSize; i <= kMaxFFTLogSize; ++i) {
		fftSetups[i] = vDSP_create_fftsetupD(i, kFFTRadix2);
	}
}

void fft(int n, double* inReal, double* inImag, double* outReal, double* outImag)
{
	int log2n = n == 0 ? 0 : 64 - __builtin_clzll(n - 1);

	DSPDoubleSplitComplex in;
	DSPDoubleSplitComplex out;

	in.realp = inReal;
	in.imagp = inImag;
	out.realp = outReal;
	out.imagp = outImag;

	vDSP_fft_zopD(fftSetups[log2n], &in, 1, &out, 1, log2n, FFT_FORWARD);

	double scale = 2. / n;
	vDSP_vsmulD(outReal, 1, &scale, outReal, 1, n);
	vDSP_vsmulD(outImag, 1, &scale, outImag, 1, n);
}

void ifft(int n, double* inReal, double* inImag, double* outReal, double* outImag)
{
	int log2n = n == 0 ? 0 : 64 - __builtin_clzll(n - 1);

	DSPDoubleSplitComplex in;
	DSPDoubleSplitComplex out;

	in.realp = inReal;
	in.imagp = inImag;
	out.realp = outReal;
	out.imagp = outImag;

	vDSP_fft_zopD(fftSetups[log2n], &in, 1, &out, 1, log2n, FFT_INVERSE);

	double scale = .5;
	vDSP_vsmulD(outReal, 1, &scale, outReal, 1, n);
	vDSP_vsmulD(outImag, 1, &scale, outImag, 1, n);
}

void fft(int n, double* ioReal, double* ioImag)
{
	int log2n = n == 0 ? 0 : 64 - __builtin_clzll(n - 1);

	DSPDoubleSplitComplex io;

	io.realp = ioReal;
	io.imagp = ioImag;

	vDSP_fft_zipD(fftSetups[log2n], &io, 1, log2n, FFT_FORWARD);

	double scale = 2. / n;
	vDSP_vsmulD(ioReal, 1, &scale, ioReal, 1, n);
	vDSP_vsmulD(ioImag, 1, &scale, ioImag, 1, n);
}

void ifft(int n, double* ioReal, double* ioImag)
{
	int log2n = n == 0 ? 0 : 64 - __builtin_clzll(n - 1);

	DSPDoubleSplitComplex io;

	io.realp = ioReal;
	io.imagp = ioImag;

	vDSP_fft_zipD(fftSetups[log2n], &io, 1, log2n, FFT_INVERSE);

	double scale = .5;
	vDSP_vsmulD(ioReal, 1, &scale, ioReal, 1, n);
	vDSP_vsmulD(ioImag, 1, &scale, ioImag, 1, n);
}

void rfft(int n, double* inReal, double* outReal, double* outImag)
{
    int n2 = n/2;
	int log2n = n == 0 ? 0 : 64 - __builtin_clzll(n - 1);

	DSPDoubleSplitComplex in;
	DSPDoubleSplitComplex out;

    vDSP_ctozD((DSPDoubleComplex*)inReal, 1, &in, 1, n2);

	out.realp = outReal;
	out.imagp = outImag;

	vDSP_fft_zropD(fftSetups[log2n], &in, 1, &out, 1, log2n, FFT_FORWARD);

	double scale = 2. / n;
	vDSP_vsmulD(outReal, 1, &scale, outReal, 1, n2);
	vDSP_vsmulD(outImag, 1, &scale, outImag, 1, n2);

    out.realp[n2] = out.imagp[0];
    out.imagp[0] = 0.;
    out.imagp[n2] = 0.;
}

void rifft(int n, double* inReal, double* inImag, double* outReal)
{
    int n2 = n/2;
	int log2n = n == 0 ? 0 : 64 - __builtin_clzll(n - 1);

	DSPDoubleSplitComplex in;

	in.realp = inReal;
	in.imagp = inImag;

    //in.imagp[0] = in.realp[n2];
    in.imagp[0] = 0.;

	vDSP_fft_zripD(fftSetups[log2n], &in, 1, log2n, FFT_INVERSE);

    vDSP_ztocD(&in, 1, (DSPDoubleComplex*)outReal, 2, n2);

	double scale = .5;
	vDSP_vsmulD(outReal, 1, &scale, outReal, 1, n);
}

#define USE_VFORCE 1

inline void complex_expD_conj(double& re, double& im)
{
	double rho = expf(re);
	re = rho * cosf(im);
	im = rho * sinf(im);
}

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "Parser.hpp"
#include "Opcode.hpp"
#include <ctype.h>
#include <cmath>
#include <vector>

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma mark PARSER

bool parseElem(Thread& th, P<Code>& code);
bool parseWord(Thread& th, P<Code>& code);
static void bindVar(Thread& th, P<String> const& name, P<Code>& code);

class ParsingWhat
{
	Thread& th;
	int what;
public:
	ParsingWhat(Thread& inThread, int newMode) : th(inThread), what(th.parsingWhat)
	{
		th.parsingWhat = newMode;
	}
	~ParsingWhat()
	{
		th.parsingWhat = what;
	}
};

static bool skipSpace(Thread& th)
{
	//ScopeLog sl("skipSpace");
	for (;;) {
		int c = th.getc();
		if (c == ';') {
			c = th.getc();
			while (c && c != '\n') { c = th.getc(); }
			if (c == 0) { th.unget(1); return true; }
		}
		if (c == 0) { th.unget(1); return true; }
		bool skip = isspace(c) || iscntrl(c);
		if (!skip) { th.unget(1); break; }
	}
	return false;
}

const char* nonnamechars = ";()[]{}.`,:\"\n";

static bool endOfWord(int c)
{
	return c == 0 || isspace(c) || strchr(nonnamechars, c) != nullptr;
}

static bool parseHexNumber(Thread& th, P<Code>& code)
{
	const char* start = th.curline();
	int64_t z = 0;

	th.getc();
	th.getc();
	int c = th.getc();
	while(isxdigit(c)) {
		if (isdigit(c)) z = z*16 + c - '0';
		else z = z*16 + toupper(c) - 'A' + 10;
		c = th.getc();
	}

	if (!endOfWord(c)) {
		// even though it starts out like a number it continues as some other token
		th.unget(start);
		return false;
	} else {
		th.unget(1);
	}

	code->add(opPushImmediate, z);

	return true;
}

static bool parseFloat(Thread& th, Z& result)
{
	//ScopeLog sl("parseFloat");
	const char* start = th.curline();
	int c = th.getc();

    if (c == 'p' && th.c() == 'i') {
        th.getc();
        result = M_PI;
        return true;
    }

	if (c == '+' || c == '-')
		c = th.getc();

	int digits = 0;
	bool sawdot = false;
	for ( ; ; ) {
		if (isdigit(c)) digits++;
		else if (c == '.') {
			if (sawdot) break;
			sawdot = true;
		}
		else break;
		c = th.getc();
	}
	if (digits == 0) {
		th.unget(start);
		return false;
	}

	if (c == 'e' || c == 'E') {
		c = th.getc();
		if (c == '+' || c == '-')
			c = th.getc();
		while (isdigit(c)) { c = th.getc(); }
	}

	th.toToken(start, (int)(th.curline() - start));

	bool sawpi = false;
	bool sawmega = false;
	bool sawkilo = false;
	bool sawhecto = false;
	bool sawcenti = false;
	bool sawmilli = false;
	bool sawmicro = false;

	if (c == 'p' && th.c() == 'i') {
		sawpi = true;
		th.getc();
	} else if (c == 'M') {
		sawmega = true;
	} else if (c == 'k') {
		sawkilo = true;
	} else if (c == 'h') {
		sawhecto = true;
	} else if (c == 'c') {
		sawcenti = true;
	} else if (c == 'm') {
		sawmilli = true;
	} else if (c == 'u') {
		sawmicro = true;
	} else {
		th.unget(1);
	}

	double x = strtod(th.token, nullptr);
	if (sawpi) x *= M_PI;
	else if (sawmega) x *= 1e6;
	else if (sawkilo) x *= 1e3;
	else if (sawhecto) x *= 1e2;
	else if (sawcenti) x *= 1e-2;
	else if (sawmilli) x *= 1e-3;
	else if (sawmicro) x *= 1e-6;

	result = x;
	return true;
}

static bool parseNumber(Thread& th, Z& result)
{
	const char* start = th.curline();

	Z a, b;
	if (parseFloat(th, a)) {
		if (th.c() == '/') {
			th.getc();
			if (parseFloat(th, b) && endOfWord(th.c())) {
				result = a/b;
				return true;
			}
		} else if (endOfWord(th.c())) {
			result = a;
			return true;
		}
	}
	th.unget(start);
	return false;
}

static bool parseNumber(Thread& th, P<Code>& code)
{
    Z x;
    if (parseNumber(th, x)) {
 		code->add(opPushImmediate, x);
        return true;
    }
    return false;
}

static bool parseSymbol(Thread& th, P<String>& result);
static bool parseItemList(Thread& th, P<Code>& code, int endbrace);

static bool parseQuote(Thread& th, P<Code>& code)
{
	th.getc();
	P<String> name;

	if (!parseSymbol(th, name))
		syntaxError("expected symbol after quote");

	V vname(name);
	code->add(opPushImmediate, vname);

	return true;
}

static bool parseBackquote(Thread& th, P<Code>& code)
{
	th.getc();
	P<String> name;

	if (!parseSymbol(th, name))
		syntaxError("expected symbol after backquote");

	V vname(name);
	V val;
	size_t index;
	int scope = th.mCompileScope->indirectLookup(th, name, index, val);
	switch (scope) {
		case scopeLocal :
			val.i = index;
			code->add(opPushLocalVar, val);
			break;
		case scopeFunVar :
			val.i = index;
			code->add(opPushFunVar, val);
			break;
		case scopeBuiltIn :
			code->add(opPushImmediate, val);
			break;
		case scopeWorkspace :
			code->add(opPushWorkspaceVar, vname);
			break;
		default :
			post("backquote error: \"%s\" is an undefined word\n", name->s);
			syntaxError("undefined word");
	}

	return true;
}

static bool parseDot(Thread& th, P<Code>& code)
{
	th.getc();
	P<String> name;

	if (!parseSymbol(th, name))
		syntaxError("expected symbol after dot");

	V vname(name);
	code->add(opDot, vname);

	return true;
}

static bool parseComma(Thread& th, P<Code>& code)
{
	th.getc();
	P<String> name;

	if (!parseSymbol(th, name))
		syntaxError("expected symbol after dot");

	V vname(name);
	code->add(opComma, vname);

	return true;
}

static bool parseColon(Thread& th, P<Code>& code)
{
	th.getc();

    P<String> name;

    if (!parseSymbol(th, name))
        syntaxError("expected symbol after colon");

    code->keys.push_back(name);

	return true;
}

static bool parseEachOp(Thread& th, P<Code>& code)
{

	int64_t mask = 0;
	int level = 0;

	th.getc();
	int c = th.getc();

	if (c == '@') {
		mask = 1;
		++level;
		do {
			mask |= 1LL << level;
			++level;
			c = th.getc();
		} while (c == '@');
	} else if (c >= '2' && c <= '9') {
		mask = 1LL << (c - '1');
		c = th.getc();
	} else if (c == '0' || c == '1') {
		do {
			if (c == '1')
				mask |= 1LL << level;
			++level;
			c = th.getc();
		} while (c == '0' || c == '1');
	} else {
		mask = 1;
	}
	if (isdigit(c)) {
		syntaxError("unexpected extra digit after @");
	}

	th.unget(1);

	V v;
	v.i = mask;

	code->add(opEach, v);

	return true;
}

static bool parseNewForm(Thread& th, P<Code>& code)
{
	ParsingWhat pw(th, parsingEnvir);
	P<Code> code2 = new Code(8);
	th.getc();

	parseItemList(th, code2, '}');

	if (code2->keys.size()) {

		P<TableMap> tmap = new TableMap(code2->keys.size());

		int i = 0;
		for (Arg key : code2->keys) {
            tmap->put(i, key, key.Hash());
			++i;
		}

		code2->keys.clear();
		code2->add(opPushImmediate, V(tmap));
		code2->add(opReturn, 0.);
		code2->shrinkToFit();

		code->add(opNewForm, V(code2));
	} else {
		code2->add(opReturn, 0.);
		code2->shrinkToFit();
		code->add(opInherit, V(code2));
	}

	return true;
}

static String* parseString(Thread& th);

static bool parseStackEffect(Thread& th, int& outTakes, int& outLeaves)
{
	outTakes = 0;
	outLeaves = 1;
	skipSpace(th);
	int c = th.c();
	if (!isdigit(c)) return true;
	outLeaves = 0;

	while(isdigit(c)) {
		outTakes = outTakes * 10 + c - '0';
		c = th.getc();
	}
	if (c != '.') return false;
	c = th.getc();
	while(isdigit(c)) {
		outLeaves = outLeaves * 10 + c - '0';
		c = th.getc();
	}
	return true;
}

static bool parseLambda(Thread& th, P<Code>& code)
{
	//ScopeLog sl("parseLambda");
	ParsingWhat pw(th, parsingLambda);
	th.getc();
	std::vector<P<String> > args;

	SaveCompileScope scs(th);

	P<InnerCompileScope> cs = new InnerCompileScope(th.mCompileScope);
	th.mCompileScope = cs();

	while (1) {
		P<String> name;
		if (!parseSymbol(th, name)) break;
		args.push_back(name);

		int takes, leaves;
		if (!parseStackEffect(th, takes, leaves)) {
			syntaxError("incorrectly formatted function argument stack effect annotation.");
		}

		LocalDef def;
		def.mName = name;
		def.mIndex = cs->mLocals.size();
		def.mTakes = takes;
		def.mLeaves = leaves;
		cs->mLocals.push_back(def);
	}

	skipSpace(th);

	P<String> help = parseString(th);

	skipSpace(th);

	int c = th.getc();
	if (c != '[') {
        post("got char '%c' %d\n", c, c);
		syntaxError("expected open square bracket after argument list");
	}

	P<Code> code2 = new Code(8);
	parseItemList(th, code2, ']');

	code2->add(opReturn, 0.);
	code2->shrinkToFit();

	// compile code to push all fun vars
	for (size_t i = 0; i < cs->mVars.size(); ++i) {
		VarDef& def = cs->mVars[i];
		V vindex;
		vindex.i = def.mFromIndex;

		if (def.mFromScope == scopeLocal) {
			code->add(opPushLocalVar, vindex);
		} else {
			code->add(opPushFunVar, vindex);
		}
	}
	if (args.size() > USHRT_MAX || cs->mLocals.size() > USHRT_MAX || cs->mVars.size() > USHRT_MAX)
	{
		post("Too many variables!\n");
		throw errSyntax;
	}

    FunDef* def = new FunDef(th, code2, args.size(), cs->mLocals.size(), cs->mVars.size(), help);
	def->mArgNames = args;
	code->add(opPushFun, def);

	return true;
}

#define COMPILE_PARENS 1

static bool parseParens(Thread& th, P<Code>& code)
{
	ParsingWhat pw(th, parsingParens);
	th.getc();

#if COMPILE_PARENS
	P<Code> code2 = new Code(8);
	parseItemList(th, code2, ')');

	code2->add(opReturn, 0.);
	code2->shrinkToFit();
	code->add(opParens, V(code2));
#else
	parseItemList(th, code, ')');
#endif

	return true;
}

static bool parseArray(Thread& th, P<Code>& code)
{
	//ScopeLog sl("parseArray");
	ParsingWhat pw(th, parsingArray);
	th.getc();
	P<Code> code2 = new Code(8);
	parseItemList(th, code2, ']');

	if (code2->size()) {
		code2->add(opReturn, 0.);
		code2->shrinkToFit();
		code->add(opNewVList, V(code2));
	} else {
		code->add(opPushImmediate, V(vm._nilv));
	}

	return true;
}

static bool parseZArray(Thread& th, P<Code>& code)
{
	ParsingWhat pw(th, parsingArray);
	th.getc();
	th.getc();
	P<Code> code2 = new Code(8);
	parseItemList(th, code2, ']');

	if (code2->size()) {
		code2->add(opReturn, 0.);
		code2->shrinkToFit();
		code->add(opNewZList, V(code2));
	} else {
		code->add(opPushImmediate, V(vm._nilz));
	}

	return true;
}

bool parseItemList(Thread& th, P<Code>& code, int endbrace)
{
	//ScopeLog sl("parseItemList");

	while (1) {
		skipSpace(th);
		int c = th.c();
		if (c == endbrace) break;
		if (!parseElem(th, code)) {
			if (endbrace == ']') syntaxError("expected ']'");
			if (endbrace == '}') syntaxError("expected '}'");
			if (endbrace == ')') syntaxError("expected ')'");
		}
	}
	th.getc(); // skip end brace

	return true;
}

bool parseSymbol(Thread& th, P<String>& result)
{
	//ScopeLog sl("parseSymbol");
	skipSpace(th);
	const char* start = th.curline();
	int c = th.getc();

	while(!endOfWord(c)) {
		c = th.getc();
	}
	th.unget(1);

	size_t len = th.curline() - start;
	if (len == 0) return false;

	th.toToken(start, (int)len);

	result = getsym(th.token);

	return true;
}

static void bindVar(Thread& th, P<String> const& name, P<Code>& code)
{
	V val;
	size_t index;
	int scope = th.mCompileScope->bindVar(th, name, index);

	V vname(name);
	if (scope == scopeWorkspace) {
		// compiling at top level
		code->add(opBindWorkspaceVar, vname);
	} else {
		val.i = index;
		code->add(opBindLocal, val);
	}
}

static void bindVarFromList(Thread& th, P<String> const& name, P<Code>& code)
{
	V val;
	size_t varIndex;
	int scope = th.mCompileScope->bindVar(th, name, varIndex);

	if (scope == scopeWorkspace) {
		// compiling at top level
		V vname(name);
		code->add(opBindWorkspaceVarFromList, vname);
	} else {
		val.i = varIndex;
		code->add(opBindLocalFromList, val);
	}
}

bool parseWord(Thread& th, P<Code>& code)
{
	//ScopeLog sl("parseWord");
	P<String> name;

	if (!parseSymbol(th, name)) return false;

	if (strcmp(name->cstr(), "=")==0) {

		skipSpace(th);
		if (th.c() == '(') {
			th.getc();
			// parse multiple assign
			std::vector<P<String>> names;
			{
				P<String> name2;
				while (parseSymbol(th, name2)) {
					names.push_back(name2);
				}
			}
			if (names.size() == 0) {
				syntaxError("expected a name after '= ('\n");
			}
			for (int64_t i = names.size()-1; i>=0; --i) {
				bindVar(th, names[i], code);
			}
			skipSpace(th);
			if (th.c() != ')') {
				syntaxError("expected ')' after '= ('\n");
			}
			th.getc();
			code->add(opNone, 0.);
		} else if (th.c() == '[') {
			th.getc();
			// parse assign from array
			std::vector<P<String>> names;
			{
				P<String> name2;
				while (parseSymbol(th, name2)) {
					names.push_back(name2);
				}
			}
			if (names.size() == 0) {
				syntaxError("expected a name after '= ['\n");
			}
			for (size_t i = 0; i<names.size(); ++i) {
				bindVarFromList(th, names[i], code);
			}
			skipSpace(th);
			if (th.c() != ']') {
				syntaxError("expected ']' after '= ['\n");
			}
			th.getc();
			code->add(opNone, 0.);
		} else {
			P<String> name2;
			if (!parseSymbol(th, name2)) {
				syntaxError("expected a name after '='\n");
			}
			bindVar(th, name2, code);
		}
	} else {
		V val;
		size_t index;
		int scope = th.mCompileScope->indirectLookup(th, name, index, val);
		V vname(name);
		switch (scope) {
			case scopeLocal :
				val.i = index;
				code->add(opCallLocalVar, val);
				break;
			case scopeFunVar :
				val.i = index;
				code->add(opCallFunVar, val);
				break;
			case scopeBuiltIn :
				code->add(opCallImmediate, val);
				break;
			case scopeWorkspace :
				code->add(opCallWorkspaceVar, vname);
				break;
			default :
				post("\"%s\" is an undefined word\n", name->cstr());
				syntaxError("undefined word");

		}

	}

	return true;
}

static bool parseString(Thread& th, P<Code>& code)
{
	//ScopeLog sl("parseString");
	ParsingWhat pw(th, parsingString);

	V string = parseString(th);
	code->add(opPushImmediate, string);

	return true;
}

static String* parseString(Thread& th)
{
	if (th.c() != '"') return nullptr;

	ParsingWhat pw(th, parsingString);
	th.getc();
	int c = th.getc();
	std::string str;

	while (true) {
		if (c == 0) {
			syntaxError("end of input in string");
		} else if (c == '\\' && th.c() == '\\') {
			th.getc();
			c = th.getc();
			switch (c) {
				case 'n' : str += '\n'; break;
				case 'r' : str += '\r'; break;
				case 'f' : str += '\f'; break;
				case 'v' : str += '\v'; break;
				case 't' : str += '\t'; break;
				default : str += c; break;
			}
			c = th.getc();
		} else if (c == '"') {
			if (th.c() == '"') {
				c = th.getc();
				str += '"';
			} else {
				break;
			}
		} else {
			str += c;
			c = th.getc();
		}
	}

	return new String(str.c_str());
}

bool parseElem(Thread& th, P<Code>& code)
{
	skipSpace(th);
	int c = th.c();
	if (c == 0)
		return false;
	if (c == ']' || c == ')' || c == '}') {
		post("unexpected '%c'.\n", c);
		throw errSyntax;
	}
	if (c == '@')
		 return parseEachOp(th, code);
	if (c == '(')
		 return parseParens(th, code);
	if (c == '[')
		 return parseArray(th, code);
	if (c == '{')
		 return parseNewForm(th, code);
	if (c == '\\')
		 return parseLambda(th, code);
	if (c == '"')
		return parseString(th, code);
	if (c == '\'')
		return parseQuote(th, code);
	if (c == '`')
		return parseBackquote(th, code);
	if (c == ',')
		return parseComma(th, code);
	if (c == ':')
		return parseColon(th, code);

	if (c == '0' && th.d() == 'x')
		return parseHexNumber(th, code) || parseWord(th, code);

	if (isdigit(c) || c == '+' || c == '-')
		return parseNumber(th, code) || parseWord(th, code);

    if (c == 'p' && th.d() == 'i')
		return parseNumber(th, code) || parseWord(th, code);

	if (c == '.')
		return parseNumber(th, code) || parseDot(th, code);

	if (c == '#') {
		int d = th.d();
		if (!d) syntaxError("end of input after '#'");
		if (d == '[') {
			return parseZArray(th, code);
		} else {
			return false;
		}
	}

	return parseWord(th, code);
}

bool parseElems(Thread& th, P<Code>& code)
{
	code = new Code(8);
	while (1) {
		if (!parseElem(th, code)) break;
	}

	code->add(opReturn, 0.);
	code->shrinkToFit();

	return true;
}

/////////////////////////

#pragma mark PRINTI

#include <ostream>

/////////////////////////         1         2         3         4         5         6
/////////////////////////1234567890123456789012345678901234567890123456789012345678901234
const char* s64Spaces = "                                                                ";
const int kNumSpaces = 64;

static void printSpaces(std::ostream& ost, int n)
{
	while (n >= kNumSpaces) {
		ost << s64Spaces;
		n -= kNumSpaces;
	}
	if (n) {
		ost << (s64Spaces + kNumSpaces - n);
	}
}

void printi(std::ostream& ost, int indent, const char* fmt, ...)
{
	printSpaces(ost, indent);
    ssize_t final_n, n = 256;
    std::string str;
    std::unique_ptr<char[]> formatted;
    va_list ap;
    while(1)
	{
        formatted.reset(new char[n]); /* wrap the plain char array into the unique_ptr */
        strcpy(&formatted[0], fmt);
        va_start(ap, fmt);
        final_n = vsnprintf(&formatted[0], n, fmt, ap);
        va_end(ap);
        if (final_n < 0)
			return; // error
		if (n <= final_n) {
            n = final_n;
        } else {
			ost << formatted.get();
			return;
		}
    }
}

void prints(std::ostream& ost, const char* fmt, ...)
{
    ssize_t final_n, n = 256;
    std::string str;
    std::unique_ptr<char[]> formatted;
    va_list ap;
    while(1)
	{
        formatted.reset(new char[n]); /* wrap the plain char array into the unique_ptr */
        strcpy(&formatted[0], fmt);
        va_start(ap, fmt);
        final_n = vsnprintf(&formatted[0], n, fmt, ap);
        va_end(ap);
        if (final_n < 0)
			return; // error
		if (n <= final_n) {
            n = final_n;
        } else {
			ost << formatted.get();
			return;
		}
    }
}
//////////////////////////////////

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "Object.hpp"
#include "VM.hpp"
#include "clz.hpp"
#include "MathOps.hpp"
#include "Opcode.hpp"
#include <algorithm>
#include <cstdarg>

void post(const char* fmt, ...)
{
    va_list vargs;
    va_start(vargs, fmt);
    vprintf(fmt, vargs);
}

void zprintf(std::string& out, const char* fmt, ...)
{
	char s[1024];
	va_list args;
	va_start(args, fmt);
	vsnprintf(s, 1024, fmt, args);
	out += s;
}

#pragma mark ERROR HANDLING

Thread gDummyThread;

[[noreturn]] void notFound(Arg key)
{
	post("notFound ");
	key.print(gDummyThread); // keys are either symbols or numbers neither of which will use the thread argument to print.
	post("\n");
	throw errNotFound;
}

[[noreturn]] void wrongType(const char* msg, const char* expected, Arg got)
{
	post("error: wrong type for  %s .  expected %s. got %s.\n", msg, expected, got.TypeName());
	throw errWrongType;
}

[[noreturn]] void syntaxError(const char* msg)
{
	post("syntax error: %s\n", msg);
	throw errSyntax;
}

[[noreturn]] void indefiniteOp(const char* msg1, const char* msg2)
{
	post("error: operation on indefinite object  %s%s\n", msg1, msg2);
	throw errIndefiniteOperation;
}

#pragma mark OBJECT

Object::Object()
	: scratch(0), elemType(0), finite(false), flags(0)
{
#if COLLECT_MINFO
	++vm.totalObjectsAllocated;
#endif
}

Object::~Object()
{
#if COLLECT_MINFO
	++vm.totalObjectsFreed;
#endif
}

void Ref::set(Arg inV)
{
	O oldval = nullptr;
	if (inV.isObject()) {
		O newval = inV.o();

		newval->retain();
		{
			SpinLocker lock(mSpinLock);
			oldval = o;
			o = newval;
		}
	} else {
		Z newval = inV.f;
		{
			SpinLocker lock(mSpinLock);
			oldval = o;
			o = nullptr;
			z = newval;
		}
	}
	if (oldval)
		oldval->release();
}

void ZRef::set(Z inZ)
{
	z = inZ;
}

V Ref::deref() const
{
	V out;
	{
		SpinLocker lock(mSpinLock);
		if (o) {
			out = o;
			if (o) o->retain();
		}
		else out = z;
	}
	return out;
}

V ZRef::deref() const
{
	return z;
}

Z Object::derefz() const
{
	return asFloat();
}

Z ZRef::derefz() const
{
	return z;
}

Form::Form(P<Table> const& inTable, P<Form> const& inNext)
	: Object(), mTable(inTable), mNextForm(inNext)
{
}

volatile int64_t gTreeNodeSerialNumber;

GForm::GForm(P<GTable> const& inTable, P<GForm> const& inNext)
	: Object(), mTable(inTable), mNextForm(inNext)
{
}

GForm::GForm(P<GForm> const& inNext)
	: Object(), mNextForm(inNext)
{
	mTable = new GTable();
}

P<GForm> consForm(P<GTable> const& inTable, P<GForm> const& inNext) { return new GForm(inTable, inNext); }
P<Form> consForm(P<Table> const& inTable, P<Form> const& inNext) { return new Form(inTable, inNext); }

void Object::apply(Thread& th) {
	th.push(this);
}

class PushFunContext
{
	Thread& th;
	P<Fun> fun;
	size_t stackBase, localBase;
public:
	PushFunContext(Thread& inThread, P<Fun> const& inFun)
		: th(inThread), fun(th.fun),
		stackBase(th.stackBase), localBase(th.localBase)
	{
	}
	~PushFunContext()
	{
		th.popLocals();
		th.fun = fun;
		th.setStackBaseTo(stackBase);
		th.setLocalBase(localBase);
	}
};

class PushREPLFunContext
{
	Thread& th;
	P<Fun> fun;
	size_t stackBase, localBase;
public:
	PushREPLFunContext(Thread& inThread, P<Fun> const& inFun)
		: th(inThread), fun(th.fun),
		stackBase(th.stackBase), localBase(th.localBase)
	{
	}
	~PushREPLFunContext()
	{
		th.popLocals();
		th.fun = fun;
		th.setStackBaseTo(stackBase);
		th.setLocalBase(localBase);
	}
};

void Fun::runREPL(Thread& th)
{
	if (th.stackDepth() < NumArgs()) {
		post("expected %qd args on stack. Only have %qd\n", (int64_t)NumArgs(), (int64_t)th.stack.size());
		throw errStackUnderflow;
	}

	PushREPLFunContext pfc(th, this);

	th.setLocalBase();

	if (NumArgs()) {
		th.local.insert(th.local.end(), th.stack.end() - NumArgs(), th.stack.end());
		th.stack.erase(th.stack.end() - NumArgs(), th.stack.end());
	}
	size_t numLocalVars = NumLocals() - NumArgs();
	if (numLocalVars) {
		V v;
		th.local.insert(th.local.end(), numLocalVars, v);
	}

	th.fun = this;

	th.run(mDef->mCode->getOps());
}

void Fun::run(Thread& th)
{
	if (th.stackDepth() < NumArgs()) {
		post("expected %qd args on stack. Only have %qd\n", (int64_t)NumArgs(), (int64_t)th.stack.size());
		throw errStackUnderflow;
	}

	PushFunContext pfc(th, this);

	th.setLocalBase();

	if (NumArgs()) {
		th.local.insert(th.local.end(), th.stack.end() - NumArgs(), th.stack.end());
		th.stack.erase(th.stack.end() - NumArgs(), th.stack.end());
	}
	size_t numLocalVars = NumLocals() - NumArgs();
	if (numLocalVars) {
		V v;
		th.local.insert(th.local.end(), numLocalVars, v);
	}

	th.setStackBase();

	th.fun = this;

	th.run(mDef->mCode->getOps());
}

void Fun::apply(Thread& th)
{
	int numArgs = NumArgs();

	if (th.stackDepth() < (size_t)numArgs) {
		throw errStackUnderflow;
	}

	if (NoEachOps()) {
		run(th);
	} else {
		if (th.stackDepth()) {
			bool haveEachOps = false;
			V* args = &th.top() - numArgs + 1;

			for (int i = 0; i < numArgs; ++i) {
				if (args[i].isEachOp()) {
					haveEachOps = true;
					break;
				}
			}

			if (haveEachOps) {
				List* s = handleEachOps(th, numArgs, this);
				th.push(s);
			} else {
				run(th);
			}
		} else {
			run(th);
		}
	}
}

Fun::Fun(Thread& th, FunDef* def)
	: mDef(def), mWorkspace(def->Workspace())
{
	if (NumVars()) {
		mVars.insert(mVars.end(), th.stack.end() - NumVars(), th.stack.end());
		th.stack.erase(th.stack.end() - NumVars(), th.stack.end());
	}
}

FunDef::FunDef(Thread& th, P<Code> const& inCode, uint16_t inNumArgs, uint16_t inNumLocals, uint16_t inNumVars, P<String> const& inHelp)
	: mCode(inCode), mNumArgs(inNumArgs), mNumLocals(inNumLocals),  mNumVars(inNumVars),
    mWorkspace(th.mWorkspace),
    mHelp(inHelp)
{
}

Fun::~Fun()
{
}

void Prim::apply(Thread& th)
{
	apply_n(th, mTakes);
}

void Prim::apply_n(Thread& th, size_t n)
{
	if (th.stackDepth() < n)
		throw errStackUnderflow;

	if (NoEachOps()) {
		prim(th, this);
	} else {

		if (n) {
			V* args = &th.top() - n + 1;

			bool haveEachOps = false;
			for (size_t i = 0; i < n; ++i) {
				if (args[i].isEachOp()) {
					haveEachOps = true;
					break;
				}
			}

			if (haveEachOps) {
				List* s = handleEachOps(th, (int)n, this);
				th.push(s);
			} else {
				prim(th, this);
			}
		} else {
			prim(th, this);
		}
	}
}

bool Form::get(Thread& th, Arg key, V& value) const
{
    const Form* e = this;
	int64_t hash = key.Hash();
    do {
       if (e->mTable->getWithHash(th, key, hash, value)) {
           return true;
        }
        e = (Form*)e->mNextForm();
    } while (e);
    return false;
}

V Form::mustGet(Thread& th, Arg key) const
{
	V value;
	if (!get(th, key, value)) {
		post("not found: ");
		throw errNotFound;
	}
	return value;
}

bool GForm::get(Thread& th, Arg key, V& value) const
{
    const GForm* e = this;
    do {
        if (e->mTable->get(th, key, value)) {
            return true;
        }
        e = (GForm*)e->mNextForm();
    } while (e);
    return false;
}

GForm* GForm::putImpure(Arg key, Arg value)
{
	if (mTable->putImpure(key, value)) return this;
	return putPure(key, value);
}

GForm* GForm::putPure(Arg inKey, Arg inValue)
{
	int64_t inKeyHash = inKey.Hash();
	return new GForm(mTable->putPure(inKey, inKeyHash, inValue), mNextForm);
}

V GForm::mustGet(Thread& th, Arg key) const
{
	V value;
	if (!get(th, key, value)) {
		post("not found: ");
		throw errNotFound;
	}
	return value;
}

GTable* GTable::putPure(Arg inKey, int64_t inKeyHash, Arg inValue)
{
    auto tree = mTree.load();
	if (tree) {
		return new GTable(tree->putPure(inKey, inKeyHash, inValue));
	} else {
		int64_t serialNo = ++gTreeNodeSerialNumber;
		return new GTable(new TreeNode(inKey, inKeyHash, inValue, serialNo, nullptr, nullptr));
	}
}

TreeNode* TreeNode::putPure(Arg inKey, int64_t inKeyHash, Arg inValue)
{
	if (inKeyHash == mHash && inKey.Identical(mKey)) {
		return new TreeNode(mKey, mHash, inValue, mSerialNumber, mLeft, mRight);
	}
    auto left = mLeft.load();
    auto right = mRight.load();
    if (inKeyHash < mHash) {
        if (left) {
            return new TreeNode(mKey, mHash, mValue, mSerialNumber, left->putPure(inKey, inKeyHash, inValue), right);
        } else {
            int64_t serialNo = ++gTreeNodeSerialNumber;
            return new TreeNode(mKey, mHash, mValue, mSerialNumber, new TreeNode(inKey, inKeyHash, inValue, serialNo, nullptr, nullptr), right);
        }
	} else {
        if (right) {
            return new TreeNode(mKey, mHash, mValue, mSerialNumber, left, right->putPure(inKey, inKeyHash, inValue));
        } else {
            int64_t serialNo = ++gTreeNodeSerialNumber;
            return new TreeNode(mKey, mHash, mValue, mSerialNumber, left, new TreeNode(inKey, inKeyHash, inValue, serialNo, nullptr, nullptr));
        }
	}
}

static bool TreeNodeEquals(Thread& th, TreeNode* a, TreeNode* b)
{
    while (1) {
        if (!a) return !b;
        if (!b) return false;

        if (!a->mKey.Equals(th, b->mKey)) return false;
        if (!a->mValue.Equals(th, b->mValue)) return false;
        if (a->mLeft == 0) {
            if (b->mLeft != 0) return false;
        } else {
            if (b->mLeft == 0) return false;
            if (!TreeNodeEquals(th, a->mLeft, b->mLeft)) return false;
        }
        a = a->mRight;
        b = b->mRight;
    }
}

bool GTable::Equals(Thread& th, Arg v)
{
	if (v.Identical(this)) return true;
	if (!v.isGTable()) return false;
	if (this == v.o()) return true;
	GTable* that = (GTable*)v.o();
    return TreeNodeEquals(th, mTree.load(), that->mTree.load());
}

void GTable::print(Thread& th, std::string& out, int depth)
{
	std::vector<P<TreeNode> > vec = sorted();
	for (size_t i = 0; i < vec.size(); ++i) {
		P<TreeNode>& p = vec[i];
		zprintf(out, "   ");
		p->mValue.print(th, out);
		zprintf(out, " :");
		p->mKey.print(th, out);
		zprintf(out, "\n");
	}
}

void GTable::printSomethingIWant(Thread& th, std::string& out, int depth)
{
	std::vector<P<TreeNode> > vec = sorted();
	for (size_t i = 0; i < vec.size(); ++i) {
		P<TreeNode>& p = vec[i];
		if (p->mValue.leaves() != 0 && p->mValue.leaves() != 1) {
			zprintf(out, "   ");
			p->mKey.print(th, out);
			zprintf(out, " : ");
			p->mValue.print(th, out);
			zprintf(out, "\n");
		}
	}
}

bool GTable::get(Thread& th, Arg inKey, V& outValue) const
{
	int32_t inKeyHash = inKey.Hash();
	TreeNode* tree = mTree.load();
	while (1) {
		if (tree == nullptr) return false;
		int32_t treeKeyHash = tree->mKey.Hash();
		if (inKeyHash == treeKeyHash) {
			outValue = tree->mValue;
			return true;
		} else if (inKeyHash < treeKeyHash) {
			tree = tree->mLeft.load();
		} else {
			tree = tree->mRight.load();
		}
	}
}

bool GTable::getInner(Arg inKey, V& outValue) const
{
	int32_t inKeyHash = inKey.Hash();
	TreeNode* tree = mTree.load();
	while (1) {
		if (tree == nullptr) return false;
		int32_t treeKeyHash = tree->mKey.Hash();
		if (inKeyHash == treeKeyHash) {
			outValue = tree->mValue;
			return true;
		} else if (inKeyHash < treeKeyHash) {
			tree = tree->mLeft.load();
		} else {
			tree = tree->mRight.load();
		}
	}
}

V GTable::mustGet(Thread& th, Arg inKey) const
{
	V value;
	if (get(th, inKey, value)) return value;

	throw errNotFound;
}

bool GTable::putImpure(Arg inKey, Arg inValue)
{
	int32_t inKeyHash = inKey.Hash();
	volatile std::atomic<TreeNode*>* treeNodePtr = &mTree;
	while (1) {
		TreeNode* tree = treeNodePtr->load();
		if (tree == nullptr) {
            int64_t serialNo = ++gTreeNodeSerialNumber;
			TreeNode* newNode = new TreeNode(inKey, inKeyHash, inValue, serialNo, nullptr, nullptr);
			newNode->retain();
            TreeNode* nullNode = nullptr;
            if (treeNodePtr->compare_exchange_weak(nullNode, newNode)) {
                break;
            }
            newNode->release();
		} else {
			int32_t treeKeyHash = tree->mKey.Hash();
			if (treeKeyHash == inKeyHash) {
				return false; // cannot rebind an existing value.
			} else if (inKeyHash < treeKeyHash) {
				treeNodePtr = &tree->mLeft;
			} else {
				treeNodePtr = &tree->mRight;
			}
		}
	}
	return true;
}

void TreeNode::getAll(std::vector<P<TreeNode> >& vec)
{
	P<TreeNode> node = this;
	do {
		vec.push_back(node);
        auto left = node->mLeft.load();
		if (left) left->getAll(vec);
		node = node->mRight.load();
	} while (node());
}

static bool compareTreeNodes(P<TreeNode> const& a, P<TreeNode> const& b)
{
	return a->mSerialNumber < b->mSerialNumber;
}

std::vector<P<TreeNode> > GTable::sorted() const
{
	std::vector<P<TreeNode> > vec;
    auto tree = mTree.load();
	if (tree) {
		tree->getAll(vec);
		sort(vec.begin(), vec.end(), compareTreeNodes);
	}
	return vec;
}

/////////

V List::unaryOp(Thread& th, UnaryOp* op)
{
	if (isVList())
		return new List(new UnaryOpGen(th, op, this));
	else
		return new List(new UnaryOpZGen(th, op, this));

}

V List::binaryOpWithReal(Thread& th, BinaryOp* op, Z _a)
{
	if (isVList())
		return op->makeVList(th, _a, this);
	else
		return op->makeZList(th, _a, this);
}

V List::binaryOpWithVList(Thread& th, BinaryOp* op, List* _a)
{
	return op->makeVList(th, _a, this);
}

V List::binaryOpWithZList(Thread& th, BinaryOp* op, List* _a)
{
	if (isVList())
		return op->makeVList(th, _a, this);
	else
		return op->makeZList(th, _a, this);
}

void V::apply(Thread& th)
{
	if (o) {
		o->apply(th);
	} else {
		th.push(*this);
	}
}

V V::deref()
{
	if (o) {
		return o->deref();
	} else {
		return *this;
	}
}

V V::mustGet(Thread& th, Arg key) const
{
	if (o) {
		return o->mustGet(th, key);
	} else {
		notFound(key);
		throw errNotFound;
	}
}

bool V::get(Thread& th, Arg key, V& value) const
{
	if (o) {
		return o->get(th, key, value);
	} else {
		notFound(key);
		throw errNotFound;
	}
}

int V::Hash() const
{
	if (o) {
		return o->Hash();
	} else {
        union {
            double f;
            uint64_t i;
        } u;
        u.f = f;
		return (int)::Hash64(u.i);
	}
}

V V::unaryOp(Thread& th, UnaryOp* op) const
{
	return !o ? V(op->op(f)) : o->unaryOp(th, op);
}

V V::binaryOp(Thread& th, BinaryOp* op, Arg _b) const
{
	return !o ? _b.binaryOpWithReal(th, op, f) : o->binaryOp(th, op, _b);
}

V V::binaryOpWithReal(Thread& th, BinaryOp* op, Z _a) const
{
	return !o ? V(op->op(_a, f)) : o->binaryOpWithReal(th, op, _a);
}

V V::binaryOpWithVList(Thread& th, BinaryOp* op, List* _a) const
{
	return !o ? op->makeVList(th, _a, *this) : o->binaryOpWithVList(th, op, _a);
}

V V::binaryOpWithZList(Thread& th, BinaryOp* op, List* _a) const
{
	return !o ? op->makeZList(th, _a, *this) : o->binaryOpWithZList(th, op, _a);
}

void Object::printDebug(Thread& th, int depth)
{
	std::string s;
	printDebug(th, s, depth);
	post("%s", s.c_str());
}

void Object::print(Thread& th, int depth)
{
	std::string s;
	print(th, s, depth);
	post("%s", s.c_str());
}

void Object::printShort(Thread& th, int depth)
{
	std::string s;
	printShort(th, s, depth);
	post("%s", s.c_str());
}

void Object::print(Thread& th, std::string& out, int depth)
{
	char s[64];
	snprintf(s, 64, "#%s", TypeName());
	out += s;
}

void Object::printDebug(Thread& th, std::string& out, int depth)
{
	char s[64];
	snprintf(s, 64, "{%s, %p}", TypeName(), this);
	out += s;
}

void Prim::print(Thread& th, std::string& out, int depth)
{
	char s[64];
	snprintf(s, 64, "{%s, %s}", TypeName(), mName);
	out += s;
}

void Prim::printDebug(Thread& th, std::string& out, int depth)
{
	char s[64];
	snprintf(s, 64, "{%s, %s}", TypeName(), mName);
	out += s;
}

void Ref::print(Thread& th, std::string& out, int depth)
{
	V v = deref();
	v.print(th, out, depth);
	zprintf(out, " R");

}

void ZRef::print(Thread& th, std::string& out, int depth)
{
	zprintf(out, "%g ZR", z);
}

void String::print(Thread& th, std::string& out, int depth)
{
	zprintf(out, "%s", (char*)s);
}

void String::printDebug(Thread& th, std::string& out, int depth)
{
	zprintf(out, "\"%s\"", (char*)s);
}

void GForm::print(Thread& th, std::string& out, int depth)
{
	if (mNextForm) {
		mNextForm->print(th, out, depth);
        zprintf(out, "new\n");
	} else {
		zprintf(out, "New\n");
	}

	mTable->print(th, out, depth+1);
}

void Form::print(Thread& th, std::string& out, int depth)
{
	if (depth >= vm.printDepth) {
		zprintf(out, "{...} ");
		return;
	}

	zprintf(out, "{");
	if (mNextForm) {
		mNextForm->print(th, out, depth);
		zprintf(out, "  ");
	}

	mTable->print(th, out, depth+1);

	zprintf(out, "}");
}

void EachOp::print(Thread& th, std::string& out, int inDepth)
{
	v.print(th, out, inDepth);
	zprintf(out, " ");

	// try to print as concisely as possible.
	if (mask == 1) {
		zprintf(out, "@");
	} else if (ONES(mask) == 1 && 1 + CTZ(mask) <= 9) {
		zprintf(out, "@%d", 1 + CTZ(mask));
	} else if (CTZ(mask) == 0) {
		int n = CTZ(~mask);
		while (n--)
			zprintf(out, "@");
	} else {
		zprintf(out, "@");
		int32_t m = mask;
		while (m) {
			zprintf(out, "%c", '0' + (m&1));
			m >>= 1;
		}
	}
}

void Code::print(Thread& th, std::string& out, int depth)
{
	if (depth >= vm.printDepth) {
		zprintf(out, "#Code{...}");
		return;
	}

	zprintf(out, "#Code %d{\n", size());
	Opcode* items = getOps();
	for (int i = 0; i < size(); ++i) {
		zprintf(out, "%4d   %s ", i, opcode_name[items[i].op]);
		items[i].v.printShort(th, out, depth+1);
		out += "\n";
	}
	zprintf(out, "}\n");
}

void List::print(Thread& th, std::string& out, int depth)
{
	if (isV()) {
		zprintf(out, "[");
	} else {
		zprintf(out, "#[");
	}

	if (depth >= vm.printDepth) {
		zprintf(out, "...]");
		return;
	}

	bool once = true;
	List* list = this;

	for (int i = 0; list && i < vm.printLength;) {
		list->force(th);

		Array* a = list->mArray();
		for (int j = 0; j < a->size() && i < vm.printLength; ++j, ++i) {
			if (!once) zprintf(out, " ");
			once = false;
			if (a->isV()) {
				a->v()[j].print(th, out, depth+1);
			} else {

				zprintf(out, "%g", a->z()[j]);
			}
		}
		if (i >= vm.printLength) {
			zprintf(out, " ...]");
			return;
		}
		list = list->nextp();
	}
	if (list && list->mNext) {
		zprintf(out, " ...]");
	} else {
		zprintf(out, "]");
	}
}

void V::print(Thread& th, std::string& out, int depth) const
{
	if (!o) zprintf(out, "%g", f);
	else o->print(th, out, depth);
}

void V::printShort(Thread& th, std::string& out, int depth) const
{
	if (!o) zprintf(out, "%g", f);
	else o->printShort(th, out, depth);
}

void V::printDebug(Thread& th, std::string& out, int depth) const
{
	if (!o) zprintf(out, "%g", f);
	else o->printDebug(th, out, depth);
}

void V::print(Thread& th, int depth) const
{
	std::string s;
	print(th, s, depth);
	post("%s", s.c_str());
}

void V::printShort(Thread& th, int depth) const
{
	std::string s;
	printShort(th, s, depth);
	post("%s", s.c_str());
}

void V::printDebug(Thread& th, int depth) const
{
	std::string s;
	printDebug(th, s, depth);
	post("%s", s.c_str());
}

Gen::Gen(Thread& th, int inItemType, bool inFinite)
	: mDone(false), mOut(0), mBlockSize(inItemType == itemTypeV ? vm.VblockSize : th.rate.blockSize)
{
	elemType = inItemType;
	setFinite(inFinite);
#if COLLECT_MINFO
	if (elemType == itemTypeV)
		++vm.totalStreamGenerators;
	else
		++vm.totalSignalGenerators;
#endif
}

Gen::~Gen()
{
#if COLLECT_MINFO
	if (elemType == itemTypeV)
		--vm.totalStreamGenerators;
	else
		--vm.totalSignalGenerators;
#endif
}

void Gen::end()
{
	setDone();
	mOut->end();
}

void Gen::produce(int shrinkBy)
{
	mOut->mArray->addSize(-shrinkBy);
	mOut = mOut->nextp();
}

void List::end()
{
	assert(mGen);
	mNext = nullptr;
	mGen = nullptr;
	mArray = vm.getNilArray(elemType);
}

V* List::fulfill(int n)
{
	assert(mGen);
	mArray = new Array(elemType, n);
	mArray->setSize(n);
	mNext = new List(mGen);
	mGen = nullptr;
	return mArray->v();
}

V* List::fulfill_link(int n, P<List> const& next)
{
	mArray = new Array(elemType, n);
	mArray->setSize(n);
	mNext = next();
	mGen = nullptr;
	return mArray->v();
}

V* List::fulfill(P<Array> const& inArray)
{
	assert(mGen);
	assert(elemType == inArray->elemType);
	mArray = inArray;
	mNext = new List(mGen);
	mGen = nullptr;
	return mArray->v();
}

Z* List::fulfillz(int n)
{
	assert(mGen);
	mArray = new Array(elemType, n);
	mArray->setSize(n);
	mNext = new List(mGen);
	mGen = nullptr;
	return mArray->z();
}

Z* List::fulfillz_link(int n, P<List> const& next)
{
	mArray = new Array(elemType, n);
	mArray->setSize(n);
	mNext = next;
	mGen = nullptr;
	return mArray->z();
}

Z* List::fulfillz(P<Array> const& inArray)
{
	assert(mGen);
	assert(elemType == inArray->elemType);
	mArray = inArray;
	mNext = new List(mGen);
	mGen = nullptr;
	return mArray->z();
}

void List::link(Thread& th, List* inList)
{
	assert(mGen);
	if (!inList) return;
	inList->force(th);
	mNext = inList->mNext;
	mArray = inList->mArray;
	mGen = nullptr;
}

void List::force(Thread& th)
{
	SpinLocker lock(mSpinLock);
	if (mGen) {
		P<Gen> gen = mGen; // keep the gen from being destroyed out from under pull().
		if (gen->done()) {
			gen->end();
		} else {
			gen->pull(th);
		}
		// mGen should be NULL at this point because one of the following should have been called: fulfill, link, end.
	}
}

int64_t List::length(Thread& th)
{
	if (!isFinite())
		indefiniteOp("size", "");

	List* list = this;

	int64_t sum = 0;
	while (list) {
		list->force(th);

		sum += list->mArray->size();
		list = list->nextp();
	}

	return sum;
}

Array::~Array()
{
	if (isV()) {
		delete [] vv;
	} else {
		free(p);
	}
}

void Array::alloc(int64_t inCap)
{
	if (mCap >= inCap) return;
	mCap = inCap;
	if (isV()) {
		V* oldv = vv;
		vv = new V[mCap];
		for (int64_t i = 0; i < size(); ++i)
			vv[i] = oldv[i];
		delete [] oldv;
	} else {
		p = realloc(p, mCap * elemSize());
	}
}

void Array::add(Arg inItem)
{
	if (mSize >= mCap)
		alloc(2 * mCap);
	if (isV()) vv[mSize++] = inItem;
	else zz[mSize++] = inItem.asFloat();
}

void Array::addAll(Array* a)
{
	if (!a->mSize)
		return;

	int64_t newSize = mSize + a->size();
	if (newSize > mCap)
		alloc(NEXTPOWEROFTWO(newSize));

	if (isV()) {
		if (a->isV()) {
			V* x = vv + size();
			V* y = a->vv;
			for (int64_t i = 0; i < a->size(); ++i) x[i] = y[i];
		} else {
			V* x = vv + size();
			Z* y = a->zz;
			for (int64_t i = 0; i < a->size(); ++i) x[i] = y[i];
		}
	} else {
		if (a->isV()) {
			Z* x = zz + size();
			V* y = a->vv;
			for (int64_t i = 0; i < a->size(); ++i) x[i] = y[i].asFloat();
		} else {
			memcpy(zz + mSize, a->zz, a->mSize * sizeof(Z));
		}
	}
	mSize = newSize;
}

void Array::addz(Z inItem)
{
	if (mSize >= mCap)
		alloc(2 * mCap);
	if (isV()) vv[mSize++] = V(inItem);
	else zz[mSize++] = inItem;
}

void Array::put(int64_t inIndex, Arg inItem)
{
	if (isV()) vv[inIndex] = inItem;
	else zz[inIndex] = inItem.asFloat();
}

void Array::putz(int64_t inIndex, Z inItem)
{
	if (isV()) vv[inIndex] = V(inItem);
	else zz[inIndex] = inItem;
}

In::In()
	: mList(nullptr), mOffset(0), mConstant(0.), mIsConstant(true)
{
}

VIn::VIn()
{
	set(0.);
}

VIn::VIn(Arg inValue)
{
	set(inValue);
}

ZIn::ZIn()
{
	set(0.);
}

ZIn::ZIn(Arg inValue)
{
	set(inValue);
}

BothIn::BothIn()
{
	set(0.);
}

BothIn::BothIn(Arg inValue)
{
	set(inValue);
}

void VIn::set(Arg inValue)
{
	if (inValue.isVList()) {
		mList = (List*)inValue.o();
		mOffset = 0;
		mIsConstant = false;
	} else {
		mList = nullptr;
		mConstant = inValue;
		mIsConstant = true;
	}
}

void VIn::setConstant(Arg inValue)
{
	mList = nullptr;
	mConstant = inValue;
	mIsConstant = true;
}

void BothIn::set(Arg inValue)
{
	if (inValue.isList()) {
		mList = (List*)inValue.o();
		mOffset = 0;
		mIsConstant = false;
	} else {
		mList = nullptr;
		mConstant = inValue;
		mIsConstant = true;
	}
}

void BothIn::setv(Arg inValue)
{
	if (inValue.isVList()) {
		mList = (List*)inValue.o();
		mOffset = 0;
		mIsConstant = false;
	} else {
		mList = nullptr;
		mConstant = inValue;
		mIsConstant = true;
	}
}

void BothIn::setConstant(Arg inValue)
{
	mList = nullptr;
	mConstant = inValue;
	mIsConstant = true;
}

void ZIn::set(Arg inValue)
{
	if (inValue.isZList()) {
		mList = (List*)inValue.o();
		mOffset = 0;
		mIsConstant = false;
	} else {
		mList = nullptr;
		mConstant = inValue;
		mIsConstant = true;
	}
}

bool VIn::operator()(Thread& th, int& ioNum, int& outStride, V*& outBuffer)
{
	if (mIsConstant) {
		outStride = 0;
		outBuffer = &mConstant;
		return false;
	}

	if (mList) {
        while (1) {
            mList->force(th);
            assert(mList->mArray);
            int num = (int)(mList->mArray->size() - mOffset);
            if (num) {
                ioNum = std::min(ioNum, num);
                outBuffer = mList->mArray->v() + mOffset;
                outStride = 1;
                return false;
            } else if (mList->next()) {
                mList = mList->next();
            } else break;
        }
    }
	mConstant = 0.;
	outStride = 0;
	outBuffer = &mConstant;
	mDone = true;
	return true;
}

bool ZIn::operator()(Thread& th, int& ioNum, int& outStride, Z*& outBuffer)
{
	if (mIsConstant) {
		outStride = 0;
		outBuffer = &mConstant.f;
		return false;
	}
	if (mList) {
		if (mOnce) {
			mOnce = false;
		}
        while (1) {
            mList->force(th);
            assert(mList->mArray);
			int num = (int)(mList->mArray->size() - mOffset);
            if (num) {
                ioNum = std::min(ioNum, num);
                outBuffer = mList->mArray->z() + mOffset;
                outStride = 1;
                return false;
            } else if (mList->next()) {
                mList = mList->next();
            } else break;
        }
	}
	mConstant = 0.;
	outStride = 0;
	outBuffer = &mConstant.f;
    ioNum = 0;
	mDone = true;
	return true;
}

void dumpList(List const* list)
{
	for (int i = 0; list; ++i, list = list->nextp()) {
		printf("  List %d %p mGen %p\n", i, list, list->mGen());
		printf("  List %d %p mNext %p\n", i, list, list->nextp());
		printf("  List %d %p mArray %p %d\n", i, list, list->mArray(), (int)(list->mArray() ? list->mArray->size() : 0));
	}
}

bool VIn::link(Thread& th, List* inList)
{
	if (!mList) return false;
	while (1) {
		mList->force(th);
		assert(mList->mArray);
		if (mOffset) {
			int n = (int)(mList->mArray->size() - mOffset);
			if (n) {
				V* out = inList->fulfill_link(n, mList->next());
				V* in = mList->mArray->v() + mOffset;
				for (int i = 0; i < n; ++i) {
					out[i] = in[i];
				}
				mList = nullptr;
				mDone = true;
				return true;
			} else {
				mList = mList->next();
			}
		} else {
			inList->link(th, mList());
			mList = nullptr;
			mDone = true;
			return true;
		}
	}
}

bool ZIn::link(Thread& th, List* inList)
{
	if (!mList) return false;

	while (1) {
		mList->force(th);
		assert(mList->mArray);
		if (mOffset) {
			int n = (int)(mList->mArray->size() - mOffset);
			if (n) {
				Z* out = inList->fulfillz_link(n, mList->next());
				Z* in = mList->mArray->z() + mOffset;
				memcpy(out, in, n * sizeof(Z));

				mList = nullptr;
				mDone = true;
				return true;
			} else {
				mList = mList->next();
			}
		} else {
			inList->link(th, mList());
			mList = nullptr;
			mDone = true;
			return true;
		}
	}
}

void In::advance(int inNum)
{
	if (mList) {
		mOffset += inNum;
		if (mOffset == mList->mArray->size()) {
			mList = mList->next();
			mOffset = 0;
		}
	}
}

bool VIn::one(Thread& th, V& v)
{
    if (mIsConstant) {
		v = mConstant;
		return false;
    }
    while (mList) {
        if (mOffset == 0) {
            mList->force(th);
		}
        if (mOffset == mList->mArray->size()) {
            mList = mList->next();
            mOffset = 0;
        } else {
			v = mList->mArray->v()[mOffset++];
			if (mOffset == mList->mArray->size()) {
				mList = mList->next();
				mOffset = 0;
			}
			return false;
		}
    }
	mDone = true;
    return true;
}

bool ZIn::onez(Thread& th, Z& z)
{
    if (mIsConstant) {
		z = mConstant.f;
		return false;
    }
    while (mList) {
        if (mOffset == 0) {
            mList->force(th);
		}
        if (mOffset == mList->mArray->size()) {
            mList = mList->next();
            mOffset = 0;
        } else {
			z = mList->mArray->z()[mOffset++];
			if (mOffset == mList->mArray->size()) {
				mList = mList->next();
				mOffset = 0;
			}
			return false;
		}
    }
	mDone = true;
    return true;
}

bool ZIn::peek(Thread& th, Z& z)
{
    if (mIsConstant) {
		z = mConstant.f;
		return false;
    }
    while (mList) {
        if (mOffset == 0) {
            mList->force(th);
		}
        if (mOffset == mList->mArray->size()) {
            mList = mList->next();
            mOffset = 0;
        } else {
			z = mList->mArray->z()[mOffset];
			return false;
		}
    }
	mDone = true;
    return true;
}

bool BothIn::one(Thread& th, V& v)
{
    if (mIsConstant) {
		v = mConstant;
		return false;
    }
    while (mList) {
        if (mOffset == 0) {
            mList->force(th);
		}
        if (mOffset == mList->mArray->size()) {
			mList = mList->next();
            mOffset = 0;
        } else {
			if (mList->isV())
				v = mList->mArray->v()[mOffset++];
			else
				v = mList->mArray->z()[mOffset++];

			if (mOffset == mList->mArray->size()) {
				mList = mList->next();
				mOffset = 0;
			}
			return false;
		}
    }
	mDone = true;
    return true;
}

bool BothIn::onez(Thread& th, Z& z)
{
    if (mIsConstant) {
		z = mConstant.asFloat();
		return false;
    }
    while (mList) {
        if (mOffset == 0) {
            mList->force(th);
		}
        if (mOffset == mList->mArray->size()) {
			mList = mList->next();
            mOffset = 0;
        } else {
			if (mList->isV())
				z = mList->mArray->v()[mOffset++].asFloat();
			else
				z = mList->mArray->z()[mOffset++];

			if (mOffset == mList->mArray->size()) {
				mList = mList->next();
				mOffset = 0;
			}
			return false;
		}
    }
	mDone = true;
    return true;
}

bool BothIn::onei(Thread& th, int64_t& i)
{
	Z z = 0.;
	bool result = onez(th, z);
	i = (int64_t)floor(z);
	return result;
}

bool ZIn::bench(Thread& th, int& ioNum)
{
	int framesToFill = ioNum;
	int framesFilled = 0;
	while (framesToFill) {
		int n = framesToFill;
		int astride;
		Z* a;
		if (operator()(th, n, astride, a)) {
			ioNum = framesFilled;
			return true;
		}
		framesToFill -= n;
		framesFilled += n;
		advance(n);
	}
	ioNum = framesFilled;
	return false;
}

bool ZIn::fill(Thread& th, int& ioNum, Z* outBuffer, int outStride)
{
	int framesToFill = ioNum;
	int framesFilled = 0;
	while (framesToFill) {
		int n = framesToFill;
		int astride;
		Z* a;
		if (operator()(th, n, astride, a)) {
			for (int i = 0, k = 0; i < framesToFill; ++i)	{
				outBuffer[k] = 0.;
				k += outStride;
			}
			ioNum = framesFilled;
			return true;
		}
		for (int i = 0, j = 0, k = 0; i < n; ++i)	{
			outBuffer[k] = a[j];
			j += astride;
			k += outStride;
		}
		framesToFill -= n;
		framesFilled += n;
		advance(n);
		outBuffer += n * outStride;
	}
	ioNum = framesFilled;
	return false;
}

bool ZIn::fill(Thread& th, int& ioNum, float* outBuffer, int outStride)
{
	int framesToFill = ioNum;
	int framesFilled = 0;
	while (framesToFill) {
		int n = framesToFill;
		int astride;
		Z* a;
		if (operator()(th, n, astride, a)) {
			for (int i = 0, k = 0; i < framesToFill; ++i)	{
				outBuffer[k] = 0.;
				k += outStride;
			}
			ioNum = framesFilled;
			return true;
		}
		for (int i = 0, j = 0, k = 0; i < n; ++i)	{
			outBuffer[k] = a[j];
			j += astride;
			k += outStride;
		}
		framesToFill -= n;
		framesFilled += n;
		advance(n);
		outBuffer += n * outStride;
	}
	ioNum = framesFilled;
	return false;
}

void ZIn::hop(Thread& th, int framesToAdvance)
{
	P<List> list = mList;
	int offset = mOffset;
	while (list && framesToAdvance) {
		list->force(th);
		int avail = (int)(list->mArray->size() - offset);
		if (avail >= framesToAdvance) {
			offset += framesToAdvance;
			mList = list;
			mOffset = offset;
			return;
		}
		framesToAdvance -= avail;
		offset = 0;
		list = list->next();
		if (!list) {
			mList = nullptr;
			mOffset = 0;
			return;
		}
	}
}

bool ZIn::fillSegment(Thread& th, int inNum, Z* outBuffer)
{
	int framesToFill = inNum;
	if (mIsConstant) {
		Z z = mConstant.f;
		for (int i = 0; i < framesToFill; ++i) outBuffer[i] = z;
		return false;
	}

	P<List> list = mList;
	int offset = mOffset;
	Z* out = outBuffer;
	while (list) {
		list->force(th);
		assert(list->mArray);

		int avail = (int)(list->mArray->size() - offset);
		int numToFill = std::min(framesToFill, avail);

		// copy
		Z* in = list->mArray->z() + offset;
		memcpy(out, in, numToFill * sizeof(Z));
		out += numToFill;
		framesToFill -= numToFill;

		if (framesToFill == 0)
			return false;

		list = list->next();
		offset = 0;
	}

	Z z = mConstant.f;
	for (int i = 0; i < framesToFill; ++i) outBuffer[i] = z;

	mDone = true;
	return true;
}

bool ZIn::mix(Thread& th, int& ioNum, Z* outBuffer)
{
	int framesToFill = ioNum;
	int framesFilled = 0;
	while (framesToFill) {
		int n = framesToFill;
		int astride;
		Z* a;
		if (operator()(th, n, astride, a)) {
			ioNum = framesFilled;
			return true;
		}
		for (int i = 0; i < n; ++i)	{
			outBuffer[i] += *a;
			a += astride;
		}
		framesToFill -= n;
		framesFilled += n;
		advance(n);
		outBuffer += n;
	}
	ioNum = framesFilled;
	return false;
}

class Comma : public Gen
{
	VIn _a;
	V key;
public:
	Comma(Thread& th, Arg a, Arg inKey) : Gen(th, itemTypeV, a.isFinite()), _a(a), key(inKey) {}

	virtual const char* TypeName() const override { return "Comma"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);

		while (framesToFill) {
			int n = framesToFill;
			int astride;
			V *a;
			if (_a(th, n,astride, a)) {
				setDone();
				break;
			} else {
				for (int i = 0; i < n; ++i) {
					out[i] = a->comma(th, key);
					a += astride;
				}
				_a.advance(n);
			}
			framesToFill -= n;
			out += n;
		}
		produce(framesToFill);

	}

};

class Dot : public Gen
{
	VIn _a;
	V key;
	V defaultValue;
public:
	Dot(Thread& th, Arg a, Arg inKey, Arg inDefaultValue)
					: Gen(th, itemTypeV, a.isFinite()), _a(a), key(inKey) {}

	virtual const char* TypeName() const override { return "Dot"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);

		while (framesToFill) {
			int n = framesToFill;
			int astride;
			V *a;
			if (_a(th, n,astride, a)) {
				setDone();
				break;
			} else {
					for (int i = 0; i < n; ++i) {
						V v = defaultValue;
						a->dot(th, key, v);
						out[i] = v;
						a += astride;
					}
				_a.advance(n);
			}
			framesToFill -= n;
			out += n;
		}
		produce(framesToFill);

	}

};

V List::comma(Thread& th, Arg key)
{
	return new List(new Comma(th, this, key));
}

bool List::dot(Thread& th, Arg key, V& ioValue)
{
	ioValue = new List(new Dot(th, this, key, ioValue));
	return true;
}

bool List::Equals(Thread& th, Arg v)
{
	if (v.Identical(this)) return true;
	if (!v.isList()) return false;
	List* that = (List*)v.o();
	if (!isFinite())
		indefiniteOp("", "equals : a");
	if (!that->isFinite())
		indefiniteOp("", "equals : b");

	if (elemType != that->elemType) return false;

	if (isVList()) {
		VIn _a(this);
		VIn _b(that);

		V *a, *b;
		int astride, bstride;

		while(1) {
			int n = kDefaultVBlockSize;
			bool aend = _a(th, n,astride, a);
			bool bend = _b(th, n,bstride, b);

			if (aend != bend) return false;
			if (aend && bend) return true;

			for (int i = 0; i < n; ++i) {
				if (!a->Equals(th, *b)) return false;
				a += astride;
				b += bstride;
			}

			_a.advance(n);
			_b.advance(n);
		}

	} else {
		ZIn _a(this);
		ZIn _b(that);

		Z *a, *b;
		int astride, bstride;

		while(1) {
			int n = th.rate.blockSize;
			bool aend = _a(th, n,astride, a);
			bool bend = _b(th, n,bstride, b);
			if (aend != bend) return false;
			if (aend && bend) return true;

			for (int i = 0; i < n; ++i) {
				if (*a != *b) return false;
				a += astride;
				b += bstride;
			}

			_a.advance(n);
			_b.advance(n);
		}
	}

}

List::List(int inItemType) // construct nil
	: mNext(nullptr), mGen(nullptr), mArray(new Array(inItemType, 0))
{
	elemType = inItemType;
	setFinite(true);
}

List::List(int inItemType, int64_t inCap) // construct nil
	: mNext(nullptr), mGen(nullptr), mArray(new Array(inItemType, inCap))
{
	elemType = inItemType;
	setFinite(true);
}

List::List(P<Gen> const& inGen)
	: mNext(nullptr), mGen(inGen), mArray(0)
{
	elemType = inGen->elemType;
	setFinite(inGen->isFinite());
	inGen->setOut(this);
}

List::List(P<Array> const& inArray)
	: mNext(nullptr), mGen(nullptr), mArray(inArray)
{
	elemType = inArray->elemType;
	setFinite(true);
}

List::List(P<Array> const& inArray, P<List> const& inNext)
	: mNext(inNext), mGen(0), mArray(inArray)
{
	assert(!mNext || mArray->elemType == mNext->elemType);
	elemType = inArray->elemType;
	setFinite(!mNext || mNext->isFinite());
}

List::~List()
{
	// free as much tail as possible at once in order to prevent stack overflow.
	P<List> list = mNext;
	mNext = nullptr;
	while (list) {
		if (list->getRefcount() > 1) break;
		P<List> next = list->mNext;
		list->mNext = nullptr;
		list = next;
	}
}

int64_t List::fillz(Thread& th, int64_t n, Z* z)
{
	int64_t k = 0;
	P<List> list = this;
	while(list && k < n) {
		list->force(th);

		int64_t m = std::min((n-k), list->mArray->size());
		for (int64_t i = 0; i < m; ++i) {
			z[k++] = list->mArray->_atz(i);
		}
		list = list->mNext;
	}
	return k;
}

List* List::pack(Thread& th)
{
    force(th);
	if (isPacked())
		return this;

	int cap = 0;
	P<List> list = this;
	while(list) {
		list->force(th);

		cap += list->mArray->size();

		list = list->mNext;
	}

	P<Array> a = new Array(elemType, cap);

	list = this;
	while(list) {
		a->addAll(list->mArray());
		list = list->mNext;
	}

	return new List(a);
}

List* List::packz(Thread& th)
{
    force(th);
	if (isPacked() && isZ())
		return this;

	int cap = 0;
	P<List> list = this;
	while(list) {
		list->force(th);

		cap += list->mArray->size();

		list = list->mNext;
	}

	P<Array> a = new Array(itemTypeZ, cap);

	list = this;
	while(list) {
		a->addAll(list->mArray());
		list = list->mNext;
	}

	return new List(a);
}

List* List::pack(Thread& th, int limit)
{
    force(th);
	if (isPacked())
		return this;

	int cap = 0;
	P<List> list = this;
	while(list) {
		list->force(th);

		cap += list->mArray->size();

		if (cap > limit) return nullptr;

		list = list->mNext;
	}

	P<Array> a = new Array(elemType, cap);

	list = this;
	while(list) {
		a->addAll(list->mArray());
		list = list->mNext;
	}

	return new List(a);
}

List* List::packSome(Thread& th, int64_t& limit)
{
    force(th);
	if (isPacked()) {
		limit = std::min(limit, length(th));
		return this;
	}

	P<List> list = this;
	int64_t count = 0;
	while(list && count < limit) {
		list->force(th);

		count += list->mArray->size();

		list = list->mNext;
	}

	P<Array> a = new Array(elemType, count);

	list = this;
	count = 0;
	while(list && count < limit) {
		a->addAll(list->mArray());
		count += list->mArray->size();
		list = list->mNext;
	}
	limit = std::min(limit, count);

	return new List(a);
}

void List::forceAll(Thread& th)
{
	List* list = this;
	while(list) {
		list->force(th);
		list = list->nextp();
	}
}

V V::msgSend(Thread& th, Arg receiver)
{
	if (!o) {
		return *this;
	} else {
		return o->msgSend(th, receiver);
	}
}

V Prim::msgSend(Thread& th, Arg receiver)
{
	SaveStack ss(th, Takes());
	th.push(receiver);
	apply(th);
	if (th.stackDepth() >= 1) {
		return th.pop();
	} else {
		return V(0.);
	}
}

V Fun::msgSend(Thread& th, Arg receiver)
{
	th.push(receiver);
	SaveStack ss(th, NumArgs());
	apply(th);
	if (th.stackDepth() >= 1) {
		return th.pop();
	} else {
		return V(0.);
	}
}

TableMap::TableMap(size_t inSize)
	: mSize(inSize)
{
	if (inSize == 0) {
		mMask = 0;
		mIndices = nullptr;
		mKeys = nullptr;
	} else {
		size_t n = 2*NEXTPOWEROFTWO((int64_t)mSize);
		mMask = n-1;
		mIndices = new size_t[n]();
		mKeys = new V[mSize];
	}
}

TableMap::TableMap(Arg inKey)
	: mSize(1)
{
	mMask = 1;
	mIndices = new size_t[2]();
	mKeys = new V[mSize];

	mKeys[0] = inKey;
	mIndices[inKey.Hash() & 1] = 1;
}

TableMap::~TableMap()
{
	delete [] mKeys;
	delete [] mIndices;
}

bool TableMap::getIndex(Arg inKey, int64_t inKeyHash, size_t& outIndex)
{
	size_t mask = mMask;
	size_t i = inKeyHash & mask;
	size_t* indices = mIndices;
	V* keys = mKeys;
	while(1) {
		size_t index = indices[i];
		if (index == 0)
			return false;
		size_t index2 = index - 1;
		if (inKey.Identical(keys[index2])) {
			outIndex = index2;
			return true;
		}
		i = (i + 1) & mask;
	}
}

void TableMap::put(size_t inIndex, Arg inKey, int64_t inKeyHash)
{
	mKeys[inIndex] = inKey;

	size_t mask = mMask;
	size_t i = inKeyHash & mask;
	size_t* indices = mIndices;

	while(1) {
		if (indices[i] == 0) {
			indices[i] = inIndex + 1;
			return;
		}
		i = (i + 1) & mask;
	}
}

Table::Table(P<TableMap> const& inMap)
	: mMap(inMap), mValues(new V[mMap->mSize])
{
}

Table::~Table()
{
	delete [] mValues;
}

bool Table::Equals(Thread& th, Arg v)
{
	if (v.Identical(this)) return true;
	if (!v.isTable()) return false;
	if (this == v.o()) return true;
	Table* that = (Table*)v.o();
	size_t size = mMap->mSize;
	if (size != that->mMap->mSize)
		return false;
	for (size_t i = 0; i < size; ++i) {
		V key = mMap->mKeys[i];
		V thatValue;
		if (!that->getWithHash(th, key, key.Hash(), thatValue))
			return false;
		if (!mValues[i].Equals(th, thatValue))
			return false;
	}
    return true;
}

bool Table::getWithHash(Thread& th, Arg key, int64_t hash, V& value) const
{
	size_t index;
	if (!mMap->getIndex(key, hash, index))
		return false;
	value = mValues[index];
	return true;
}

void Table::put(size_t inIndex, Arg inValue)
{
	mValues[inIndex] = inValue;
}

void Table::print(Thread& th, std::string& out, int depth)
{
	for (size_t i = 0; i < mMap->mSize; ++i) {
		if (i == 0) zprintf(out, ":");
		else zprintf(out, "  :");
		mMap->mKeys[i].print(th, out, depth+1);
		zprintf(out, " ");
		mValues[i].print(th, out, depth+1);
	}
}

void TableMap::print(Thread& th, std::string& out, int depth)
{
	zprintf(out, "{");
	for (size_t i = 0; i < mSize; ++i) {
		if (i == 0) zprintf(out, ":");
		else zprintf(out, "  :");
		mKeys[i].print(th, out, depth+1);
	}
	zprintf(out, "}");
}

static P<List> chase_z(Thread& th, P<List> list, int64_t n)
{
	if (n <= 0) return list;

	while (list && n > 0) {
		list->force(th);

		Array* a = list->mArray();
		int64_t asize = a->size();
		if (asize > n) {
			int64_t remain = asize - n;
			Array* a2 = new Array(list->elemType, remain);
			a2->setSize(remain);

			memcpy(a2->z(), a->z() + n, remain * a->elemSize());

			return new List(a2, list->next());
		}
		n -= asize;
		list = list->next();
	}

	if (!list) {
		list = vm._nilz;
	}

	return list;
}

static P<List> chase_v(Thread& th, P<List> list, int64_t n)
{
	if (n <= 0) return list;

	if (!list->isFinite()) {
		indefiniteOp("chase : list", "");
	}

	int64_t length = list->length(th);

	P<List> result = new List(itemTypeV, length);
	P<Array> array = result->mArray;
	V* out = array->v();

	int64_t i = 0;

	while (list) {
		list->force(th);

		Array* a = list->mArray();
		int64_t asize = a->size();
		V* in = a->v();

		for (int64_t j = 0; j < asize; ++j, ++i) {
			out[i] = in[j].chase(th, n);
		}

		list = list->next();
	}

	return list;
}

V List::chase(Thread& th, int64_t n)
{
	if (isVList()) {
		return chase_v(th, this, n);
	} else {
		return chase_z(th, this, n);
	}
}

P<Form> Form::chaseForm(Thread& th, int64_t n)
{
	P<Form> nextForm = mNextForm() ? mNextForm->chaseForm(th, n) : nullptr;
	return new Form(mTable->chaseTable(th, n), nextForm);
}

P<Table> Table::chaseTable(Thread& th, int64_t n)
{
	P<Table> result = new Table(mMap);

	size_t size = mMap->mSize;

	for (size_t i = 0; i < size; ++i) {
		result->mValues[i] = mValues[i].chase(th, n);
	}

	return result;
}

void Plug::setPlug(Arg inV)
{
	SpinLocker lock(mSpinLock);
	in.set(inV);
	++mChangeCount;
}

void Plug::setPlug(const VIn& inVIn, int inChangeCount)
{
	SpinLocker lock(mSpinLock);
	if (inChangeCount == mChangeCount) {
		in = inVIn;
	}
}

void Plug::getPlug(VIn& outVIn, int& outChangeCount)
{
	SpinLocker lock(mSpinLock);
	outVIn = in;
	outChangeCount = mChangeCount;
}

void ZPlug::setPlug(Arg inV) {
	SpinLocker lock(mSpinLock);
	in.set(inV);
	++mChangeCount;
}

void ZPlug::setPlug(const ZIn& inZIn, int inChangeCount) {
	SpinLocker lock(mSpinLock);
	if (inChangeCount == mChangeCount) {
		in = inZIn;
	}
}

void ZPlug::getPlug(ZIn& outZIn, int& outChangeCount) {
	SpinLocker lock(mSpinLock);
	outZIn = in;
	outChangeCount = mChangeCount;
}

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "SoundFiles.hpp"
#include <valarray>

extern char gSessionTime[256];

class SFReaderOutputChannel;

class SFReader : public Object
{
	ExtAudioFileRef mXAF;
	int64_t mFramesRemaining;
	SFReaderOutputChannel* mOutputs;
	int mNumChannels;
	AudioBufferList* mABL;
	bool mFinished = false;

public:

	SFReader(ExtAudioFileRef inXAF, int inNumChannels, int64_t inDuration);

	~SFReader();

	virtual const char* TypeName() const override { return "SFReader"; }

	P<List> createOutputs(Thread& th);

	bool pull(Thread& th);
	void fulfillOutputs(int blockSize);
	void produceOutputs(int shrinkBy);
};

class SFReaderOutputChannel : public Gen
{
	friend class SFReader;
	P<SFReader> mSFReader;
	SFReaderOutputChannel* mNextOutput = nullptr;
	Z* mDummy = nullptr;

public:
	SFReaderOutputChannel(Thread& th, SFReader* inSFReader)
        : Gen(th, itemTypeZ, true), mSFReader(inSFReader)
	{
	}

	~SFReaderOutputChannel()
	{
		if (mDummy) free(mDummy);
	}

	virtual void norefs() override
	{
		mOut = nullptr;
		mSFReader = nullptr;
	}

	virtual const char* TypeName() const override { return "SFReaderOutputChannel"; }

	virtual void pull(Thread& th) override
	{
		if (mSFReader->pull(th)) {
			end();
		}
	}

};

SFReader::SFReader(ExtAudioFileRef inXAF, int inNumChannels, int64_t inDuration)
	: mXAF(inXAF), mNumChannels(inNumChannels), mFramesRemaining(inDuration), mABL(nullptr)
{
	mABL = (AudioBufferList*)calloc(1, sizeof(AudioBufferList) + (mNumChannels - 1) * sizeof(AudioBuffer));
}

SFReader::~SFReader()
{
	ExtAudioFileDispose(mXAF); free(mABL);
	SFReaderOutputChannel* output = mOutputs;
	do {
		SFReaderOutputChannel* next = output->mNextOutput;
		delete output;
		output = next;
	} while (output);
}

void SFReader::fulfillOutputs(int blockSize)
{
	mABL->mNumberBuffers = mNumChannels;
	SFReaderOutputChannel* output = mOutputs;
	size_t bufSize = blockSize * sizeof(Z);
	for (int i = 0; output; ++i, output = output->mNextOutput){
		Z* out;
		if (output->mOut)
			out = output->mOut->fulfillz(blockSize);
		else {
			if (!output->mDummy)
				output->mDummy = (Z*)calloc(output->mBlockSize, sizeof(Z));

			out = output->mDummy;
		}

		mABL->mBuffers[i].mNumberChannels = 1;
		mABL->mBuffers[i].mData = out;
		mABL->mBuffers[i].mDataByteSize = (UInt32)bufSize;
		memset(out, 0, bufSize);
	};
}

void SFReader::produceOutputs(int shrinkBy)
{
	SFReaderOutputChannel* output = mOutputs;
	do {
		if (output->mOut)
			output->produce(shrinkBy);
		output = output->mNextOutput;
	} while (output);
}

P<List> SFReader::createOutputs(Thread& th)
{
	P<List> s = new List(itemTypeV, mNumChannels);

	// fill s->mArray with ola's output channels.
    SFReaderOutputChannel* last = nullptr;
	P<Array> a = s->mArray;
	for (int i = 0; i < mNumChannels; ++i) {
        SFReaderOutputChannel* c = new SFReaderOutputChannel(th, this);
        if (last) last->mNextOutput = c;
        else mOutputs = c;
        last = c;
		a->add(new List(c));
	}

	return s;
}

bool SFReader::pull(Thread& th)
{
	if (mFramesRemaining == 0)
		mFinished = true;

	if (mFinished)
		return true;

	SFReaderOutputChannel* output = mOutputs;
	int blockSize = output->mBlockSize;
	if (mFramesRemaining > 0)
		blockSize = (int)std::min(mFramesRemaining, (int64_t)blockSize);

	fulfillOutputs(blockSize);

	// read file here.
	UInt32 framesRead = blockSize;
	OSStatus err = ExtAudioFileRead(mXAF, &framesRead, mABL);

	if (err || framesRead == 0) {
		mFinished = true;
	}

	produceOutputs(blockSize - framesRead);
	if (mFramesRemaining > 0) mFramesRemaining -= blockSize;

	return mFinished;
}

void sfread(Thread& th, Arg filename, int64_t offset, int64_t frames)
{
	const char* path = ((String*)filename.o())->s;

	CFStringRef cfpath = CFStringCreateWithFileSystemRepresentation(0, path);
	if (!cfpath) {
		post("failed to create path\n");
		return;
	}
	CFReleaser cfpathReleaser(cfpath);

	CFURLRef url = CFURLCreateWithFileSystemPath(0, cfpath, kCFURLPOSIXPathStyle, false);
	if (!url) {
		post("failed to create url\n");
		return;
	}
	CFReleaser urlReleaser(url);

	ExtAudioFileRef xaf;
	OSStatus err = ExtAudioFileOpenURL(url, &xaf);

	cfpathReleaser.release();
	urlReleaser.release();

	if (err) {
		post("failed to open file %d\n", (int)err);
		return;
	}

	AudioStreamBasicDescription fileFormat;

	UInt32 propSize = sizeof(fileFormat);
	err = ExtAudioFileGetProperty(xaf, kExtAudioFileProperty_FileDataFormat, &propSize, &fileFormat);

	int numChannels = fileFormat.mChannelsPerFrame;

	AudioStreamBasicDescription clientFormat = {
		th.rate.sampleRate,
		kAudioFormatLinearPCM,
		kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved,
		static_cast<UInt32>(sizeof(double)),
		1,
		static_cast<UInt32>(sizeof(double)),
		static_cast<UInt32>(numChannels),
		64,
		0
	};

	err = ExtAudioFileSetProperty(xaf, kExtAudioFileProperty_ClientDataFormat, sizeof(clientFormat), &clientFormat);
	if (err) {
		post("failed to set client data format\n");
		ExtAudioFileDispose(xaf);
		return;
	}

	err = ExtAudioFileSeek(xaf, offset);
	if (err) {
		post("seek failed %d\n", (int)err);
		ExtAudioFileDispose(xaf);
		return;
	}

	SFReader* sfr = new SFReader(xaf, numChannels, -1);

	th.push(sfr->createOutputs(th));
}

ExtAudioFileRef sfcreate(Thread& th, const char* path, int numChannels, double fileSampleRate, bool interleaved)
{
	if (fileSampleRate == 0.)
		fileSampleRate = th.rate.sampleRate;

	CFStringRef cfpath = CFStringCreateWithFileSystemRepresentation(0, path);
	if (!cfpath) {
		post("failed to create path '%s'\n", path);
		return nullptr;
	}
	CFReleaser cfpathReleaser(cfpath);

	CFURLRef url = CFURLCreateWithFileSystemPath(0, cfpath, kCFURLPOSIXPathStyle, false);
	if (!url) {
		post("failed to create url\n");
		return nullptr;
	}
	CFReleaser urlReleaser(url);

	AudioStreamBasicDescription fileFormat = {
		fileSampleRate,
		kAudioFormatLinearPCM,
		kAudioFormatFlagsNativeFloatPacked,
		static_cast<UInt32>(sizeof(float) * numChannels),
		1,
		static_cast<UInt32>(sizeof(float) * numChannels),
		static_cast<UInt32>(numChannels),
		32,
		0
	};

	int interleavedChannels = interleaved ? numChannels : 1;
	UInt32 interleavedBit = interleaved ? 0 : kAudioFormatFlagIsNonInterleaved;

	AudioStreamBasicDescription clientFormat = {
		th.rate.sampleRate,
		kAudioFormatLinearPCM,
		kAudioFormatFlagsNativeFloatPacked | interleavedBit,
		static_cast<UInt32>(sizeof(float) * interleavedChannels),
		1,
		static_cast<UInt32>(sizeof(float) * interleavedChannels),
		static_cast<UInt32>(numChannels),
		32,
		0
	};

	ExtAudioFileRef xaf;
	OSStatus err = ExtAudioFileCreateWithURL(url, kAudioFileWAVEType, &fileFormat, nullptr, kAudioFileFlags_EraseFile, &xaf);

	if (err) {
		post("failed to create file '%s'. err: %d\n", path, (int)err);
		return nullptr;
	}

	err = ExtAudioFileSetProperty(xaf, kExtAudioFileProperty_ClientDataFormat, sizeof(clientFormat), &clientFormat);
	if (err) {
		post("failed to set client data format\n");
		ExtAudioFileDispose(xaf);
		return nullptr;
	}

	return xaf;
}

std::atomic<int32_t> gFileCount = 0;

void makeRecordingPath(Arg filename, char* path, int len)
{
	if (filename.isString()) {
		const char* recDir = getenv("SAPF_RECORDINGS");
		if (!recDir || strlen(recDir)==0) recDir = "/tmp";
		snprintf(path, len, "%s/%s.wav", recDir, ((String*)filename.o())->s);
	} else {
		int32_t count = ++gFileCount;
		snprintf(path, len, "/tmp/sapf-%s-%04d.wav", gSessionTime, count);
	}
}

void sfwrite(Thread& th, V& v, Arg filename, bool openIt)
{
	std::vector<ZIn> in;

	int numChannels = 0;

	if (v.isZList()) {
		if (!v.isFinite()) indefiniteOp(">sf : s - indefinite number of frames", "");
		numChannels = 1;
		in.push_back(ZIn(v));
	} else {
		if (!v.isFinite()) indefiniteOp(">sf : s - indefinite number of channels", "");
		P<List> s = (List*)v.o();
		s = s->pack(th);
		Array* a = s->mArray();
		numChannels = (int)a->size();

		if (numChannels > kMaxSFChannels)
			throw errOutOfRange;

		bool allIndefinite = true;
		for (int i = 0; i < numChannels; ++i) {
			V va = a->at(i);
			if (va.isFinite()) allIndefinite = false;
			in.push_back(ZIn(va));
			va.o = nullptr;
		}

		s = nullptr;
		a = nullptr;

		if (allIndefinite) indefiniteOp(">sf : s - all channels have indefinite number of frames", "");
	}
	v.o = nullptr;

	char path[1024];

	makeRecordingPath(filename, path, 1024);

	ExtAudioFileRef xaf = sfcreate(th, path, numChannels, 0., true);
	if (!xaf) return;

	std::valarray<float> buf(0., numChannels * kBufSize);
	AudioBufferList abl;
	abl.mNumberBuffers = 1;
	abl.mBuffers[0].mNumberChannels = numChannels;
	abl.mBuffers[0].mData = &buf[0];
	abl.mBuffers[0].mDataByteSize = kBufSize * sizeof(float);

	int64_t framesPulled = 0;
	int64_t framesWritten = 0;
	bool done = false;
	while (!done) {
		int minn = kBufSize;
		memset(&buf[0], 0, kBufSize * numChannels);
		for (int i = 0; i < numChannels; ++i) {
			int n = kBufSize;
			bool imdone = in[i].fill(th, n, &buf[0]+i, numChannels);
			framesPulled += n;
			if (imdone) done = true;
			minn = std::min(n, minn);
		}

		abl.mBuffers[0].mDataByteSize = minn * sizeof(float);
		OSStatus err = ExtAudioFileWrite(xaf, minn, &abl);
		if (err) {
			post("ExtAudioFileWrite failed %d\n", (int)err);
			break;
		}

		framesWritten += minn;
	}

	post("wrote file '%s'  %d channels  %g secs\n", path, numChannels, framesWritten * th.rate.invSampleRate);

	ExtAudioFileDispose(xaf);

	if (openIt) {
		char cmd[1100];
		snprintf(cmd, 1100, "open \"%s\"", path);
		system(cmd);
	}
}

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "MultichannelExpansion.hpp"
#include "clz.hpp"
#include <algorithm>
#include <vector>

// multi channel mapping is a special case of auto mapping where the mask is all z's.

class MultichannelMapper : public Gen
{
	V fun;
	int numArgs;
	VIn args[kMaxArgs];
public:

	MultichannelMapper(Thread& th, bool inFinite, int n, V* inArgs, Arg inFun)
		: Gen(th, itemTypeV, inFinite), fun(inFun), numArgs(n)
	{
		for (int i = 0; i < numArgs; ++i) {
			args[i].set(inArgs[i]);
		}
	}

	const char* TypeName() const override { return "MultichannelMapper"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);

		for (int i = 0; i < mBlockSize; ++i) {
			SaveStack ss(th);
			for (int j = 0; j < numArgs; ++j) {
				V v;
				if (args[j].one(th, v)) {
					setDone();
					produce(framesToFill);
					return;
				}
				th.push(v);
			}
			try {
				fun.apply(th);
			} catch (...) {
				setDone();
				produce(framesToFill);
				throw;
			}
			out[i] = th.pop();
			--framesToFill;
		}
		produce(framesToFill);
	}
};

template <int N>
void mcx_(Thread& th, Prim* prim)
{
	if (th.stackDepth() < N)
		throw errStackUnderflow;

	V& fun = prim->v;
	V* args = &th.top() - (N - 1);

	bool hasVList = false;
	bool isFinite = false;
	for (int k = 0; k < N; ++k) {
		if (args[k].isVList()) {
			hasVList = true;
			if (args[k].isFinite())
				isFinite = true;
		}
	}

	if (hasVList) {
		List* s = new List(new MultichannelMapper(th, isFinite, N, args, prim));
		th.popn(N);
		th.push(s);
	} else {
		fun.apply(th);
	}

}

const char* kAaa = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
const size_t kAaaLength = strlen(kAaa);

const char* kZzz = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
const size_t kZzzLength = strlen(kZzz);

const char* Prim::GetAutoMapMask() const
{
	//return NoEachOps() || mNumArgs == 0 ? nullptr : kAaa + kAaaLength - mNumArgs;
	return nullptr;
}

class MultichannelMapPrim : public Prim
{
public:
	MultichannelMapPrim(PrimFun _primFun, Arg _v, int n, const char* inName, const char* inHelp)
		: Prim(_primFun, _v, n, 1, inName, inHelp)
	{
	}

	virtual const char* GetAutoMapMask() const { return kZzz + kZzzLength - mTakes; }

};

Prim* mcx(int n, Arg f, const char* name, const char* help)
{
	PrimFun pf = nullptr;
	switch (n) {
		case  1 : pf = mcx_< 1>; break;
		case  2 : pf = mcx_< 2>; break;
		case  3 : pf = mcx_< 3>; break;
		case  4 : pf = mcx_< 4>; break;
		case  5 : pf = mcx_< 5>; break;
		case  6 : pf = mcx_< 6>; break;
		case  7 : pf = mcx_< 7>; break;
		case  8 : pf = mcx_< 8>; break;
		case  9 : pf = mcx_< 9>; break;
		case 10 : pf = mcx_<10>; break;
		case 11 : pf = mcx_<11>; break;
		case 12 : pf = mcx_<12>; break;
		default : throw errFailed;
	}

	return new MultichannelMapPrim(pf, f, n, name, help);
}

class AutoMapPrim : public Prim
{
public:
	const char* mask;
	AutoMapPrim(PrimFun _primFun, Arg _v, int n, const char* inMask, const char* inName, const char* inHelp)
		: Prim(_primFun, _v, n, 1, inName, inHelp), mask(inMask)
	{
	}

	virtual const char* GetAutoMapMask() const { return mask; }

};

class AutoMapper : public Gen
{
	V fun;
	int numArgs;
	BothIn args[kMaxArgs];
public:

	AutoMapper(Thread& th, bool inFinite, const char* inMask, int n, V* inArgs, Arg inFun)
		: Gen(th, itemTypeV, inFinite), fun(inFun), numArgs(n)
	{
		for (int i = 0; i < numArgs; ++i) {
			switch (inMask[i]) {
				case 'a' :
					args[i].setConstant(inArgs[i]);
					break;
				case 'z' : // auto map over V lists, but not Z lists.
					args[i].setv(inArgs[i]);
					break;
				default :
					post("unrecognized AutoMap char '%c'\n", inMask[i]);
				case 'k' :
					args[i].set(inArgs[i]);

			}
		}
	}

	const char* TypeName() const override { return "AutoMapper"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);

		for (int i = 0; i < mBlockSize; ++i) {
			SaveStack ss(th);
			for (int j = 0; j < numArgs; ++j) {
				V v;
				if (args[j].one(th, v)) {
					setDone();
					produce(framesToFill);
					return;
				}
				th.push(v);
			}
			try {
				fun.apply(th);
			} catch (...) {
				setDone();
				produce(framesToFill);
				throw;
			}
			out[i] = th.pop();
			--framesToFill;
		}
		produce(framesToFill);
	}
};

/*
	a - as is. argument is not automapped.
	z - argument is expected to be a signal or scalar, streams are auto mapped.
	k - argument is expected to be a scalar, signals and streams are automapped.
*/

template <int N>
void automap_(Thread& th, Prim* prim)
{

	const char* mask = ((AutoMapPrim*)prim)->mask;

	if (th.stackDepth() < N)
		throw errStackUnderflow;

	V* args = &th.top() - (N - 1);

	bool canMap = false;
	bool isFinite = false;
	for (int k = 0; k < N; ++k) {
		switch (mask[k]) {
			case 'a' :
				break;
			case 'z' :
				if (args[k].isVList()) {
					canMap = true;
					if (args[k].isFinite())
						isFinite = true;
				}
				break;
			default :
				post("unrecognized AutoMap char '%c'\n", mask[k]);
				throw errFailed;
				break;
			case 'k' :
				if (args[k].isList()) {
					canMap = true;
					if (args[k].isFinite())
						isFinite = true;
				}
				break;
		}
	}

	if (canMap) {
		List* s = new List(new AutoMapper(th, isFinite, mask, N, args, prim));
		th.popn(N);
		th.push(s);
	} else {
		prim->v.apply(th);
	}

}

Prim* automap(const char* mask, int n, Arg f, const char* inName, const char* inHelp)
{
	PrimFun pf = nullptr;
	switch (n) {
		case  1 : pf = automap_< 1>; break;
		case  2 : pf = automap_< 2>; break;
		case  3 : pf = automap_< 3>; break;
		case  4 : pf = automap_< 4>; break;
		case  5 : pf = automap_< 5>; break;
		case  6 : pf = automap_< 6>; break;
		case  7 : pf = automap_< 7>; break;
		case  8 : pf = automap_< 8>; break;
		case  9 : pf = automap_< 9>; break;
		case 10 : pf = automap_<10>; break;
		case 11 : pf = automap_<11>; break;
		case 12 : pf = automap_<12>; break;
		default : throw errFailed;
	}

	return new AutoMapPrim(pf, f, n, mask, inName, inHelp);
}

class EachMapper : public Gen
{
	const int level;
	const int numLevels;
	V fun;
	ArgInfo args;
public:

	EachMapper(Thread& th, bool inFinite, int inLevel, int inNumLevels, const ArgInfo& inArgs, Arg inFun)
		: Gen(th, itemTypeV, inFinite), level(inLevel), numLevels(inNumLevels), args(inArgs), fun(inFun)
	{
	}

	const char* TypeName() const override { return "EachMapper"; }

	void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		if (level == 0) {
			for (int i = 0; i < mBlockSize; ++i) {
				SaveStack ss(th);
				for (int j = 0; j < args.numArgs; ++j) {
					V v;
					if (args.arg[j].in.one(th, v)) {
						setDone();
						produce(framesToFill);
						return;
					}
					th.push(v);
				}
				try {
					fun.apply(th);
				} catch (...) {
					setDone();
					produce(framesToFill);
					throw;
				}
				out[i] = th.pop();

				--framesToFill;
			}
		} else {
			ArgInfo subargs;
			subargs.numArgs = args.numArgs;
			for (int j = 0; j < args.numArgs; ++j) {
				subargs.arg[j].mask = args.arg[j].mask;
			}

			int bit = 1 << (numLevels - level);

			bool mmIsFinite = true;

			for (int i = 0; i < mBlockSize; ++i) {
				V argv[kMaxArgs];
				bool allConstant = true;
				for (int j = 0; j < args.numArgs; ++j) {
					V v;
					if (args.arg[j].in.one(th, argv[j])) {
						setDone();
						produce(framesToFill);
						return;
					}
					if (argv[j].isList() && (args.arg[j].mask & bit))
						allConstant = false;
				}

				if (allConstant) {
					SaveStack ss(th);
					for (int j = 0; j < args.numArgs; ++j) {
						th.push(argv[j]);
					}
					try {
						fun.apply(th);
					} catch (...) {
						setDone();
						produce(framesToFill);
						throw;
					}
					out[i] = th.pop();
				} else {
					for (int j = 0; j < args.numArgs; ++j) {
						V v = argv[j];
						if (args.arg[j].mask & bit) {
							if (v.isList() && !v.isFinite())
								mmIsFinite = false;
							subargs.arg[j].in.set(v);
						} else {
							subargs.arg[j].in.setConstant(v);
						}
					}

					out[i] = new List(new EachMapper(th, mmIsFinite, level - 1, numLevels, subargs, fun));
				}
				--framesToFill;
			}
		}
		produce(framesToFill);
	}
};

List* handleEachOps(Thread& th, int numArgs, Arg fun)
{
	ArgInfo args;
	V argv[kMaxArgs];

	args.numArgs = numArgs;
	int32_t maxMask = 0;

	bool mmIsFinite = true;

	for (int i = numArgs-1; i >= 0; --i) {
		V v = th.pop();
		argv[i] = v;
		if (v.isEachOp()) {

			EachOp* adv = (EachOp*)v.o();
			args.arg[i].mask = adv->mask;
			maxMask |= adv->mask;
			if (adv->mask & 1) {
				if (!adv->v.isFinite())
					mmIsFinite = false;
				args.arg[i].in.set(adv->v);
			} else {
				args.arg[i].in.setConstant(adv->v);
			}
		} else {
			args.arg[i].in.setConstant(v);
			args.arg[i].mask = 0;
		}
	}
	if (maxMask > 1 && maxMask != NEXTPOWEROFTWO(maxMask) - 1) {
		post("there are empty levels of iteration. mask: %x\n", maxMask);
		throw errFailed;
	}

	int numLevels = maxMask <= 1 ? 1 : LOG2CEIL(maxMask);
	return new List(new EachMapper(th, mmIsFinite, numLevels-1, numLevels, args, fun));
}

class Flop : public Gen
{
	size_t numArgs;
	std::vector<BothIn> args;
public:

	Flop(Thread& th, bool inFinite, size_t n, V* inArgs)
		: Gen(th, itemTypeV, inFinite), numArgs(n)
	{
		args.reserve(numArgs);
		for (size_t i = 0; i < numArgs; ++i) {
			BothIn in;
			in.set(inArgs[i]);
			args.push_back(in);
		}
	}

	const char* TypeName() const override { return "Flop"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);

		for (int i = 0; i < mBlockSize; ++i) {
			P<List> s = new List(itemTypeV, numArgs);
			P<Array> a = s->mArray;
			for (size_t j = 0; j < numArgs; ++j) {
				V v;
				if (args[j].one(th, v)) {
					setDone();
					produce(framesToFill);
					return;
				}
				a->add(v);
			}
			out[i] = s;
			--framesToFill;
		}
		produce(framesToFill);
	}
};

class Flops : public Gen
{
	size_t numArgs;
	std::vector<VIn> args;
public:

	Flops(Thread& th, bool inFinite, size_t n, V* inArgs)
		: Gen(th, itemTypeV, inFinite), numArgs(n)
	{
		args.reserve(numArgs);
		for (size_t i = 0; i < numArgs; ++i) {
			VIn in;
			in.set(inArgs[i]);
			args.push_back(in);
		}
	}

	const char* TypeName() const override { return "Flops"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);

		for (int i = 0; i < mBlockSize; ++i) {
			P<List> s = new List(itemTypeV, numArgs);
			P<Array> a = s->mArray;
			for (size_t j = 0; j < numArgs; ++j) {
				V v;
				if (args[j].one(th, v)) {
					setDone();
					produce(framesToFill);
					return;
				}
				a->add(v);
			}
			out[i] = s;
			--framesToFill;
		}
		produce(framesToFill);
	}
};

class Flopz : public Gen
{
	size_t numArgs;
	std::vector<ZIn> args;
public:

	Flopz(Thread& th, bool inFinite, size_t n, V* inArgs)
		: Gen(th, itemTypeV, inFinite), numArgs(n)
	{
		args.reserve(numArgs);
		for (size_t i = 0; i < numArgs; ++i) {
			ZIn in;
			in.set(inArgs[i]);
			args.push_back(in);
		}
	}

	const char* TypeName() const override { return "Flopz"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);

		for (int i = 0; i < mBlockSize; ++i) {
			P<List> s = new List(itemTypeZ, numArgs);
			P<Array> a = s->mArray;
			for (size_t j = 0; j < numArgs; ++j) {
				Z z;
				if (args[j].onez(th, z)) {
					setDone();
					produce(framesToFill);
					return;
				}
				a->addz(z);
			}
			out[i] = s;
			--framesToFill;
		}
		produce(framesToFill);
	}
};

class FlopNth : public Gen
{
	VIn _in;
	size_t _nth;
public:

	FlopNth(Thread& th, size_t nth, Arg in)
		: Gen(th, itemTypeV, false), _nth(nth), _in(in)
	{
	}

	const char* TypeName() const override { return "FlopNth"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);

		for (int i = 0; i < mBlockSize; ++i) {
			V v;
			if (_in.one(th, v)) {
				setDone();
				produce(framesToFill);
				return;
			}
			if (v.isList()) {
				if (!v.isFinite()) {
					setDone();
					produce(framesToFill);
					return;
				}
				P<List> u = (List*)v.o();
				u = u->pack(th);
				P<Array> b = u->mArray;

				out[i] = u->wrapAt(_nth);
			} else {
				out[i] = v;
			}

			--framesToFill;
		}
		produce(framesToFill);
	}
};

class FlopsNth : public Gen
{
	VIn _in;
	size_t _nth;
public:

	FlopsNth(Thread& th, size_t nth, Arg in)
		: Gen(th, itemTypeV, false), _nth(nth), _in(in)
	{
	}

	const char* TypeName() const override { return "FlopsNth"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);

		for (int i = 0; i < mBlockSize; ++i) {
			V v;
			if (_in.one(th, v)) {
				setDone();
				produce(framesToFill);
				return;
			}
			if (v.isVList()) {
				if (!v.isFinite()) {
					setDone();
					produce(framesToFill);
					return;
				}
				P<List> u = (List*)v.o();
				u = u->pack(th);
				P<Array> b = u->mArray;

				out[i] = u->wrapAt(_nth);
			} else {
				out[i] = v;
			}

			--framesToFill;
		}
		produce(framesToFill);
	}
};

void flop_(Thread& th, Prim* prim)
{

	P<List> s = th.popVList("flop : list");

	if (s->isFinite()) {

		s = s->pack(th);

		V* args = s->mArray->v();
		size_t N = s->mArray->size();

		bool hasList = false;
		bool isFinite = false;
		bool allZ = true;
		for (size_t k = 0; k < N; ++k) {
			if (args[k].isList()) {
				hasList = true;
				if (args[k].isFinite())
					isFinite = true;
				if (!args[k].isZList())
					allZ = false;
			}
		}

		if (hasList) {
			if (allZ) {
				List* result = new List(new Flopz(th, isFinite, N, args));
				th.push(result);
			} else {
				List* result = new List(new Flop(th, isFinite, N, args));
				th.push(result);
			}
		} else {
			th.push(s);
		}
	} else {
		VIn in(s);
		V first;
		if (in.one(th, first)) {
			post("flop : can't flop an empty list.");
			throw errFailed;
		}
		if (!first.isList()) {
			wrongType("flop : first item in list", "List", first);
		}
		if (!first.isFinite()) {
			post("flop : can't flop an infinite list of infinite lists.");
			throw errFailed;
		}

		size_t n = first.length(th);

		List* result = new List(itemTypeV, n);
		for (size_t i = 0; i < n; ++i) {
			result->add(new List(new FlopNth(th, i, s)));
		}
		th.push(result);
	}

}

void flops_(Thread& th, Prim* prim)
{

	P<List> s = th.popVList("flops : list");

	if (s->isFinite()) {

		s = s->pack(th);

		V* args = s->mArray->v();
		size_t N = s->mArray->size();

		bool hasList = false;
		bool isFinite = false;
		bool allZ = true;
		for (size_t k = 0; k < N; ++k) {
			if (args[k].isList()) {
				hasList = true;
				if (args[k].isFinite())
					isFinite = true;
				if (!args[k].isZList())
					allZ = false;
			}
		}

		if (hasList) {
			if (allZ) {
				List* result = new List(new Flops(th, isFinite, N, args));
				th.push(result);
			} else {
				List* result = new List(new Flops(th, isFinite, N, args));
				th.push(result);
			}
		} else {
			th.push(s);
		}
	} else {
		VIn in(s);
		V first;
		if (in.one(th, first)) {
			post("flops : can't flop an empty list.");
			throw errFailed;
		}
		if (!first.isList()) {
			wrongType("flops : first item in list", "List", first);
		}
		if (!first.isFinite()) {
			post("flops : can't flop an infinite list of infinite lists.");
			throw errFailed;
		}

		size_t n = first.length(th);

		List* result = new List(itemTypeV, n);
		for (size_t i = 0; i < n; ++i) {
			result->add(new List(new FlopsNth(th, i, s)));
		}
		th.push(result);
	}

}

void flop1_(Thread& th, Prim* prim)
{

	P<List> s = th.popVList("flop1 : list");

	if (s->isFinite()) {

		s = s->pack(th);

		V* args = s->mArray->v();
		size_t N = s->mArray->size();

		bool hasList = false;
		bool isFinite = false;
		bool allZ = true;
		for (size_t k = 0; k < N; ++k) {
			if (args[k].isList()) {
				hasList = true;
				if (args[k].isFinite())
					isFinite = true;
				if (!args[k].isZList())
					allZ = false;
			}
		}

		if (hasList) {
			if (allZ) {
				List* result = new List(new Flopz(th, isFinite, N, args));
				th.push(result);
			} else {
				List* result = new List(new Flop(th, isFinite, N, args));
				th.push(result);
			}
		} else {
			List* result = new List(itemTypeV, 1);
			result->add(s);
			th.push(result);
		}
	} else {
		VIn in(s);
		V first;
		if (in.one(th, first)) {
			post("flop1 : can't flop an empty list.");
			throw errFailed;
		}
		if (!first.isList()) {
			wrongType("flop1 : first item in list", "List", first);
		}
		if (!first.isFinite()) {
			post("flop1 : can't flop an infinite list of infinite lists.");
			throw errFailed;
		}

		size_t n = first.length(th);

		List* result = new List(itemTypeV, n);
		for (size_t i = 0; i < n; ++i) {
			result->add(new List(new FlopNth(th, i, s)));
		}
		th.push(result);
	}

}

class Lace : public Gen
{
	size_t numArgs;
	size_t argPos;
	std::vector<BothIn> args;
public:

	Lace(Thread& th, bool inFinite, size_t n, V* inArgs)
		: Gen(th, itemTypeV, inFinite), numArgs(n), argPos(0)
	{
		args.reserve(numArgs);
		for (size_t i = 0; i < numArgs; ++i) {
			BothIn in;
			in.set(inArgs[i]);
			args.push_back(in);
		}
	}

	const char* TypeName() const override { return "Lace"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);

		for (int i = 0; i < mBlockSize; ++i) {
			V v;
			if (args[argPos].one(th, v)) {
				setDone();
				produce(framesToFill);
				return;
			}
			out[i] = v;
			--framesToFill;
			if (++argPos >= numArgs) argPos = 0;
		}
		produce(framesToFill);
	}
};

class Lacez : public Gen
{
	size_t numArgs;
	size_t argPos;
	std::vector<ZIn> args;
public:

	Lacez(Thread& th, bool inFinite, size_t n, V* inArgs)
		: Gen(th, itemTypeZ, inFinite), numArgs(n), argPos(0)
	{
		post("Lacez\n");
		args.reserve(numArgs);
		for (size_t i = 0; i < numArgs; ++i) {
			ZIn in;
			in.set(inArgs[i]);
			args.push_back(in);
		}
	}

	const char* TypeName() const override { return "Lacez"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);

		for (int i = 0; i < mBlockSize; ++i) {
			Z z;
			if (args[argPos].onez(th, z)) {
				setDone();
				produce(framesToFill);
				return;
			}
			out[i] = z;
			--framesToFill;
			if (++argPos >= numArgs) argPos = 0;
		}
		produce(framesToFill);
	}
};

void lace_(Thread& th, Prim* prim)
{

	P<List> s = th.popList("lace : list");
	if (!s->isVList())
		wrongType("lace : list", "VList", s);

	if (!s->isFinite())
		indefiniteOp("lace : list", "");

	s = s->pack(th);

	V* args = s->mArray->v();
	size_t N = s->mArray->size();

	bool hasList = false;
	bool isFinite = false;
	bool allZ = true;
	for (size_t k = 0; k < N; ++k) {
		if (args[k].isList()) {
			hasList = true;
			if (args[k].isFinite())
				isFinite = true;
			if (!args[k].isZList())
				allZ = false;
		}
	}

	if (hasList) {
		if (allZ) {
			List* result = new List(new Lacez(th, isFinite, N, args));
			th.push(result);
		} else {
			List* result = new List(new Lace(th, isFinite, N, args));
			th.push(result);
		}
	} else {
		th.push(s);
	}

}

class Sel : public Gen
{
	int64_t numArgs;
	std::vector<BothIn> args;
	BothIn sel;
public:

	Sel(Thread& th, bool inFinite, int64_t n, V* inArgs, Arg inSel)
		: Gen(th, itemTypeV, inFinite), numArgs(n), sel(inSel)
	{
		args.reserve(numArgs);
		for (int64_t i = 0; i < numArgs; ++i) {
			BothIn in;
			in.set(inArgs[i]);
			args.push_back(in);
		}
	}

	const char* TypeName() const override { return "Sel"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);

		for (int i = 0; i < mBlockSize; ++i) {
			int64_t k;
			if (sel.onei(th, k)) {
				setDone();
				break;
			}
			k = sc_imod(k, numArgs);
			for (int64_t j = 0; j < numArgs; ++j) {
				V v;
				if (args[j].one(th, v)) {
					setDone();
					produce(framesToFill);
					return;
				}
				if (j == k) {
					out[i] = v;
					--framesToFill;
				}
			}
		}
		produce(framesToFill);
	}
};

class Selz : public Gen
{
	int64_t numArgs;
	std::vector<ZIn> args;
	BothIn sel;
public:

	Selz(Thread& th, bool inFinite, int64_t n, V* inArgs, Arg inSel)
		: Gen(th, itemTypeZ, inFinite), numArgs(n), sel(inSel)
	{
		args.reserve(numArgs);
		for (int64_t i = 0; i < numArgs; ++i) {
			ZIn in;
			in.set(inArgs[i]);
			args.push_back(in);
		}
	}

	const char* TypeName() const override { return "Selz"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);

		for (int i = 0; i < mBlockSize; ++i) {
			int64_t k;
			if (sel.onei(th, k)) {
				setDone();
				break;
			}
			k = sc_imod(k, numArgs);
			for (int64_t j = 0; j < numArgs; ++j) {
				Z z;
				if (args[j].onez(th, z)) {
					setDone();
					produce(framesToFill);
					return;
				}
				if (j == k) {
					out[i] = z;
					--framesToFill;
				}
			}
		}
		produce(framesToFill);
	}
};

class Sell : public Gen
{
	int64_t numArgs;
	std::vector<BothIn> args;
	BothIn sel;
public:

	Sell(Thread& th, bool inFinite, int64_t n, V* inArgs, Arg inSel)
		: Gen(th, itemTypeV, inFinite), numArgs(n), sel(inSel)
	{
		args.reserve(numArgs);
		for (int64_t i = 0; i < numArgs; ++i) {
			BothIn in;
			in.set(inArgs[i]);
			args.push_back(in);
		}
	}

	const char* TypeName() const override { return "Sell"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);

		for (int i = 0; i < mBlockSize; ++i) {
			int64_t k;
			if (sel.onei(th, k)) {
				setDone();
				break;
			}
			k = sc_imod(k, numArgs);
			V v;
			if (args[k].one(th, v)) {
				setDone();
				produce(framesToFill);
				return;
			}
			out[i] = v;
			--framesToFill;
		}
		produce(framesToFill);
	}
};

class Sellz : public Gen
{
	int64_t numArgs;
	std::vector<ZIn> args;
	BothIn sel;
public:

	Sellz(Thread& th, bool inFinite, int64_t n, V* inArgs, Arg inSel)
		: Gen(th, itemTypeZ, inFinite), numArgs(n), sel(inSel)
	{
		args.reserve(numArgs);
		for (int64_t i = 0; i < numArgs; ++i) {
			ZIn in;
			in.set(inArgs[i]);
			args.push_back(in);
		}
	}

	const char* TypeName() const override { return "Sellz"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);

		for (int i = 0; i < mBlockSize; ++i) {
			int64_t k;
			if (sel.onei(th, k)) {
				setDone();
				break;
			}
			k = sc_imod(k, numArgs);
			Z z;
			if (args[k].onez(th, z)) {
				setDone();
				produce(framesToFill);
				return;
			}
			out[i] = z;
			--framesToFill;
		}
		produce(framesToFill);
	}
};

void sel_(Thread& th, Prim* prim)
{
	P<List> indices = th.popList("sel : indices");

	P<List> s = th.popList("sel : list");
	if (!s->isVList())
		wrongType("sel : list", "VList", s);

	if (!s->isFinite())
		indefiniteOp("sel : list", "");

	s = s->pack(th);

	V* args = s->mArray->v();
	size_t N = s->mArray->size();

	bool hasList = false;
	bool isFinite = false;
	bool allZ = true;
	for (size_t k = 0; k < N; ++k) {
		if (args[k].isList()) {
			hasList = true;
			if (args[k].isFinite())
				isFinite = true;
			if (!args[k].isZList())
				allZ = false;
		}
	}

	if (hasList) {
		if (allZ) {
			List* result = new List(new Selz(th, isFinite, N, args, indices));
			th.push(result);
		} else {
			List* result = new List(new Sel(th, isFinite, N, args, indices));
			th.push(result);
		}
	} else {
		th.push(s);
	}

}

void sell_(Thread& th, Prim* prim)
{
	P<List> indices = th.popList("sell : indices");

	P<List> s = th.popList("sell : list");
	if (!s->isVList())
		wrongType("sell : list", "VList", s);

	if (!s->isFinite())
		indefiniteOp("sell : list", "");

	s = s->pack(th);

	V* args = s->mArray->v();
	size_t N = s->mArray->size();

	bool hasList = false;
	bool isFinite = false;
	bool allZ = true;
	for (size_t k = 0; k < N; ++k) {
		if (args[k].isList()) {
			hasList = true;
			if (args[k].isFinite())
				isFinite = true;
			if (!args[k].isZList())
				allZ = false;
		}
	}

	if (hasList) {
		if (allZ) {
			List* result = new List(new Sellz(th, isFinite, N, args, indices));
			th.push(result);
		} else {
			List* result = new List(new Sell(th, isFinite, N, args, indices));
			th.push(result);
		}
	} else {
		th.push(s);
	}

}

class Manta {
  public:
    Manta(void) {
   for(int i = 0; i < 53; ++i)
   {
      LastInReport[i] = 0;
      MaxSensorValues[i] = AverageMaxSensorValues[i];
   }
   for(int i = 53; i < 57; ++i)
   {
      LastInReport[i] = 0xFF;
   }
   for(unsigned int i = 0; i < sizeof(CurrentOutReport); ++i)
   {
      CurrentOutReport[i] = 0;
   }
   for(unsigned int i = 0; i < sizeof(VelocityWaiting) / sizeof(VelocityWaiting[0]); ++i)
   {
      VelocityWaiting[i] = false;
   }
}
    virtual void SetPadLED(LEDState state, int ledID) {
   int row = ledID / 8;
   int column = ledID % 8;

   if(ledID < 0 || ledID > 47)
   {
      throw std::invalid_argument("Invalid Pad Index");
   }

   switch(state)
   {
      case Amber:
         CurrentOutReport[AmberIndex + row] |= (1 << column);
         CurrentOutReport[RedIndex + row] &= ~(1 << column);
         break;
      case Red:
         CurrentOutReport[RedIndex + row] |= (1 << column);
         CurrentOutReport[AmberIndex + row] &= ~(1 << column);
         break;
      case Off:
         CurrentOutReport[AmberIndex + row] &= ~(1 << column);
         CurrentOutReport[RedIndex + row] &= ~(1 << column);
         break;
      default:
         throw std::invalid_argument("Invalid state");
   }

   if(IsConnected())
   {
      WriteFrame(CurrentOutReport, false);
   }
}
    virtual void SetPadLEDRow(LEDState state, int row, uint8_t mask) {
   if(row < 0 || row > 5)
   {
      throw std::invalid_argument("Invalid Row Index");
   }

   MantaClient::DebugPrint("Called SetPadLEDRow(%s, %d, %X)",
         state == Off ? "Off" : state == Amber ? "Amber" : "Red", row, mask);
   MantaClient::DebugPrint("ByteReverse(0x%X) = 0x%X", 0xA0, byteReverse(0xA0));
   switch(state)
   {
      case Amber:
         CurrentOutReport[AmberIndex + row] |= byteReverse(mask);
         CurrentOutReport[RedIndex + row] &= ~byteReverse(mask);
         break;
      case Red:
         CurrentOutReport[RedIndex + row] |= byteReverse(mask);
         CurrentOutReport[AmberIndex + row] &= ~byteReverse(mask);
         break;
      case Off:
         CurrentOutReport[RedIndex + row] &= ~byteReverse(mask);
         CurrentOutReport[AmberIndex + row] &= ~byteReverse(mask);
         break;
      default:
         throw std::invalid_argument("Invalid state");
   }
   if(IsConnected())
   {
      WriteFrame(CurrentOutReport, false);
   }
}
    virtual void SetPadLEDColumn(LEDState state, int column, uint8_t mask) {
   if(column < 0 || column > 7)
   {
      throw std::invalid_argument("Invalid Column Index");
   }

   MantaClient::DebugPrint("Called SetPadLEDColumn(%s, %d, %X)",
         state == Off ? "Off" : state == Amber ? "Amber" : "Red", column, mask);
   switch(state)
   {
      case Amber:
         for(int i = 0; i < 6; ++i)
         {
            if((mask >> i) & 0x01)
            {
               CurrentOutReport[AmberIndex + i] |= (0x01 << column);
               CurrentOutReport[RedIndex + i] &= ~(0x01 << column);
            }
         }
         break;
      case Red:
         for(int i = 0; i < 6; ++i)
         {
            if((mask >> i) & 0x01)
            {
               CurrentOutReport[RedIndex + i] |= (0x01 << column);
               CurrentOutReport[AmberIndex + i] &= ~(0x01 << column);
            }
         }
         break;
      case Off:
         for(int i = 0; i < 6; ++i)
         {
            if((mask >> i) & 0x01)
            {
               CurrentOutReport[RedIndex + i] &= ~(0x01 << column);
               CurrentOutReport[AmberIndex + i] &= ~(0x01 << column);
            }
         }
         break;
      default:
         throw std::invalid_argument("Invalid state");
   }

   if(IsConnected())
   {
      WriteFrame(CurrentOutReport, false);
   }
}
    virtual void SetPadLEDFrame(LEDState state, uint8_t mask[]) {
   switch(state)
   {
      case Amber:
         for(unsigned int i = 0; i < sizeof(LEDFrame); ++i)
         {
            CurrentOutReport[AmberIndex + i] |= byteReverse(mask[i]);
            CurrentOutReport[RedIndex + i] &= ~byteReverse(mask[i]);
         }
         break;
      case Red:
         for(unsigned int i = 0; i < sizeof(LEDFrame); ++i)
         {
            CurrentOutReport[RedIndex + i] |= byteReverse(mask[i]);
            CurrentOutReport[AmberIndex + i] &= ~byteReverse(mask[i]);
         }
         break;
      case Off:
         for(unsigned int i = 0; i < sizeof(LEDFrame); ++i)
         {
            CurrentOutReport[RedIndex + i] &= ~byteReverse(mask[i]);
            CurrentOutReport[AmberIndex + i] &= ~byteReverse(mask[i]);
         }
         break;
      case All:
         // when setting both colors we use two frames at once
         for(unsigned int i = 0; i < sizeof(LEDFrame); ++i)
         {
            uint8_t amberMask = mask[i];
            uint8_t redMask = mask[i+sizeof(LEDFrame)];
            // turn off any amber LEDs if there's a red LED in that position
            amberMask &= ~redMask;
            CurrentOutReport[RedIndex + i] = byteReverse(redMask);
            CurrentOutReport[AmberIndex + i] = byteReverse(amberMask);
         }
         break;
      default:
         throw std::invalid_argument("Invalid state");
   }
   if(IsConnected())
   {
      WriteFrame(CurrentOutReport, false);
   }
}
    virtual void SetSliderLED(LEDState state, int id, uint8_t mask) {
   if(id < 0 || id > 1)
   {
      throw std::invalid_argument("Invalid Slider Index");
   }
   switch(state)
   {
      case Amber:
         CurrentOutReport[SliderIndex + id] |= byteReverse(mask);
         break;
      case Red:
         /* no Red slider LEDs, do nothing */
         break;
      case Off:
         CurrentOutReport[SliderIndex + id] &= ~byteReverse(mask);
         break;
      default:
         throw std::invalid_argument("Invalid state");
   }
   if(IsConnected())
   {
      WriteFrame(CurrentOutReport, false);
   }
}
    virtual void SetButtonLED(LEDState state, int id) {
   if(id < 0 || id > 3)
   {
      throw std::invalid_argument("Invalid Button Index");
   }

   switch(state)
   {
      case Amber:
         CurrentOutReport[ButtonIndex] |= (0x01 << (id));
         CurrentOutReport[ButtonIndex] &= ~(0x01 << (id + 4));
         break;
      case Red:
         CurrentOutReport[ButtonIndex] |= (0x01 << (id + 4));
         CurrentOutReport[ButtonIndex] &= ~(0x01 << (id));
         break;
      case Off:
         CurrentOutReport[ButtonIndex] &= ~(0x01 << (id + 4));
         CurrentOutReport[ButtonIndex] &= ~(0x01 << (id));
         break;
      default:
         throw std::invalid_argument("Invalid state");
   }
   if(IsConnected())
   {
      WriteFrame(CurrentOutReport, false);
   }
}
    virtual void ResendLEDState(void) {
   if(IsConnected())
   {
      WriteFrame(CurrentOutReport, false);
   }
}
    virtual void ClearPadAndButtonLEDs(void) {
   for(unsigned int i = 0; i < sizeof(LEDFrame); ++i)
   {
      CurrentOutReport[AmberIndex + i] = 0;
      CurrentOutReport[RedIndex + i] = 0;
   }

   CurrentOutReport[ButtonIndex] = 0;

   if(IsConnected())
   {
      WriteFrame(CurrentOutReport, false);
   }
}
    virtual void ClearButtonLEDs(void) {
   CurrentOutReport[ButtonIndex] = 0;

   if(IsConnected())
   {
      WriteFrame(CurrentOutReport, false);
   }
}
    virtual void Recalibrate(void) {
   if(! IsConnected())
   {
      throw MantaNotConnectedException(this);
   }

   /* make sure these messages get queued so that they
    * don't just cancel each other out */
   CurrentOutReport[ConfigIndex] |= 0x40;
   WriteFrame(CurrentOutReport, true);
   CurrentOutReport[ConfigIndex] &= ~0x40;
   WriteFrame(CurrentOutReport, true);
}
    virtual void SetLEDControl(LEDControlType control, bool state) {
   uint8_t flag;

   switch(control)
   {
      case PadAndButton:
         flag = 0x01;
         break;
      case Slider:
         flag = 0x02;
         break;
      case Button:
         flag = 0x20;
         break;
      default:
         throw std::invalid_argument("Invalid Control Type");
   }

   if(state)
      CurrentOutReport[ConfigIndex] |= flag;
   else
      CurrentOutReport[ConfigIndex] &= ~flag;
   if(IsConnected())
   {
      /* if we're disabling LEDControl, we want to make sure that this
       * message gets queued so that any pending LED messages get sent
       * down before we disable LEDs */
      WriteFrame(CurrentOutReport, !state);
   }
}
    virtual void SetTurboMode(bool Enabled) {
   if(Enabled)
      CurrentOutReport[ConfigIndex] |= 0x04;
   else
      CurrentOutReport[ConfigIndex] &= ~0x04;
   if(IsConnected())
   {
      WriteFrame(CurrentOutReport, false);
   }
}
    virtual void SetRawMode(bool Enabled) {
   if(Enabled)
      CurrentOutReport[ConfigIndex] |= 0x08;
   else
      CurrentOutReport[ConfigIndex] &= ~0x08;
   if(IsConnected())
   {
      WriteFrame(CurrentOutReport, false);
   }
}
    virtual void SetMaxSensorValues(int *values) {
   for(int i = 0; i < 53; ++i)
   {
      MaxSensorValues[i] = values[i];
   }
}
  private:
    virtual void FrameReceived(int8_t *frame) {
bool hadEvent = false;
   uint8_t *uframe = (uint8_t *)frame;
   for(int i = 1; i < 53; ++i)
   {
      uframe[i] = ScaleSensorValue(frame[i] + 128, i);
   }
   /* apply the offset to the slider bytes without scaling them */
   for(int i = 53; i < 57; ++i)
   {
      uframe[i] = frame[i] + 128;
   }
   //printf("\n");
   FrameEvent(uframe);
   /* input frames have one reportID byte at the beginning */
   for(int i = 1; i < 53; ++i)
   {
      /*
       * overall pad logic:
       * if there's a velocity waiting to be calculated, send a note-on
       * if this is the first zero value, send a note-off
       * if the pad changed, send a padValue
       */
      int padIndex = i - 1;
      /* track whether this pad just went high for a single sample,
       * so we don't trigger a simultaneous note-on and note-off */
      bool singleSample = false;

      /* check to see if there's a previous sample waiting to have
       * the velocity algorithm run */
      if(VelocityWaiting[i])
      {
         if(uframe[i] == 0)
         {
            // we were waiting for a 2nd sample for velocity but we got zero.
            singleSample = true;
         }
         else
         {
            if(padIndex < 48) {
               PadVelocityEvent(padIndex / 8, padIndex % 8, padIndex,
                     CalculateVelocity(LastInReport[i], uframe[i]));
			  hadEvent = true;
            } else {
               ButtonVelocityEvent(padIndex - 48,
                       CalculateVelocity(LastInReport[i], uframe[i]));
			  hadEvent = true;
			}
         }
         VelocityWaiting[i] = false;
      }

      if(uframe[i] != LastInReport[i])
      {
         if(padIndex < 48) {
            PadEvent(padIndex / 8, padIndex % 8, padIndex, uframe[i]);
			  hadEvent = true;
         } else {
            ButtonEvent(padIndex - 48, uframe[i]);
			  hadEvent = true;
		}

         /* check to see if this is a release */
         if(0 == uframe[i] && !singleSample)
         {
            if(padIndex < 48) {
               PadVelocityEvent(padIndex / 8, padIndex % 8, padIndex, 0);
			  hadEvent = true;
            } else {
               ButtonVelocityEvent(padIndex - 48, 0);
			  hadEvent = true;
			}
         }
         /* check to see if this is the first nonzero sample */
         else if(0 == LastInReport[i])
         {
            VelocityWaiting[i] = true;
         }
      }
      LastInReport[i] = uframe[i];
   }
   if(uframe[53] != LastInReport[53] || uframe[54] != LastInReport[54])
   {
	  //printf("slider 0 %3d %3d\n", (int)uframe[53], uframe[54]);
	  int value = (uframe[53]) | ((uframe[54]) << 8 );
	  //if (value != 65535) {
      	  SliderEvent(0, value);
			  hadEvent = true;
	  //}
   }
   if(uframe[55] != LastInReport[55] || uframe[56] != LastInReport[56])
   {
	  //printf("slider 1 %3d %3d\n", (int)uframe[55], uframe[56]);
	  int value = (uframe[55]) | ((uframe[56]) << 8 );
	  //if (value != 65535) {
      	  SliderEvent(0, value);
			  hadEvent = true;
	  //}
   }
   for(int i = 53; i < 57; ++i)
   {
      LastInReport[i] = uframe[i];
   }

   if (hadEvent) printf("---\n");
}
    int ScaleSensorValue(int rawValue, int index) {
   float div = (float)rawValue / MaxSensorValues[index];
   return (int)((div * 210) + 0.5);
}
    static uint8_t byteReverse(uint8_t inByte) {
   // Algorithm from Bit Twiddling Hacks
   uint8_t outByte = inByte; // first get LSB of inByte
   int s = 7; // extra shift needed at end

   for (inByte >>= 1; inByte; inByte >>= 1)
   {
      outByte <<= 1;
      outByte |= inByte & 1;
      s--;
   }
   outByte <<= s; // shift when inByte's highest bits are zero
   return outByte;
}
    static int CalculateVelocity(int firstValue, int secondValue) {
   float LOG1, LOG2;
   float MAX;
   float MIN;
   float RELATIVE1, RELATIVE2;
   float LOG_RELATIVE1, LOG_RELATIVE2;
   float SUM_RAW;
   float LOG_SUM_RAW;
   float LOG_SUM_RELATIVE;
   float UP1;
   float VELOCITY = 0;
   int VELint = 0;

   // now do the velocity calculation
   LOG1 = log(1.0 + (float)LastValue);
   LOG2 = log(1.0 + (float)CurrentValue);

   MIN = LastValue;
   if (CurrentValue < MIN)
   {
      MIN = CurrentValue;
   }
   MAX = LastValue;
   if (CurrentValue > MAX)
   {
      MAX = CurrentValue;
   }
   RELATIVE1 = LastValue/MAX;
   RELATIVE2 = CurrentValue/MAX;
   LOG_RELATIVE1 = log(1.0 + RELATIVE1);
   LOG_RELATIVE2 = log(1.0 + RELATIVE2);
   SUM_RAW = LastValue+CurrentValue;
   LOG_SUM_RAW = log(1.0 + SUM_RAW);
   LOG_SUM_RELATIVE = log(1.0 + SUM_RAW/MAX);
   UP1 = 0;
   if (CurrentValue>LastValue) { UP1 = 1; }
   VELOCITY =
      -14.997037  +
      LastValue      *  0.009361  +
      MIN           * -0.014234  +
      LOG1          *  1.099763  +
      RELATIVE2     * -9.588311  +
      LOG_RELATIVE1  *-27.595303  +
      LOG_RELATIVE2  * -8.803761  +
      LOG_SUM_RELATIVE * 44.013138  +
      UP1           *  0.221622;
   //Then trim value to [0.4] range:
   if (VELOCITY < 0.)
   {
      VELOCITY = 0.;
   }
   if (VELOCITY > 4.)
   {
      VELOCITY = 4.;
   }
   //get it to 0. to 1. range
   VELOCITY = VELOCITY / 4.;
   // curve it exponentially
   VELOCITY = VELOCITY * VELOCITY;
   //get it to 0-126 range
   VELOCITY = VELOCITY * 126;
   //get it to 1-127 range
   VELOCITY = VELOCITY+ 1;
   //round to ints
   VELint = (int)VELOCITY;
   return VELint;
}
    static const int AmberIndex = 0;
    static const int RedIndex = 10;
    static const int SliderIndex = 7;
    static const int ButtonIndex = 6;
    static const int ConfigIndex = 9;
    static const int AverageMaxSensorValues[53];
    int MaxSensorValues[53];
    uint8_t LastInReport[InPacketLen];
    uint8_t CurrentOutReport[OutPacketLen];
    bool VelocityWaiting[53];
    bool CentroidEnabled;
    bool MaximumEnabled;
    bool PadFrameEnabled;
};

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "Play.hpp"
#include <AudioToolbox/AudioToolbox.h>
#include <pthread.h>
#include <atomic>

#include "SoundFiles.hpp"

pthread_mutex_t gPlayerMutex = PTHREAD_MUTEX_INITIALIZER;

const int kMaxChannels = 32;

struct AUPlayer* gAllPlayers = nullptr;

struct AUPlayer
{
	AUPlayer(Thread& inThread, int inNumChannels, ExtAudioFileRef inXAFRef = nullptr)
		: th(inThread), count(0), done(false), prev(nullptr), next(gAllPlayers), outputUnit(nullptr), numChannels(inNumChannels), xaf(inXAFRef)
	{
		gAllPlayers = this;
		if (next) next->prev = this;
	}

	~AUPlayer() {
		if (next) next->prev = prev;

		if (prev) prev->next = next;
		else gAllPlayers = next;

		if (xaf) {
			ExtAudioFileDispose(xaf);
			char cmd[1100];
			snprintf(cmd, 1100, "open \"%s\"", path.c_str());
			system(cmd);
		}
	}

	Thread th;
	int count;
	bool done;
	AUPlayer* prev;
	AUPlayer* next;
	AudioComponentInstance outputUnit;
	int numChannels;
	ZIn in[kMaxChannels];
	ExtAudioFileRef xaf = nullptr;
	std::string path;

};

static void stopPlayer(AUPlayer* player)
{
	AudioComponentInstance outputUnit = player->outputUnit;
	player->outputUnit = nullptr;
	if (outputUnit) {

		OSStatus err = AudioOutputUnitStop(outputUnit);
		if (err) post("AudioOutputUnitStop err %d\n", (int)err);
		err = AudioComponentInstanceDispose(outputUnit);
		if (err) post("AudioComponentInstanceDispose outputUnit err %d\n", (int)err);

	}
	delete player;
}

void stopPlaying()
{
	Locker lock(&gPlayerMutex);

	AUPlayer* player = gAllPlayers;
	while (player) {
		AUPlayer* next = player->next;
		stopPlayer(player);
		player = next;
	}
}

void stopPlayingIfDone()
{
	Locker lock(&gPlayerMutex);

	AUPlayer* player = gAllPlayers;
	while (player) {
		AUPlayer* next = player->next;
		if (player->done)
			stopPlayer(player);
		player = next;
	}
}

static void* stopDonePlayers(void* x)
{
	while(1) {
		sleep(1);
		stopPlayingIfDone();
	}
	return nullptr;
}

bool gWatchdogRunning = false;
pthread_t watchdog;

static bool fillBufferList(AUPlayer* player, int inNumberFrames, AudioBufferList* ioData)
{
	if (player->done) {
zeroAll:
		for (int i = 0; i < (int)ioData->mNumberBuffers; ++i) {
			memset((float*)ioData->mBuffers[i].mData, 0, inNumberFrames * sizeof(float));
		}
		return true;
	}
	ZIn* in = player->in;
	bool done = true;
	for (int i = 0; i < (int)ioData->mNumberBuffers; ++i) {
		int n = inNumberFrames;
		if (i >= player->numChannels) {
			memset(ioData->mBuffers[i].mData, 0, ioData->mBuffers[i].mDataByteSize);
		} else {
			try {
				float* buf = (float*)ioData->mBuffers[i].mData;
				bool imdone = in[i].fill(player->th, n, buf, 1);
				if (n < inNumberFrames) {
					memset((float*)ioData->mBuffers[i].mData + n, 0, (inNumberFrames - n) * sizeof(float));
				}
				done = done && imdone;
			} catch (int err) {
				if (err <= -1000 && err > -1000 - kNumErrors) {
					post("\nerror: %s\n", errString[-1000 - err]);
				} else {
					post("\nerror: %d\n", err);
				}
				post("exception in real time. stopping player.\n");
				done = true;
				goto zeroAll;
			} catch (std::bad_alloc& xerr) {
				post("\nnot enough memory\n");
				post("exception in real time. stopping player.\n");
				done = true;
				goto zeroAll;
			} catch (...) {
				post("\nunknown error\n");
				post("exception in real time. stopping player.\n");
				done = true;
				goto zeroAll;
			}
		}
	}

	return done;
}

static void recordPlayer(AUPlayer* player, int inNumberFrames, AudioBufferList const* inData)
{
	if (!player->xaf) return;

	OSStatus err = ExtAudioFileWriteAsync(player->xaf, inNumberFrames, inData); // initialize async.
	if (err) printf("ExtAudioFileWriteAsync err %d\n", (int)err);
}

static OSStatus inputCallback(	void *							inRefCon,
						AudioUnitRenderActionFlags *	ioActionFlags,
						const AudioTimeStamp *			inTimeStamp,
						UInt32							inBusNumber,
						UInt32							inNumberFrames,
						AudioBufferList *				ioData)
{

	AUPlayer* player = (AUPlayer*)inRefCon;

	bool done = fillBufferList(player, inNumberFrames, ioData);
	recordPlayer(player, inNumberFrames, ioData);

	if (done) {
		player->done = true;
	}
	return noErr;
}

static AudioComponentInstance openAU(UInt32 inType, UInt32 inSubtype, UInt32 inManuf)
{
    AudioComponentDescription desc;
    desc.componentType = inType;
    desc.componentSubType = inSubtype;
    desc.componentManufacturer = inManuf;
    desc.componentFlags = 0;
    desc.componentFlagsMask = 0;

    AudioComponent comp = AudioComponentFindNext(nullptr, &desc);
	if (!comp) {
		return nullptr;
	}

    AudioComponentInstance au = nullptr;
    AudioComponentInstanceNew(comp, &au);

	return au;
}

static OSStatus createGraph(AUPlayer* player)
{
    OSStatus err = noErr;
	AudioComponentInstance outputUnit = openAU('auou', 'def ', 'appl');
	if (!outputUnit) {
		post("open output unit failed\n");
		return 'fail';
	}

	player->outputUnit = outputUnit;

	UInt32 flags = kAudioFormatFlagsNativeFloatPacked | kAudioFormatFlagIsNonInterleaved;
	AudioStreamBasicDescription fmt = { vm.ar.sampleRate, kAudioFormatLinearPCM, flags, 4, 1, 4, (UInt32)player->numChannels, 32, 0 };

	err = AudioUnitSetProperty(outputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &fmt, sizeof(fmt));
	if (err) {
		post("set outputUnit client format failed\n");
		return err;
	}

	AURenderCallbackStruct cbs;

	cbs.inputProc = inputCallback;
	cbs.inputProcRefCon = player;

	err = AudioUnitSetProperty(outputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &cbs, sizeof(cbs));
	if (err) {
		post("set render callback failed\n");
		return err;
	}

	err = AudioUnitInitialize(outputUnit);
	if (err) {
		post("initialize output unit failed\n");
		return err;
	}

	err = AudioOutputUnitStart(outputUnit);
	if (err) {
		post("start output unit failed\n");
		return err;
	}

	post("start output unit OK\n");

	return noErr;
}

void playWithAudioUnit(Thread& th, V& v)
{
	if (!v.isList()) wrongType("play : s", "List", v);

	Locker lock(&gPlayerMutex);

	AUPlayer *player;

	if (v.isZList()) {
		player = new AUPlayer(th, 1);
		player->in[0].set(v);
		player->numChannels = 1;
	} else {
		if (!v.isFinite()) indefiniteOp("play : s", "");
		P<List> s = (List*)v.o();
		s = s->pack(th, kMaxChannels);
		if (!s()) {
			post("Too many channels. Max is %d.\n", kMaxChannels);
			return;
		}
		Array* a = s->mArray();

		int asize = (int)a->size();

		player = new AUPlayer(th, asize);
		for (int i = 0; i < asize; ++i) {
			player->in[i].set(a->at(i));
		}
		s = nullptr;
		a = nullptr;
	}
	v.o = nullptr; // try to prevent leak.

    std::atomic_thread_fence(std::memory_order_seq_cst);

	if (!gWatchdogRunning) {
		pthread_create(&watchdog, nullptr, stopDonePlayers, nullptr);
		gWatchdogRunning = true;
	}

	{
		OSStatus err = noErr;
		err = createGraph(player);
		if (err) {
			post("play failed: %d '%4.4s'\n", (int)err, (char*)&err);
			throw errFailed;
		}
	}
}

void recordWithAudioUnit(Thread& th, V& v, Arg filename)
{
	if (!v.isList()) wrongType("play : s", "List", v);

	Locker lock(&gPlayerMutex);

	AUPlayer *player;

	char path[1024];
	ExtAudioFileRef xaf = nullptr;

	if (v.isZList()) {
		makeRecordingPath(filename, path, 1024);
		xaf = sfcreate(th, path, 1, 0., false);
		if (!xaf) {
			printf("couldn't create recording file \"%s\"\n", path);
			return;
		}

		player = new AUPlayer(th, 1, xaf);
		player->in[0].set(v);
		player->numChannels = 1;
	} else {
		if (!v.isFinite()) indefiniteOp("play : s", "");
		P<List> s = (List*)v.o();
		s = s->pack(th, kMaxChannels);
		if (!s()) {
			post("Too many channels. Max is %d.\n", kMaxChannels);
			return;
		}
		Array* a = s->mArray();

		int numChannels = (int)a->size();

		makeRecordingPath(filename, path, 1024);
		xaf = sfcreate(th, path, numChannels, 0., false);
		if (!xaf) {
			printf("couldn't create recording file \"%s\"\n", path);
			return;
		}

		player = new AUPlayer(th, numChannels, xaf);
		for (int i = 0; i < numChannels; ++i) {
			player->in[i].set(a->at(i));
		}
		s = nullptr;
		a = nullptr;
	}
	v.o = nullptr; // try to prevent leak.

	player->path = path;

	{
		OSStatus err = ExtAudioFileWriteAsync(xaf, 0, nullptr); // initialize async.
		if (err) printf("init ExtAudioFileWriteAsync err %d\n", (int)err);
    }

    std::atomic_thread_fence(std::memory_order_seq_cst);

	if (!gWatchdogRunning) {
		pthread_create(&watchdog, nullptr, stopDonePlayers, nullptr);
		gWatchdogRunning = true;
	}

	{
		OSStatus err = noErr;
		err = createGraph(player);
		if (err) {
			post("play failed: %d '%4.4s'\n", (int)err, (char*)&err);
			throw errFailed;
		}
	}
}

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "VM.hpp"
#include <stdio.h>
#include <histedit.h>
#include <algorithm>
#include <sys/stat.h>
#include "primes.hpp"
#include <complex>
#include <dispatch/dispatch.h>
#include <CoreFoundation/CoreFoundation.h>
#include "Manta.h"

class MyManta : public Manta
{
	virtual void PadEvent(int row, int column, int id, int value) {
		printf("pad %d %d %d %d\n", row, column, id, value);
	}
	virtual void SliderEvent(int id, int value) {
		printf("slider %d %d\n", id, value);
	}
	virtual void ButtonEvent(int id, int value) {
		printf("button %d %d\n", id, value);
	}
	virtual void PadVelocityEvent(int row, int column, int id, int velocity) {
		printf("pad vel %d %d %d %d\n", row, column, id, velocity);

	}
	virtual void ButtonVelocityEvent(int id, int velocity) {
		printf("button vel %d %d\n", id, velocity);
	}
	virtual void FrameEvent(uint8_t *frame) {}
	virtual void DebugPrint(const char *fmt, ...) {}
};

Manta* manta();
Manta* manta()
{
	static MyManta* sManta = new MyManta();
	return sManta;
}

/* issue:

[These comments are very old and I have not checked if they are still relevant.]

TableData alloc should use new

bugs:

itd should have a tail time. currently the ugen stops as soon as its input, cutting off the delayed signal.

+ should not stop until both inputs stop?
other additive binops: - avg2 sumsq

no, use a  operator

---

adsrg (gate a d s r --> out) envelope generator with gate.
adsr (dur a d s r --> out) envelope generator with duration.
evgg - (gate levels times curves suspt --> out) envelope generator with gate. suspt is the index of the sustain level.
evg - (dur levels times curves suspt --> out) envelope generator with duration. suspt is the index of the sustain level.

blip (freq phase nharm --> out) band limited impulse oscillator.
dsf1 (freq phase nharm lharm hmul --> out) sum of sines oscillator.

formant (freq formfreq bwfreq --> out) formant oscillator

svf (in freq rq --> [lp hp bp bs]) state variable filter.
moogf (in freq rq --> out) moog ladder low pass filter.

*/

extern void AddCoreOps();
extern void AddMathOps();
extern void AddStreamOps();
extern void AddLFOps();
extern void AddUGenOps();
extern void AddSetOps();
extern void AddRandomOps();
extern void AddMidiOps();

const char* gVersionString = "0.1.21";

static void usage()
{
	fprintf(stdout, "sapf [-r sample-rate][-p prelude-file]\n");
	fprintf(stdout, "\n");
	fprintf(stdout, "sapf [-h]\n");
	fprintf(stdout, "    print this help\n");
	fprintf(stdout, "\n");
}

int main (int argc, const char * argv[])
{
	post("------------------------------------------------\n");
	post("A tool for the expression of sound as pure form.\n");
	post("------------------------------------------------\n");
	post("--- version %s\n", gVersionString);

	for (int i = 1; i < argc;) {
		int c = argv[i][0];
		if (c == '-') {
			c = argv[i][1];
			switch (c) {
				case 'r' : {
					if (argc <= i+1) { post("expected sample rate after -r\n"); return 1; }

					double sr = atof(argv[i+1]);
					if (sr < 1000. || sr > 768000.) { post("sample rate out of range.\n"); return 1; }
					vm.setSampleRate(sr);
					post("sample rate set to %g\n", vm.ar.sampleRate);
					i += 2;
				} break;
				case 'p' : {
					if (argc <= i+1) { post("expected prelude file name after -p\n"); return 1; }
					vm.prelude_file = argv[i+1];
					i += 2;
				} break;
				case 'h' : {
					usage();
					exit(0);
				} break;
				default:
					post("unrecognized option -%c\n", c);
			}
		} else {
			post("expected option, got \"%s\"\n", argv[i]);
			++i;
		}
	}

	vm.addBifHelp("Argument Automapping legend:");
	vm.addBifHelp("   a - as is. argument is not automapped.");
	vm.addBifHelp("   z - argument is expected to be a signal or scalar, streams are auto mapped.");
	vm.addBifHelp("   k - argument is expected to be a scalar, signals and streams are automapped.");
	vm.addBifHelp("");

	AddCoreOps();
	AddMathOps();
	AddStreamOps();
    AddRandomOps();
	AddUGenOps();
	AddMidiOps();
    AddSetOps();

	vm.log_file = getenv("SAPF_LOG");
	if (!vm.log_file) {
		const char* home_dir = getenv("HOME");
		char logfilename[PATH_MAX];
		snprintf(logfilename, PATH_MAX, "%s/sapf-log.txt", home_dir);
		vm.log_file = strdup(logfilename);
	}

	__block Thread th;

	auto m = manta();
	try {
		m->Connect();
	} catch(...) {
	}
	printf("Manta %s connected.\n", m->IsConnected() ? "is" : "IS NOT");

	dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
		/*** see at bottom for better way ***/
		while(true) {
			try {
				MantaUSB::HandleEvents();
				usleep(5000);
			} catch(...) {
				sleep(1);
			}
		}
	});

	if (!vm.prelude_file) {
		vm.prelude_file = getenv("SAPF_PRELUDE");
	}
	if (vm.prelude_file) {
		loadFile(th, vm.prelude_file);
	}

	dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
		th.repl(stdin, vm.log_file);
		exit(0);
	});

	CFRunLoopRun();

	return 0;
}

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "VM.hpp"
#include "clz.hpp"

struct SetPair
{
	V mValue;
	int mIndex;
};

class Set : public Object
{
	int mSize;
	int mCap;
	int* mIndices;
	SetPair* mPairs;

	void grow(Thread& th);
	void alloc(int cap);

	Set(const Set& that) {}
public:

	Set(int capacity) { alloc(capacity); }
    Set(Thread& th, P<List> list) { alloc(32); putAll(th, list); }

	virtual ~Set();

	virtual const char* TypeName() const override { return "Set"; }

	virtual bool isSet() const override { return true; }
	virtual bool Equals(Thread& th, Arg v) override;

	int size() { return mSize; }

	bool has(Thread& th, V& value);
    int indexOf(Thread& th, V& value);

	void put(Thread& th, V& inValue, int inIndex);

    void putAll(Thread& th, P<List>& list);

	virtual V at(int64_t i) override { return mPairs[i].mValue; }

    P<List> asVList(Thread& th);
    P<List> asZList(Thread& th);
};

bool Set::Equals(Thread& th, Arg v)
{
	if (v.Identical(this)) return true;
	if (!v.isSet()) return false;
	if (this == v.o()) return true;
	Set* that = (Set*)v.o();
	if (mSize != that->size()) return false;

	for (int64_t i = 0; i < mSize; ++i) {
		V& value = mPairs[i].mValue;
		V u;
		if (!that->has(th, value)) return false;
	}

	return true;
}

bool Set::has(Thread& th, V& value)
{
	int hash = value.Hash();
	int mask = mCap * 2 - 1;
	int index = hash & mask;
	int* indices = mIndices;
	SetPair* pairs = mPairs;

	while(1) {
		int index2 = indices[index]-1;
		if (index2 == -1) {
			return false;
		}
		V& testVal = pairs[index2].mValue;
		if (value.Equals(th, testVal)) {
			return true;
		}
		index = (index + 1) & mask;
	}

	return false;
}

int Set::indexOf(Thread& th, V& value)
{
	int hash = value.Hash();
	int mask = mCap * 2 - 1;
	int index = hash & mask;
	int* indices = mIndices;
	SetPair* pairs = mPairs;

	while(1) {
		int index2 = indices[index]-1;
		if (index2 == -1) {
			return -1;
		}
		V& testVal = pairs[index2].mValue;
		if (value.Equals(th, testVal)) {
			return pairs[index2].mIndex;
		}
		index = (index + 1) & mask;
	}

	return -1;
}

Set::~Set()
{
	delete [] mPairs;
	free(mIndices);
}

void Set::alloc(int cap)
{
	cap = NEXTPOWEROFTWO(cap);
	mPairs = new SetPair[cap];
	mIndices = (int*)calloc(2 * cap, sizeof(int));
	mCap = cap;
	mSize = 0;
}

void Set::grow(Thread& th)
{
	free(mIndices);

	SetPair* oldPairs = mPairs;
	int oldSize = mSize;

	alloc(mCap * 2);

	for (int i = 0; i < oldSize; ++i) {
		SetPair& pair = oldPairs[i];
		put(th, pair.mValue, pair.mIndex);
	}

	delete [] oldPairs;
}

void Set::put(Thread& th, V& inValue, int inIndex)
{
	if (mSize == mCap) {
		grow(th);
	}

	int hash = inValue.Hash();
	int mask = mCap * 2 - 1;
	int index = hash & mask;
	int* indices = mIndices;
	SetPair* pairs = mPairs;

	while(1) {
		int index2 = indices[index]-1;
		if (index2 == -1) {
			index2 = mSize++;
			indices[index] = index2+1;
			pairs[index2].mValue = inValue;
			pairs[index2].mIndex = inIndex;
			return;
		}
		V& testVal = pairs[index2].mValue;
		if (inValue.Equals(th, testVal)) {
			return;
		}
		index = (index + 1) & mask;
	}
}

void Set::putAll(Thread& th, P<List>& in)
{
    // caller must ensure that in is finite.
    int64_t insize = in->length(th);
    in = in->pack(th);
    for (int i = 0; i < insize; ++i) {
        V val = in->at(i);
        put(th, val, i);
    }
}

P<List> Set::asVList(Thread& th)
{
    int64_t outsize = size();

    P<List> out = new List(itemTypeV, outsize);

    for (int i = 0; i < outsize; ++i) {
        out->add(at(i));
    }

    return out;
}

P<List> Set::asZList(Thread& th)
{
    int64_t outsize = size();

    P<List> out = new List(itemTypeZ, outsize);

    for (int i = 0; i < outsize; ++i) {
        out->add(at(i));
    }

    return out;
}

static P<List> nub(Thread& th, P<List> in)
{
    P<Set> set = new Set(th, in);

    return set->asVList(th);
}

static P<List> set_or(Thread& th, P<List> a, P<List> b)
{
    P<Set> set = new Set(32);

    set->putAll(th, a);
    set->putAll(th, b);
    return a->isZ() && b->isZ() ? set->asZList(th) : set->asVList(th);
}

static P<List> set_and(Thread& th, P<List> a, P<List> b)
{
    P<Set> setA = new Set(th, a);
    P<Set> setB = new Set(th, b);
    P<List> out = new List(a->isZ() && b->isZ() ? itemTypeZ : itemTypeV, 32);

    for (int64_t i = 0; i < setA->size(); ++i) {
        V v = setA->at(i);
        if (setB->has(th, v)) out->add(v);
    }

    return out;
}

static P<List> set_minus(Thread& th, P<List> a, P<List> b)
{
    P<Set> setA = new Set(th, a);
    P<Set> setB = new Set(th, b);
    P<List> out = new List(a->isZ() && b->isZ() ? itemTypeZ : itemTypeV, 32);

    for (int64_t i = 0; i < setA->size(); ++i) {
        V v = setA->at(i);
        if (!setB->has(th, v)) out->add(v);
    }

    return out;
}

static P<List> set_xor(Thread& th, P<List> a, P<List> b)
{
    P<Set> setA = new Set(th, a);
    P<Set> setB = new Set(th, b);
    P<List> out = new List(a->isZ() && b->isZ() ? itemTypeZ : itemTypeV, 32);

    for (int64_t i = 0; i < setA->size(); ++i) {
        V v = setA->at(i);
        if (!setB->has(th, v)) out->add(v);
    }
    for (int64_t i = 0; i < setB->size(); ++i) {
        V v = setB->at(i);
        if (!setA->has(th, v)) out->add(v);
    }

    return out;
}

static bool subset(Thread& th, P<List> a, P<List> b)
{
    P<Set> setA = new Set(th, a);
    P<Set> setB = new Set(th, b);

    for (int64_t i = 0; i < setA->size(); ++i) {
        V v = setA->at(i);
        if (!setB->has(th, v)) return false;
    }

	return true;
}

static bool set_equals(Thread& th, P<List> a, P<List> b)
{
    P<Set> setA = new Set(th, a);
    P<Set> setB = new Set(th, b);

	return setA->Equals(th, setB);
}

/*

 list minus values from set.

*/

static void nub_(Thread& th, Prim* prim)
{
    P<List> a = th.popList("nub : a");

    if (!a->isFinite())
        indefiniteOp("nub : a", "");

    th.push(nub(th, a));
}

static void set_or_(Thread& th, Prim* prim)
{
    P<List> b = th.popList("|| : b");
    if (!b->isFinite())
        indefiniteOp("|| : b", "");

    P<List> a = th.popList("|| : a");

    if (!a->isFinite())
        indefiniteOp("|| : a", "");

    th.push(set_or(th, a, b));
}

static void set_and_(Thread& th, Prim* prim)
{
    P<List> b = th.popList("&& : b");
    if (!b->isFinite())
        indefiniteOp("&& : b", "");

    P<List> a = th.popList("&& : a");

    if (!a->isFinite())
        indefiniteOp("&& : a", "");

    th.push(set_and(th, a, b));
}

static void set_xor_(Thread& th, Prim* prim)
{
    P<List> b = th.popList("set_xor : b");
    if (!b->isFinite())
        indefiniteOp("set_xor : b", "");

    P<List> a = th.popList("set_xor : a");

    if (!a->isFinite())
        indefiniteOp("set_xor : a", "");

    th.push(set_xor(th, a, b));
}

static void set_minus_(Thread& th, Prim* prim)
{
    P<List> b = th.popList("set_minus : b");
    if (!b->isFinite())
        indefiniteOp("set_minus : b", "");

    P<List> a = th.popList("set_minus : a");

    if (!a->isFinite())
        indefiniteOp("set_minus : a", "");

    th.push(set_minus(th, a, b));
}

static void subset_(Thread& th, Prim* prim)
{
    P<List> b = th.popList("subset : b");
    if (!b->isFinite())
        indefiniteOp("subset : b", "");

    P<List> a = th.popList("subset : a");

    if (!a->isFinite())
        indefiniteOp("subset : a", "");

    th.pushBool(subset(th, a, b));
}

static void set_equals_(Thread& th, Prim* prim)
{
    P<List> b = th.popList("set_equals : b");
    if (!b->isFinite())
        indefiniteOp("set_equals : b", "");

    P<List> a = th.popList("set_equals : a");

    if (!a->isFinite())
        indefiniteOp("set_equals : a", "");

    th.push(set_equals(th, a, b));
}

struct FindV : Gen
{
	P<Set> mSet;
	VIn items;

	FindV(Thread& th, Arg inItems, P<Set> const& inSet)
		: Gen(th, itemTypeV, inItems.isFinite()), mSet(inSet), items(inItems) {}

	const char* TypeName() const override { return "FindV"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int astride;
			V *a;
			if (items(th, n, astride, a)) {
				setDone();
				break;
			}
			for (int i = 0; i < n; ++i) {
				out[i] = mSet->indexOf(th, *a);
				a += astride;
			}
			items.advance(n);
			framesToFill -= n;
		}
		produce(framesToFill);
	}
};

struct FindZ : Gen
{
	P<Set> mSet;
	ZIn items;

	FindZ(Thread& th, Arg inItems, P<Set> const& inSet)
		: Gen(th, itemTypeZ, inItems.isFinite()), mSet(inSet), items(inItems) {}

	const char* TypeName() const override { return "FindZ"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int astride;
			Z *a;
			if (items(th, n, astride, a)) {
				setDone();
				break;
			}
			for (int i = 0; i < n; ++i) {
				V va = *a;
				out[i] = mSet->indexOf(th, va);
				a += astride;
			}
			items.advance(n);
			framesToFill -= n;
		}
		produce(framesToFill);
	}
};

struct SetHasV : Gen
{
	P<Set> mSet;
	VIn items;

	SetHasV(Thread& th, Arg inItems, P<Set> const& inSet)
		: Gen(th, itemTypeV, inItems.isFinite()), mSet(inSet), items(inItems) {}

	const char* TypeName() const override { return "SetHasV"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		V* out = mOut->fulfill(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int astride;
			V *a;
			if (items(th, n, astride, a)) {
				setDone();
				break;
			}
			for (int i = 0; i < n; ++i) {
				out[i] = mSet->has(th, *a);
				a += astride;
			}
			items.advance(n);
			framesToFill -= n;
		}
		produce(framesToFill);
	}
};

struct SetHasZ : Gen
{
	P<Set> mSet;
	ZIn items;

	SetHasZ(Thread& th, Arg inItems, P<Set> const& inSet)
		: Gen(th, itemTypeV, inItems.isFinite()), mSet(inSet), items(inItems) {}

	const char* TypeName() const override { return "SetHasZ"; }

	virtual void pull(Thread& th) override
	{
		int framesToFill = mBlockSize;
		Z* out = mOut->fulfillz(framesToFill);
		while (framesToFill) {
			int n = framesToFill;
			int astride;
			Z *a;
			if (items(th, n, astride, a)) {
				setDone();
				break;
			}
			for (int i = 0; i < n; ++i) {
				V va = *a;
				out[i] = mSet->has(th, va);
				a += astride;
			}
			items.advance(n);
			framesToFill -= n;
		}
		produce(framesToFill);
	}
};

static V findBase(Thread& th, V& a, P<Set> const& inSet)
{
	V result;
	if (a.isList()) {
		if (a.isZList()) {
			result = new List(new FindZ(th, a, inSet));
		} else {
			result = new List(new FindV(th, a, inSet));
		}
	} else {
		result = inSet->indexOf(th, a);
	}
	return result;
}

static void find_(Thread& th, Prim* prim)
{
    P<List> b = th.popList("find : list");
    if (!b->isFinite())
        indefiniteOp("find : list", "");

	V a = th.pop();

    P<Set> setB = new Set(th, b);

	th.push(findBase(th, a, setB));
}

static V hasBase(Thread& th, V& a, P<Set> const& inSet)
{
	V result;
	if (a.isList()) {
		if (a.isZList()) {
			result = new List(new SetHasZ(th, a, inSet));
		} else {
			result = new List(new SetHasV(th, a, inSet));
		}
	} else {
		result = inSet->has(th, a);
	}
	return result;
}

static void sethas_(Thread& th, Prim* prim)
{
    P<List> b = th.popList("Shas : list");
    if (!b->isFinite())
        indefiniteOp("Shas : list", "");

	V a = th.pop();

    P<Set> setB = new Set(th, b);

	th.push(hasBase(th, a, setB));
}

#pragma mark ADD STREAM OPS

#define DEF(NAME, N, HELP) 	vm.def(#NAME, N, NAME##_, HELP);
#define DEFMCX(NAME, N, HELP) 	vm.defmcx(#NAME, N, NAME##_, HELP);
#define DEFAM(NAME, MASK, HELP) 	vm.defautomap(#NAME, #MASK, NAME##_, HELP);

void AddSetOps();
void AddSetOps()
{
	vm.addBifHelp("\n*** set operations ***");
    vm.def("S", 1, 1, nub_, "(list --> set) removes all duplicates from a finite list.");
    vm.def("S|", 2, 1, set_or_, "(listA listB --> set) returns the set union of the elements of lists A and B.");
    vm.def("S&", 2, 1, set_and_, "(listA listB --> set) returns the set intersection of the elements of lists A and B.");
    vm.def("Sx", 2, 1, set_xor_, "(listA listB --> set) returns the set of the elements which occur in list A or B, but not both.");
    vm.def("S-", 2, 1, set_minus_, "(listA listB --> set) returns the set of the elements of listA which do not occur in listB.");
    vm.def("S=", 2, 1, set_equals_, "(listA listB --> set) returns 1 if the set of elements in listA is equal to the set of elements in listB.");
    vm.def("subset?", 2, 1, subset_, "(listA listB --> set) returns 1 if the set of elements of listA is a subset of the set of elements of listB. else 0.");
    vm.def("find", 2, 1, find_, "(item(s) list --> set) returns index of item in finite list, or -1 if not in list.");
    vm.def("Shas", 2, 1, sethas_, "(item(s) list --> set) returns 1 if finite list contains item(s), else 0.");
}

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "MathFuns.hpp"
#include "VM.hpp"

double gSineTable[kSineTableSize+1];
double gDBAmpTable[kDBAmpTableSize+2];
double gDecayTable[kDecayTableSize+1];
double gFirstOrderCoeffTable[kFirstOrderCoeffTableSize+1];

inline double freqToTableF(double freq)
{
	return std::clamp(3. * log2(freq * .05), 0., 28.999);
}

Z gFreqToTable[20001];

static void initFreqToTable()
{
	for (int freq = 0; freq < 20001; ++freq) {
		gFreqToTable[freq] = freqToTableF(freq);
	}
}

inline double freqToTable(double freq)
{
	double findex = std::clamp(freq, 0., 20000.);
	double iindex = floor(findex);
	return lut(gFreqToTable, (int)iindex, findex - iindex);
}

////////////////////////////////////////////////////////////////////////////////////

void fillSineTable()
{
	for (int i = 0; i < kSineTableSize; ++i) {
		gSineTable[i] = sin(gSineTableOmega * i);
	}
	gSineTable[kSineTableSize] = gSineTable[0];
}

void fillDBAmpTable()
{
	for (int i = 0; i < kDBAmpTableSize+2; ++i) {
		double dbgain = i * kInvDBAmpScale - kDBAmpOffset;
		double amp = pow(10., .05 * dbgain);
		gDBAmpTable[i] = amp;
	}
}

void fillDecayTable()
{
	for (int i = 0; i < kDecayTableSize+1; ++i) {
		gDecayTable[i] = exp(log001 * i * .001);
	}
}

void fillFirstOrderCoeffTable()
{
	double k = M_PI * kInvFirstOrderCoeffTableSize;
	for (int i = 0; i < kFirstOrderCoeffTableSize+1; ++i) {
		double x = k * i;
		Z b = 2. - cos(x);
		gFirstOrderCoeffTable[i] = b - sqrt(b*b - 1.);
	}
}

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "Opcode.hpp"
#include "clz.hpp"

const char* opcode_name[kNumOpcodes] =
{
	"BAD OPCODE",
	"opNone",
	"opPushImmediate",
	"opPushLocalVar",
	"opPushFunVar",
	"opPushWorkspaceVar",

	"opPushFun",

	"opCallImmediate",
	"opCallLocalVar",
	"opCallFunVar",
	"opCallWorkspaceVar",

	"opDot",
	"opComma",
	"opBindLocal",
	"opBindLocalFromList",
	"opBindWorkspaceVar",
	"opBindWorkspaceVarFromList",

	"opParens",
	"opNewVList",
	"opNewZList",
	"opNewForm",
	"opInherit",
	"opEach",
	"opReturn"
};

static void printOpcode(Thread& th, Opcode* c)
{
	V& v = c->v;
	post("%p %s ", c, opcode_name[c->op]);
	switch (c->op) {
		case opPushImmediate :
		case opPushWorkspaceVar :
		case opPushFun :
		case opCallImmediate :
		case opCallWorkspaceVar :
		case opDot :
		case opComma :
		case opInherit :
		case opNewForm :
		case opBindWorkspaceVar :
		case opBindWorkspaceVarFromList :
		case opParens :
		case opNewVList :
		case opNewZList :
			v.printShort(th);
			break;

		case opPushLocalVar :
		case opPushFunVar :
		case opCallLocalVar :
		case opCallFunVar :
		case opBindLocal :
		case opBindLocalFromList :
			post("%lld", (int64_t)v.i);
			break;
		case opEach :
			post("%llx", (int64_t)v.i);
			break;

		case opNone :
		case opReturn :
			break;

		default :
			post("BAD OPCODE\n");
	}
	post("\n");
}

void Thread::run(Opcode* opc)
{
	Thread& th = *this;
	try {
		for (;;++opc) {

			V& v = opc->v;

			if (vm.traceon) {
				post("stack : "); th.printStack(); post("\n");
				printOpcode(th, opc);
			}

			switch (opc->op) {
				case opNone :
					break;

				case opPushImmediate :
					push(v);
					break;

				case opPushLocalVar :
					push(getLocal(v.i));
					break;

				case opPushFunVar :
					push(fun->mVars[v.i]);
					break;

				case opPushWorkspaceVar :
					push(fun->Workspace()->mustGet(th, v));
					break;

				case opPushFun : {
						push(new Fun(th, (FunDef*)v.o()));
					} break;

				case opCallImmediate :
					v.apply(th);
					break;

				case opCallLocalVar :
					getLocal(v.i).apply(th);
					break;

				case opCallFunVar :
					fun->mVars[v.i].apply(th);
					break;

				case opCallWorkspaceVar :
					fun->Workspace()->mustGet(th, v).apply(th);
					break;

				case opDot : {
					V ioValue;
					if (!pop().dot(th, v, ioValue))
						notFound(v);
					push(ioValue);
					break;
				}
				case opComma :
					push(pop().comma(th, v));
					break;

				case opBindLocal :
					getLocal(v.i) = pop();
					break;
				case opBindWorkspaceVar : {
                    V value = pop();
                    if (value.isList() && !value.isFinite()) {
                        post("WARNING: binding a possibly infinite list at the top level can leak unbounded memory!\n");
                    } else if (value.isFun()) {
						const char* mask = value.GetAutoMapMask();
						const char* help = value.OneLineHelp();
						if (mask || help) {
							char* name = ((String*)v.o())->s;
							vm.addUdfHelp(name, mask, help);
						}
					}
					fun->Workspace() = fun->Workspace()->putImpure(v, value); // workspace mutation
					th.mWorkspace = th.mWorkspace->putImpure(v, value); // workspace mutation
                } break;

				case opBindLocalFromList :
				case opBindWorkspaceVarFromList :
				{
					V list = pop();
					BothIn in(list);
					while (1) {
						if (opc->op == opNone) {
							break;
						} else {
							V value;
							if (in.one(th, value)) {
								post("not enough items in list for = [..]\n");
								throw errFailed;
							}
							if (opc->op == opBindLocalFromList) {
								getLocal(opc->v.i) = value;
							} else if (opc->op == opBindWorkspaceVarFromList) {
								v = opc->v;
								if (value.isList() && !value.isFinite()) {
									post("WARNING: binding a possibly infinite list at the top level can leak unbounded memory!\n");
								} else if (value.isFun()) {
									const char* mask = value.GetAutoMapMask();
									const char* help = value.OneLineHelp();
									if (mask || help) {
										char* name = ((String*)v.o())->s;
										vm.addUdfHelp(name, mask, help);
									}
								}
								fun->Workspace() = fun->Workspace()->putImpure(v, value); // workspace mutation
								th.mWorkspace = th.mWorkspace->putImpure(v, value); // workspace mutation
							}
						}
						++opc;
					}
				} break;
				case opParens : {
						ParenStack ss(th);
						run(((Code*)v.o())->getOps());
					} break;
				case opNewVList : {
						V x;
						{
							SaveStack ss(th);
							run(((Code*)v.o())->getOps());
							size_t len = stackDepth();
							vm.newVList->apply_n(th, len);
							x = th.pop();
						}
						th.push(x);
					} break;
				case opNewZList : {
						V x;
						{
							SaveStack ss(th);
							run(((Code*)v.o())->getOps());
							size_t len = stackDepth();
							vm.newZList->apply_n(th, len);
							x = th.pop();
						}
						th.push(x);
					} break;
				case opInherit : {
						V result;
						{
							SaveStack ss(th);
							run(((Code*)v.o())->getOps());
							size_t depth = stackDepth();
							if (depth < 1) {
								result = vm._ee;
							} else if (depth > 1) {
								fprintf(stderr, "more arguments than keys for form.\n");
								throw errFailed;
							} else {
								vm.inherit->apply_n(th, 1);
								result = th.pop();
							}
						}
						th.push(result);
					} break;
				case opNewForm : {
						V result;
						{
							SaveStack ss(th);
							run(((Code*)v.o())->getOps());
							size_t depth = stackDepth();
							TableMap* tmap = (TableMap*)th.top().o();
							size_t numArgs = tmap->mSize;
							if (depth == numArgs+1) {
								// no inheritance, must insert zero for parent.
								th.tuck(numArgs+1, V(0.));
							} else if (depth < numArgs+1) {
								fprintf(stderr, "fewer arguments than keys for form.\n");
								throw errStackUnderflow;
							} else if (depth > numArgs+2) {
								fprintf(stderr, "more arguments than keys for form.\n");
								throw errFailed;
							}
							vm.newForm->apply_n(th, numArgs+2);
							result = th.pop();
						}
						th.push(result);
					} break;
				case opEach :
					push(new EachOp(pop(), (int)v.i));
					break;

				case opReturn : return;

				default :
					post("BAD OPCODE\n");
					throw errInternalError;
			}
		}
	} catch (...) {
		post("backtrace: %s ", opcode_name[opc->op]);
		opc->v.printShort(th);
		post("\n");
		throw;
	}
}

Code::~Code() { }

void Code::shrinkToFit()
{
	std::vector<Opcode>(ops.begin(), ops.end()).swap(ops);
}

void Code::add(int _op, Arg v)
{
	ops.push_back(Opcode(_op, v));
}

void Code::add(int _op, double f)
{
	add(_op, V(f));
}

void Code::addAll(const P<Code> &that)
{
	for (Opcode& op : that->ops) {
		ops.push_back(op);
	}
}

void Code::decompile(Thread& th, std::string& out)
{
	for (Opcode& c : ops) {
		V& v = c.v;
		switch (c.op) {
			case opPushImmediate : {
				std::string s;
				v.printShort(th, s);
				out += s;
				break;
			}
			case opPushWorkspaceVar :
			case opPushFun :
			case opCallImmediate :
			case opCallWorkspaceVar :
			case opDot :
			case opComma :
			case opInherit :
			case opNewForm :
			case opBindWorkspaceVar :
			case opBindWorkspaceVarFromList :
			case opParens :
			case opNewVList :
			case opNewZList :
				v.printShort(th);
				break;

			case opPushLocalVar :
			case opPushFunVar :
			case opCallLocalVar :
			case opCallFunVar :
			case opBindLocal :
			case opBindLocalFromList :
				post("%lld", (int64_t)v.i);
				break;
			case opEach :
				post("%llx", (int64_t)v.i);
				break;

			case opNone :
			case opReturn :
				break;

			default :
				post("BAD OPCODE\n");
		}
	}
}

class MantaUSB {
  public:
    MantaUSB(void) {
   mantaList.push_back(this);
   MantaIndex = int(mantaList.size());

   DebugPrint("%s-%d: Manta %d initialized", __FILE__, __LINE__, MantaIndex);
}
    virtual ~MantaUSB(void) {
   Disconnect();
   mantaList.remove(this);
   if(mantaList.empty())
   {
      hid_exit();
   }
}
    void WriteFrame(uint8_t *frame, bool forceQueued) {
   if(NULL == DeviceHandle)
   {
      throw(MantaNotConnectedException(this));
   }
   MantaTxQueueEntry *queuedMessage = GetQueuedTxMessage();
   if(queuedMessage && !forceQueued)
   {
      /* replace the queued packet payload with the new one */
      for(int i = 0; i < OutPacketLen; ++i)
      {
         /* the first byte of the report is the report ID (0x00) */
         queuedMessage->OutFrame[i+1] = frame[i];
      }
      DebugPrint("%s-%d: (WriteFrame) Queued Transfer overwritten on Manta %d",
            __FILE__, __LINE__, GetSerialNumber());
   }
   else
   {
      /* no transfer in progress, queue up a new one */
      MantaTxQueueEntry *newMessage = new MantaTxQueueEntry;
      newMessage->OutFrame[0] = 0;
      newMessage->TargetManta = this;
      /* the first byte of the report is the report ID (0x00) */
      memcpy(newMessage->OutFrame + 1, frame, OutPacketLen);
      txQueue.push_back(newMessage);
      DebugPrint("%s-%d: (WriteFrame) Transfer Queued on Manta %d",
            __FILE__, __LINE__, GetSerialNumber());
   }
}
    bool IsConnected(void) {
   return DeviceHandle != NULL;
}
    void Connect(int connectionSerial = 0) {
#define SERIAL_STRING_SIZE 32
   wchar_t serialString[SERIAL_STRING_SIZE];

   if(IsConnected())
   {
      return;
   }

   DebugPrint("%s-%d: Attempting to Connect to Manta %d...",
         __FILE__, __LINE__, connectionSerial);
   if(connectionSerial)
   {
      swprintf(serialString, SERIAL_STRING_SIZE, L"%d", connectionSerial);
      DeviceHandle = hid_open(VendorID, ProductID, serialString);
   }
   else
   {
      DeviceHandle = hid_open(VendorID, ProductID, NULL);
   }
   if(NULL == DeviceHandle)
      throw(MantaNotFoundException());
   hid_get_serial_number_string(DeviceHandle, serialString, SERIAL_STRING_SIZE);
   SerialNumber = int(wcstol(serialString, NULL, 10));
   int rc = hid_set_nonblocking(DeviceHandle, 1);
   printf("hid_set_nonblocking %d\n", rc);
   printf("SerialNumber %d\n", SerialNumber);
}
    void Disconnect() {
   if(! IsConnected())
   {
      return;
   }

   DebugPrint("%s-%d: Manta %d Disconnecting...", __FILE__, __LINE__, GetSerialNumber());
   hid_close(DeviceHandle);
   DeviceHandle = NULL;
}
    int GetSerialNumber(void) {
   return SerialNumber;
}
    int GetHardwareVersion(void) {
   return (SerialNumber < 70) ? 1 : 2;
}
    bool MessageQueued(void) {
   return GetQueuedTxMessage() != NULL;
}
    static void HandleEvents(void) {
   list<MantaUSB *>::iterator i = mantaList.begin();
   /* read from each manta and trigger any events */
   while(mantaList.end() != i)
   {
      MantaUSB *current = *i;
      if(current->IsConnected())
      {
         int bytesRead;
         int8_t inFrame[InPacketLen];

         bytesRead = hid_read(current->DeviceHandle,
               reinterpret_cast<uint8_t *>(inFrame), InPacketLen);
         if(bytesRead < 0)
         {
            current->DebugPrint("%s-%d: Read error on Manta %d",
                  __FILE__, __LINE__, current->GetSerialNumber());
            throw(MantaCommunicationException(current));
         }
         else if(bytesRead)
         {
            current->FrameReceived(inFrame);
         }
      }
      ++i;
   }

   /* pop one item off the transmit queue and send down to its target */
   if(! txQueue.empty())
   {
      int bytesWritten;
      MantaTxQueueEntry *txMessage = txQueue.front();
      txQueue.pop_front();
      bytesWritten = hid_write(txMessage->TargetManta->DeviceHandle,
            txMessage->OutFrame, OutPacketLen + 1);
      txMessage->TargetManta->DebugPrint("%s-%d: Frame Written to Manta %d",
            __FILE__, __LINE__, txMessage->TargetManta->GetSerialNumber());
      for(int j = 0; j < 16; j += 8)
      {
         uint8_t *frame = txMessage->OutFrame + 1;
         txMessage->TargetManta->DebugPrint("\t\t%x %x %x %x %x %x %x %x",
               frame[j], frame[j+1], frame[j+2], frame[j+3], frame[j+4],
               frame[j+5], frame[j+6], frame[j+7]);
      }
      delete txMessage;
      if(bytesWritten < 0)
      {
         txMessage->TargetManta->DebugPrint("%s-%d: Write error on Manta %d",
               __FILE__, __LINE__, txMessage->TargetManta->GetSerialNumber());
         throw(MantaCommunicationException(txMessage->TargetManta));
      }
   }
}
  protected:
    virtual void FrameReceived(int8_t *frame) = 0;
    static const int OutPacketLen = 16;
    static const int InPacketLen = 64;
    int SerialNumber;
    int MantaIndex;
  private:
    struct MantaTxQueueEntry
      {
         MantaUSB *TargetManta;
         uint8_t OutFrame[17];
      };
    MantaTxQueueEntry *GetQueuedTxMessage();
    static const int Interface = 0;
    static const int EndpointIn = 0x81;
    static const int EndpointOut = 0x02;
    static const int Timeout = 5000;
    static const int VendorID = 0x2424;
    static const int ProductID = 0x2424;
    hid_device *DeviceHandle;
    static list<MantaUSB *> mantaList;
    static list<MantaTxQueueEntry *> txQueue;
};

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "symbol.hpp"
#include "VM.hpp"
#include "Hash.hpp"
#include <string.h>
#include <atomic>

const int kSymbolTableSize = 4096;
const int kSymbolTableMask = kSymbolTableSize - 1;

// global atomic symbol table
volatile std::atomic<String*> sSymbolTable[kSymbolTableSize];

static String* SymbolTable_lookup(String* list, const char* name, int32_t hash)
{
	while (list) {
		if (list->hash == hash && strcmp(list->s, name) == 0)
			return list;
		list = list->nextSymbol;
	}
	return nullptr;
}

static String* SymbolTable_lookup(const char* name, int hash)
{
	return SymbolTable_lookup(sSymbolTable[hash & kSymbolTableMask].load(), name, hash);
}

static String* SymbolTable_lookup(const char* name)
{
	uintptr_t hash = Hash(name);
	return SymbolTable_lookup(name, (int)hash);
}

P<String> getsym(const char* name)
{
	// thread safe

	int32_t hash = Hash(name);
    int32_t binIndex = hash & kSymbolTableMask;
	volatile std::atomic<String*>* bin = &sSymbolTable[binIndex];
	while (1) {
        // get the head of the list.
		String* head = bin->load();
        // search the list for the symbol
		String* existingSymbol = head;
		while (existingSymbol) {
			if (existingSymbol->hash == hash && strcmp(existingSymbol->s, name) == 0) {
				return existingSymbol;
			}
			existingSymbol = existingSymbol->nextSymbol;
		}
		String* newSymbol = new String(name, hash, head);
		if (bin->compare_exchange_weak(head, newSymbol)) {
			newSymbol->retain();
			return newSymbol;
		}
        delete newSymbol;
	}
}

/*******************************************************
 HIDAPI - Multi-Platform library for
 communication with HID devices.

 Alan Ott
 Signal 11 Software

 8/22/2009

 Copyright 2009, All Rights Reserved.

 At the discretion of the user of this library,
 this software may be licensed under the terms of the
 GNU Public License v3, a BSD-Style license, or the
 original HIDAPI license as outlined in the LICENSE.txt,
 LICENSE-gpl3.txt, LICENSE-bsd.txt, and LICENSE-orig.txt
 files located at the root of the source distribution.
 These files may also be found in the public source
 code repository located at:
        http://github.com/signal11/hidapi .
********************************************************/

/** @file
 * @defgroup API hidapi API
 */

#ifndef HIDAPI_H__
#define HIDAPI_H__

#include <wchar.h>

#ifdef _WIN32
      #define HID_API_EXPORT __declspec(dllexport)
      #define HID_API_CALL
#else
      #define HID_API_EXPORT /**< API export macro */
      #define HID_API_CALL /**< API call macro */
#endif

#define HID_API_EXPORT_CALL HID_API_EXPORT HID_API_CALL /**< API export and call macro*/

#ifdef __cplusplus
extern "C" {
#endif
		struct hid_device_;
		typedef struct hid_device_ hid_device; /**< opaque hidapi structure */

		/** hidapi info structure */
		struct hid_device_info {
			/** Platform-specific device path */
			char *path;
			/** Device Vendor ID */
			unsigned short vendor_id;
			/** Device Product ID */
			unsigned short product_id;
			/** Serial Number */
			wchar_t *serial_number;
			/** Device Release Number in binary-coded decimal,
			    also known as Device Version Number */
			unsigned short release_number;
			/** Manufacturer String */
			wchar_t *manufacturer_string;
			/** Product string */
			wchar_t *product_string;
			/** Usage Page for this Device/Interface
			    (Windows/Mac only). */
			unsigned short usage_page;
			/** Usage for this Device/Interface
			    (Windows/Mac only).*/
			unsigned short usage;
			/** The USB interface which this logical device
			    represents. Valid on both Linux implementations
			    in all cases, and valid on the Windows implementation
			    only if the device contains more than one interface. */
			int interface_number;

			/** Pointer to the next device */
			struct hid_device_info *next;
		};

		/** @brief Initialize the HIDAPI library.

			This function initializes the HIDAPI library. Calling it is not
			strictly necessary, as it will be called automatically by
			hid_enumerate() and any of the hid_open_*() functions if it is
			needed.  This function should be called at the beginning of
			execution however, if there is a chance of HIDAPI handles
			being opened by different threads simultaneously.

			@ingroup API

			@returns
				This function returns 0 on success and -1 on error.
		*/
		int HID_API_EXPORT HID_API_CALL hid_init(void);

		/** @brief Finalize the HIDAPI library.

			This function frees all of the static data associated with
			HIDAPI. It should be called at the end of execution to avoid
			memory leaks.

			@ingroup API

		    @returns
				This function returns 0 on success and -1 on error.
		*/
		int HID_API_EXPORT HID_API_CALL hid_exit(void);

		/** @brief Enumerate the HID Devices.

			This function returns a linked list of all the HID devices
			attached to the system which match vendor_id and product_id.
			If @p vendor_id and @p product_id are both set to 0, then
			all HID devices will be returned.

			@ingroup API
			@param vendor_id The Vendor ID (VID) of the types of device
				to open.
			@param product_id The Product ID (PID) of the types of
				device to open.

		    @returns
		    	This function returns a pointer to a linked list of type
		    	struct #hid_device, containing information about the HID devices
		    	attached to the system, or NULL in the case of failure. Free
		    	this linked list by calling hid_free_enumeration().
		*/
		struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id);

		/** @brief Free an enumeration Linked List

		    This function frees a linked list created by hid_enumerate().

			@ingroup API
		    @param devs Pointer to a list of struct_device returned from
		    	      hid_enumerate().
		*/
		void  HID_API_EXPORT HID_API_CALL hid_free_enumeration(struct hid_device_info *devs);

		/** @brief Open a HID device using a Vendor ID (VID), Product ID
			(PID) and optionally a serial number.

			If @p serial_number is NULL, the first device with the
			specified VID and PID is opened.

			@ingroup API
			@param vendor_id The Vendor ID (VID) of the device to open.
			@param product_id The Product ID (PID) of the device to open.
			@param serial_number The Serial Number of the device to open
				               (Optionally NULL).

			@returns
				This function returns a pointer to a #hid_device object on
				success or NULL on failure.
		*/
		HID_API_EXPORT hid_device * HID_API_CALL hid_open(unsigned short vendor_id, unsigned short product_id, const wchar_t *serial_number);

		/** @brief Open a HID device by its path name.

			The path name be determined by calling hid_enumerate(), or a
			platform-specific path name can be used (eg: /dev/hidraw0 on
			Linux).

			@ingroup API
		    @param path The path name of the device to open

			@returns
				This function returns a pointer to a #hid_device object on
				success or NULL on failure.
		*/
		HID_API_EXPORT hid_device * HID_API_CALL hid_open_path(const char *path);

		/** @brief Write an Output report to a HID device.

			The first byte of @p data[] must contain the Report ID. For
			devices which only support a single report, this must be set
			to 0x0. The remaining bytes contain the report data. Since
			the Report ID is mandatory, calls to hid_write() will always
			contain one more byte than the report contains. For example,
			if a hid report is 16 bytes long, 17 bytes must be passed to
			hid_write(), the Report ID (or 0x0, for devices with a
			single report), followed by the report data (16 bytes). In
			this example, the length passed in would be 17.

			hid_write() will send the data on the first OUT endpoint, if
			one exists. If it does not, it will send the data through
			the Control Endpoint (Endpoint 0).

			@ingroup API
			@param device A device handle returned from hid_open().
			@param data The data to send, including the report number as
				the first byte.
			@param length The length in bytes of the data to send.

			@returns
				This function returns the actual number of bytes written and
				-1 on error.
		*/
		int  HID_API_EXPORT HID_API_CALL hid_write(hid_device *device, const unsigned char *data, size_t length);

		/** @brief Read an Input report from a HID device with timeout.

			Input reports are returned
			to the host through the INTERRUPT IN endpoint. The first byte will
			contain the Report number if the device uses numbered reports.

			@ingroup API
			@param device A device handle returned from hid_open().
			@param data A buffer to put the read data into.
			@param length The number of bytes to read. For devices with
				multiple reports, make sure to read an extra byte for
				the report number.
			@param milliseconds timeout in milliseconds or -1 for blocking wait.

			@returns
				This function returns the actual number of bytes read and
				-1 on error.
		*/
		int HID_API_EXPORT HID_API_CALL hid_read_timeout(hid_device *dev, unsigned char *data, size_t length, int milliseconds);

		/** @brief Read an Input report from a HID device.

			Input reports are returned
		    to the host through the INTERRUPT IN endpoint. The first byte will
			contain the Report number if the device uses numbered reports.

			@ingroup API
			@param device A device handle returned from hid_open().
			@param data A buffer to put the read data into.
			@param length The number of bytes to read. For devices with
				multiple reports, make sure to read an extra byte for
				the report number.

			@returns
				This function returns the actual number of bytes read and
				-1 on error.
		*/
		int  HID_API_EXPORT HID_API_CALL hid_read(hid_device *device, unsigned char *data, size_t length);

		/** @brief Set the device handle to be non-blocking.

			In non-blocking mode calls to hid_read() will return
			immediately with a value of 0 if there is no data to be
			read. In blocking mode, hid_read() will wait (block) until
			there is data to read before returning.

			Nonblocking can be turned on and off at any time.

			@ingroup API
			@param device A device handle returned from hid_open().
			@param nonblock enable or not the nonblocking reads
			 - 1 to enable nonblocking
			 - 0 to disable nonblocking.

			@returns
				This function returns 0 on success and -1 on error.
		*/
		int  HID_API_EXPORT HID_API_CALL hid_set_nonblocking(hid_device *device, int nonblock);

		/** @brief Send a Feature report to the device.

			Feature reports are sent over the Control endpoint as a
			Set_Report transfer.  The first byte of @p data[] must
			contain the Report ID. For devices which only support a
			single report, this must be set to 0x0. The remaining bytes
			contain the report data. Since the Report ID is mandatory,
			calls to hid_send_feature_report() will always contain one
			more byte than the report contains. For example, if a hid
			report is 16 bytes long, 17 bytes must be passed to
			hid_send_feature_report(): the Report ID (or 0x0, for
			devices which do not use numbered reports), followed by the
			report data (16 bytes). In this example, the length passed
			in would be 17.

			@ingroup API
			@param device A device handle returned from hid_open().
			@param data The data to send, including the report number as
				the first byte.
			@param length The length in bytes of the data to send, including
				the report number.

			@returns
				This function returns the actual number of bytes written and
				-1 on error.
		*/
		int HID_API_EXPORT HID_API_CALL hid_send_feature_report(hid_device *device, const unsigned char *data, size_t length);

		/** @brief Get a feature report from a HID device.

			Make sure to set the first byte of @p data[] to the Report
			ID of the report to be read.  Make sure to allow space for
			this extra byte in @p data[].

			@ingroup API
			@param device A device handle returned from hid_open().
			@param data A buffer to put the read data into, including
				the Report ID. Set the first byte of @p data[] to the
				Report ID of the report to be read.
			@param length The number of bytes to read, including an
				extra byte for the report ID. The buffer can be longer
				than the actual report.

			@returns
				This function returns the number of bytes read and
				-1 on error.
		*/
		int HID_API_EXPORT HID_API_CALL hid_get_feature_report(hid_device *device, unsigned char *data, size_t length);

		/** @brief Close a HID device.

			@ingroup API
			@param device A device handle returned from hid_open().
		*/
		void HID_API_EXPORT HID_API_CALL hid_close(hid_device *device);

		/** @brief Get The Manufacturer String from a HID device.

			@ingroup API
			@param device A device handle returned from hid_open().
			@param string A wide string buffer to put the data into.
			@param maxlen The length of the buffer in multiples of wchar_t.

			@returns
				This function returns 0 on success and -1 on error.
		*/
		int HID_API_EXPORT_CALL hid_get_manufacturer_string(hid_device *device, wchar_t *string, size_t maxlen);

		/** @brief Get The Product String from a HID device.

			@ingroup API
			@param device A device handle returned from hid_open().
			@param string A wide string buffer to put the data into.
			@param maxlen The length of the buffer in multiples of wchar_t.

			@returns
				This function returns 0 on success and -1 on error.
		*/
		int HID_API_EXPORT_CALL hid_get_product_string(hid_device *device, wchar_t *string, size_t maxlen);

		/** @brief Get The Serial Number String from a HID device.

			@ingroup API
			@param device A device handle returned from hid_open().
			@param string A wide string buffer to put the data into.
			@param maxlen The length of the buffer in multiples of wchar_t.

			@returns
				This function returns 0 on success and -1 on error.
		*/
		int HID_API_EXPORT_CALL hid_get_serial_number_string(hid_device *device, wchar_t *string, size_t maxlen);

		/** @brief Get a string from a HID device, based on its string index.

			@ingroup API
			@param device A device handle returned from hid_open().
			@param string_index The index of the string to get.
			@param string A wide string buffer to put the data into.
			@param maxlen The length of the buffer in multiples of wchar_t.

			@returns
				This function returns 0 on success and -1 on error.
		*/
		int HID_API_EXPORT_CALL hid_get_indexed_string(hid_device *device, int string_index, wchar_t *string, size_t maxlen);

		/** @brief Get a string describing the last error which occurred.

			@ingroup API
			@param device A device handle returned from hid_open().

			@returns
				This function returns a string containing the last error
				which occurred or NULL if none has occurred.
		*/
		HID_API_EXPORT const wchar_t* HID_API_CALL hid_error(hid_device *device);

#ifdef __cplusplus
}
#endif

#endif

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "primes.hpp"
#include "ErrorCodes.hpp"
#include <stdint.h>

// Within a cycle of 30, there are only 8 numbers that are not multiples of 2, 3 or 5.
// We pack these 8 into a one byte bit map.

const int gLowPrimes[10] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};
const int gPrimeOffsets[8]   = {1, 7, 11, 13, 17, 19, 23, 29};
const int gPrimesShift[30] =
{
	-1,  0, -1, -1, -1, -1, -1,  1, -1, -1,
	-1,  2, -1,  3, -1, -1, -1,  4, -1,  5,
	-1, -1, -1,  6, -1, -1, -1, -1, -1,  7
};

const uint32_t gLowPrimesMask = 0x208a28ac;

//const int kPrimeUpperLimit = kPrimesMaskSize * 30;
uint8_t gPrimesMask[kPrimesMaskSize] = {
	0xdf, 0xef, 0x7e, 0xb6, 0xdb, 0x3d, 0xf9, 0xd5, 0x4f, 0x1e, 0xf3, 0xea, 0xa6, 0xed, 0x9e, 0xe6,
	0x0c, 0xd3, 0xd3, 0x3b, 0xdd, 0x59, 0xa5, 0x6a, 0x67, 0x92, 0xbd, 0x78, 0x1e, 0xa6, 0x56, 0x56,
	0xe3, 0xad, 0x2d, 0xde, 0x2a, 0x4c, 0x55, 0xd9, 0xa3, 0xf0, 0x9f, 0x03, 0x54, 0xa1, 0xf8, 0x2e,
	0xfd, 0x44, 0xe9, 0x66, 0xf6, 0x13, 0x3a, 0xb8, 0x4c, 0x2b, 0x3a, 0x45, 0x11, 0xbf, 0x54, 0x8c,
	0xc1, 0x7a, 0xb3, 0xc8, 0xbc, 0x8c, 0x4f, 0x21, 0x58, 0x71, 0x71, 0x9b, 0xc1, 0x17, 0xef, 0x54,
	0x96, 0x1a, 0x08, 0xe5, 0x83, 0x8c, 0x46, 0x72, 0xfb, 0xae, 0x65, 0x92, 0x8f, 0x58, 0x87, 0xd2,
	0x92, 0xd8, 0x81, 0x65, 0x26, 0xe3, 0xa0, 0x11, 0x38, 0xc7, 0x26, 0x3c, 0x81, 0xeb, 0x99, 0x8d,
	0x51, 0x88, 0x3e, 0x24, 0xf3, 0x33, 0x4d, 0x5a, 0x8b, 0x1c, 0xa7, 0x2a, 0xb4, 0x58, 0x4c, 0x4e,
	0x26, 0xf6, 0x19, 0x82, 0xdc, 0x83, 0xc3, 0x2c, 0xf1, 0x38, 0x02, 0xb5, 0xcd, 0xcd, 0x02, 0xb2,
	0x4a, 0x94, 0x0c, 0x57, 0x4c, 0x7a, 0x30, 0x43, 0x0b, 0xf1, 0xcb, 0x44, 0x6c, 0x24, 0xf8, 0x19,
	0x01, 0x95, 0xa8, 0x5c, 0x73, 0xea, 0x8d, 0x24, 0x96, 0x2b, 0x50, 0xa6, 0x22, 0x1e, 0xc4, 0xd1,
	0x48, 0x06, 0xd4, 0x3a, 0x2f, 0x74, 0x9c, 0x07, 0x6a, 0x05, 0x88, 0xbf, 0x68, 0x15, 0x2e, 0x60,
	0x55, 0xe3, 0xb7, 0x51, 0x98, 0x08, 0x14, 0x86, 0x5a, 0xaa, 0x45, 0x4d, 0x49, 0x70, 0x27, 0xd2,
	0x93, 0xd5, 0xca, 0xab, 0x02, 0x83, 0x61, 0x05, 0x24, 0xce, 0x87, 0x22, 0xc2, 0xa9, 0xad, 0x18,
	0x8c, 0x4d, 0x78, 0xd1, 0x89, 0x16, 0xb0, 0x57, 0xc7, 0x62, 0xa2, 0xc0, 0x34, 0x24, 0x52, 0xae,
	0x5a, 0x40, 0x32, 0x8d, 0x21, 0x08, 0x43, 0x34, 0xb6, 0xd2, 0xb6, 0xd9, 0x19, 0xe1, 0x60, 0x67,
	0x1a, 0x39, 0x60, 0xd0, 0x44, 0x7a, 0x94, 0x9a, 0x09, 0x88, 0x83, 0xa8, 0x74, 0x55, 0x10, 0x27,
	0xa1, 0x5d, 0x68, 0x1e, 0x23, 0xc8, 0x32, 0xe0, 0x19, 0x03, 0x44, 0x73, 0x48, 0xb1, 0x38, 0xc3,
	0xe6, 0x2a, 0x57, 0x61, 0x98, 0xb5, 0x1c, 0x0a, 0x68, 0xc5, 0x81, 0x8f, 0xac, 0x02, 0x29, 0x1a,
	0x47, 0xe3, 0x94, 0x11, 0x4e, 0x64, 0x2e, 0x14, 0xcb, 0x3d, 0xdc, 0x14, 0xc5, 0x06, 0x10, 0xe9,
	0x29, 0xb1, 0x82, 0xe9, 0x30, 0x47, 0xe3, 0x34, 0x19, 0xc3, 0x25, 0x0a, 0x30, 0x30, 0xb4, 0x6c,
	0xc1, 0xe5, 0x46, 0x44, 0xd8, 0x8e, 0x4c, 0x5d, 0x22, 0x24, 0x70, 0x78, 0x92, 0x89, 0x81, 0x82,
	0x56, 0x26, 0x1b, 0x86, 0xe9, 0x08, 0xa5, 0x00, 0xd3, 0xc3, 0x29, 0xb0, 0xc2, 0x4a, 0x10, 0xb2,
	0x59, 0x38, 0xa1, 0x1d, 0x42, 0x60, 0xc7, 0x22, 0x27, 0x8c, 0xc8, 0x44, 0x1a, 0xc6, 0x8b, 0x82,
	0x81, 0x1a, 0x46, 0x10, 0xa6, 0x31, 0x09, 0xf0, 0x54, 0x2f, 0x18, 0xd2, 0xd8, 0xa9, 0x15, 0x06,
	0x2e, 0x0c, 0xf6, 0xc0, 0x0e, 0x50, 0x91, 0xcd, 0x26, 0xc1, 0x18, 0x38, 0x65, 0x19, 0xc3, 0x56,
	0x93, 0x8b, 0x2a, 0x2d, 0xd6, 0x84, 0x4a, 0x61, 0x0a, 0xa5, 0x2c, 0x09, 0xe0, 0x76, 0xc4, 0x6a,
	0x3c, 0xd8, 0x08, 0xe8, 0x14, 0x66, 0x1b, 0xb0, 0xa4, 0x02, 0x63, 0x36, 0x10, 0x31, 0x07, 0xd5,
	0x92, 0x48, 0x42, 0x12, 0xc3, 0x8a, 0xa0, 0x9f, 0x2d, 0x74, 0xa4, 0x82, 0x85, 0x78, 0x5c, 0x0d,
	0x18, 0xb0, 0x61, 0x14, 0x1d, 0x02, 0xe8, 0x18, 0x12, 0xc1, 0x01, 0x49, 0x1c, 0x83, 0x30, 0x67,
	0x33, 0xa1, 0x88, 0xd8, 0x0f, 0x0c, 0xf4, 0x98, 0x88, 0x58, 0xd7, 0x66, 0x42, 0x47, 0xb1, 0x16,
	0xa8, 0x96, 0x08, 0x18, 0x41, 0x59, 0x15, 0xb5, 0x44, 0x2a, 0x52, 0xe1, 0xb3, 0xaa, 0xa1, 0x59,
	0x45, 0x62, 0x55, 0x18, 0x11, 0xa5, 0x0c, 0xa3, 0x3c, 0x67, 0x00, 0xbe, 0x54, 0xd6, 0x0a, 0x20,
	0x36, 0x6b, 0x82, 0x0c, 0x15, 0x08, 0x7e, 0x56, 0x91, 0x01, 0x78, 0xd0, 0x61, 0x0a, 0x84, 0xa8,
	0x2c, 0x01, 0x57, 0x0e, 0x56, 0xa0, 0x50, 0x0b, 0x98, 0x8c, 0x47, 0x6c, 0x20, 0x63, 0x10, 0xc4,
	0x09, 0xe4, 0x0c, 0x57, 0x88, 0x0b, 0x75, 0x0b, 0xc2, 0x52, 0x82, 0xc2, 0x39, 0x24, 0x02, 0x2c,
	0x56, 0x25, 0x7a, 0x31, 0x29, 0xd6, 0xa3, 0x20, 0xe1, 0xb1, 0x18, 0xb0, 0x0c, 0x8a, 0x32, 0xc1,
	0x11, 0x32, 0x09, 0xc5, 0xad, 0x30, 0x37, 0x08, 0xbc, 0x91, 0x82, 0xcf, 0x20, 0x25, 0x6b, 0x9c,
	0x30, 0x8f, 0x44, 0x26, 0x46, 0x6a, 0x07, 0x49, 0x8e, 0x09, 0x58, 0x10, 0x02, 0x25, 0xc5, 0xc4,
	0x42, 0x5a, 0x80, 0xa0, 0x80, 0x3c, 0x90, 0x28, 0x64, 0x14, 0xe1, 0x03, 0x84, 0x51, 0x0c, 0x2e,
	0xa3, 0x8a, 0xa4, 0x08, 0xc0, 0x47, 0x7e, 0xd3, 0x2b, 0x03, 0xcd, 0x54, 0x2a, 0x00, 0x04, 0xb3,
	0x92, 0x6c, 0x42, 0x29, 0x4c, 0x83, 0xc1, 0x92, 0xcc, 0x1c, 0x2d, 0x46, 0x21, 0xdb, 0x38, 0x59,
	0x84, 0x8c, 0x24, 0x12, 0x58, 0xbb, 0xe0, 0x06, 0x0d, 0x70, 0x30, 0xc9, 0x09, 0x28, 0x91, 0x41,
	0x44, 0x32, 0xf9, 0x8c, 0x30, 0x80, 0xc2, 0x72, 0xa4, 0x62, 0x0c, 0x7d, 0x81, 0x83, 0x14, 0xe1,
	0xaa, 0x0e, 0x15, 0x82, 0x0a, 0x78, 0x14, 0x70, 0x97, 0x08, 0x10, 0x6f, 0x2e, 0xf0, 0xb2, 0x1d,
	0x30, 0x49, 0x44, 0x32, 0x53, 0x62, 0x86, 0x65, 0x45, 0x84, 0x0a, 0x11, 0x4b, 0x36, 0xd9, 0x8c,
	0x69, 0x3a, 0x61, 0x80, 0x90, 0x7c, 0x19, 0xc0, 0x30, 0x95, 0x40, 0x8b, 0x0c, 0x05, 0x2d, 0x0e,
	0xc0, 0x71, 0xa1, 0xb4, 0x96, 0x85, 0x1a, 0x16, 0xc0, 0x15, 0x14, 0x51, 0x4c, 0x48, 0xb7, 0x79,
	0x95, 0x10, 0x89, 0x8a, 0x2e, 0x02, 0xa1, 0x1c, 0xd5, 0x90, 0x81, 0x10, 0x91, 0x08, 0x22, 0xb4,
	0x1e, 0xe9, 0x78, 0xc0, 0x33, 0x20, 0x5c, 0x8b, 0xc4, 0x0e, 0xe2, 0xaa, 0x23, 0x10, 0x47, 0xe3,
	0x28, 0x55, 0x7b, 0x19, 0xa1, 0x51, 0xa8, 0x06, 0x04, 0x90, 0x82, 0x2c, 0xd1, 0x61, 0x60, 0xa1,
	0x52, 0x06, 0x41, 0x44, 0x81, 0x54, 0x23, 0x88, 0x18, 0xa9, 0x04, 0x27, 0x22, 0x72, 0x48, 0xb6,
	0x40, 0x1c, 0x42, 0x12, 0x17, 0xca, 0x29, 0xa0, 0x5a, 0x01, 0x3c, 0xe1, 0x52, 0x84, 0x89, 0xda,
	0x01, 0x40, 0x06, 0xf1, 0x2c, 0x69, 0xd2, 0x48, 0x42, 0x63, 0xc1, 0x21, 0x34, 0x8a, 0xc8, 0x6c,
	0x21, 0x71, 0x26, 0x09, 0x88, 0x2e, 0x44, 0xd5, 0x28, 0x92, 0x1d, 0x98, 0x86, 0x12, 0xd1, 0x52,
	0xa3, 0xa0, 0x46, 0x20, 0x46, 0x14, 0x50, 0x83, 0xdc, 0xdd, 0x83, 0x50, 0x44, 0xa9, 0x8c, 0x4d,
	0x14, 0x2e, 0x54, 0x14, 0x89, 0x1b, 0x39, 0x42, 0x80, 0x14, 0x81, 0x29, 0x8f, 0x80, 0x02, 0x03,
	0x32, 0x96, 0x82, 0x83, 0xb0, 0x05, 0x0c, 0x14, 0x51, 0x88, 0x07, 0xf8, 0xd9, 0xec, 0x1a, 0x06,
	0x48, 0x31, 0x5c, 0x43, 0x86, 0x06, 0x23, 0xb3, 0x09, 0x44, 0x0c, 0xa5, 0x00, 0xf0, 0xb2, 0x80,
	0x01, 0xca, 0xe1, 0x42, 0xc2, 0x18, 0xb6, 0x2c, 0x9b, 0xc7, 0x2e, 0x52, 0x29, 0x2c, 0x61, 0x1a,
	0x8a, 0x26, 0x24, 0x8b, 0x84, 0x80, 0x5e, 0x60, 0x02, 0xc1, 0x53, 0x2a, 0x35, 0x45, 0x21, 0x52,
	0xd1, 0x42, 0xb8, 0xc5, 0x4a, 0x48, 0x04, 0x16, 0x70, 0x23, 0x61, 0xc1, 0x09, 0x50, 0xc4, 0x81,
	0x88, 0x18, 0xd9, 0x01, 0x16, 0x22, 0x28, 0x8c, 0x90, 0x84, 0xeb, 0x38, 0x24, 0x53, 0x09, 0xa9,
	0x41, 0x6a, 0x14, 0x82, 0x09, 0x86, 0x30, 0x9c, 0xa2, 0x38, 0x42, 0x00, 0x2b, 0x95, 0x5a, 0x02,
	0x4a, 0xc1, 0xa2, 0xad, 0x45, 0x04, 0x27, 0x54, 0xc0, 0x28, 0xa9, 0x05, 0x8c, 0x2e, 0x6e, 0x60,
	0xf3, 0x16, 0x90, 0x1b, 0x82, 0x1e, 0x00, 0x80, 0x26, 0x1c, 0x1d, 0x4a, 0x14, 0xc2, 0x1a, 0x22,
	0x3c, 0x85, 0xc1, 0x10, 0xb0, 0x48, 0x11, 0x00, 0x4a, 0xc5, 0x30, 0xf4, 0xaa, 0x30, 0x8c, 0xc8,
	0x49, 0x18, 0x07, 0xf0, 0x1c, 0xe9, 0x07, 0xcd, 0x0c, 0x22, 0x1b, 0x1c, 0xec, 0xc2, 0x45, 0x0a,
	0x50, 0x3a, 0x20, 0xe4, 0x0e, 0x6c, 0x20, 0x82, 0xc3, 0x91, 0xd9, 0xc8, 0x62, 0x4c, 0xd1, 0x80,
	0x85, 0x65, 0x09, 0x02, 0x30, 0x95, 0xf9, 0x10, 0x69, 0x02, 0xa6, 0x4a, 0x64, 0xc2, 0xb6, 0xf9,
	0x16, 0x20, 0x48, 0xa1, 0x73, 0x94, 0x31, 0x54, 0x61, 0x44, 0x07, 0x03, 0x82, 0x2c, 0x06, 0x00,
	0x38, 0x33, 0x09, 0x1c, 0xc1, 0x4b, 0xce, 0x12, 0x35, 0x41, 0xa4, 0x90, 0x99, 0x2d, 0x2a, 0xc0,
	0x59, 0x84, 0xd1, 0x4a, 0xa4, 0x72, 0x04, 0x22, 0x3c, 0x54, 0xc2, 0xa0, 0x0c, 0x01, 0x18, 0xac,
	0xac, 0x96, 0xe4, 0x04, 0x04, 0x03, 0x93, 0x05, 0x9b, 0x48, 0x44, 0x63, 0x32, 0x01, 0x31, 0x5f,
	0x60, 0x5c, 0x02, 0x00, 0x03, 0x80, 0xd1, 0x04, 0x2e, 0x34, 0xa9, 0x30, 0x49, 0xc4, 0xca, 0x1e,
	0x02, 0x4b, 0x32, 0x14, 0x05, 0x86, 0x22, 0xc5, 0xc2, 0x2a, 0x4c, 0xc8, 0x00, 0x08, 0xf3, 0x18,
	0x38, 0x65, 0x99, 0x82, 0x4a, 0x54, 0x63, 0xa1, 0x94, 0x8f, 0x44, 0x12, 0x93, 0xe9, 0x19, 0x28,
	0xca, 0xa1, 0x06, 0x12, 0x8a, 0x20, 0xa5, 0x51, 0x48, 0x18, 0x70, 0x31, 0xae, 0x90, 0x08, 0x43,
	0x68, 0x97, 0x32, 0xaf, 0x91, 0xc8, 0x0a, 0x2c, 0x02, 0x02, 0x8c, 0x0c, 0x51, 0xa5, 0x12, 0x00,
	0x1b, 0x1e, 0x81, 0x8a, 0x08, 0x28, 0x60, 0xd2, 0x86, 0x34, 0x44, 0x63, 0x76, 0x04, 0x43, 0x00,
	0xf1, 0x01, 0x04, 0x40, 0x36, 0xc8, 0xa1, 0x8c, 0xc3, 0xce, 0x32, 0x42, 0x09, 0x29, 0x55, 0x44,
	0x4e, 0x28, 0x43, 0x60, 0x1c, 0xa0, 0xda, 0x28, 0x0e, 0xa5, 0xf2, 0x05, 0x40, 0x59, 0x8c, 0x4e,
	0xa2, 0x60, 0x05, 0xe0, 0x05, 0xc5, 0x04, 0x62, 0x81, 0x26, 0xa8, 0x8a, 0xa4, 0x24, 0xb4, 0xd3,
	0x1a, 0x81, 0x42, 0xe4, 0x60, 0x34, 0x08, 0x2a, 0x39, 0xcc, 0x62, 0x08, 0x47, 0xd0, 0x00, 0x40,
	0x07, 0x02, 0x78, 0x20, 0xd9, 0xa2, 0x89, 0x50, 0x23, 0x32, 0x00, 0xb8, 0x80, 0x64, 0x11, 0xe9,
	0x74, 0x62, 0x0a, 0xaa, 0x44, 0x08, 0x8b, 0x16, 0x06, 0x18, 0x2d, 0xf1, 0x4c, 0x4f, 0x2a, 0x21,
	0x40, 0x15, 0xb4, 0x0c, 0x48, 0x22, 0xd1, 0xe1, 0x80, 0xd8, 0x17, 0xa7, 0x14, 0xa0, 0x82, 0x15,
	0x64, 0xc0, 0x89, 0x5a, 0x11, 0xbb, 0x8c, 0x78, 0x08, 0xcd, 0x04, 0x24, 0xa2, 0x9b, 0x9c, 0x10,
	0xcc, 0x42, 0xa3, 0x28, 0x3b, 0x58, 0x5f, 0xa6, 0x40, 0x90, 0x23, 0x04, 0x2c, 0xd2, 0xae, 0x52,
	0x51, 0x1a, 0xa4, 0x69, 0x80, 0xc1, 0x4a, 0xa3, 0xc8, 0x90, 0x19, 0x48, 0x42, 0x24, 0x22, 0x43,
	0x02, 0x45, 0xc1, 0x05, 0x00, 0x74, 0x11, 0x15, 0x94, 0x10, 0x0c, 0x38, 0x73, 0x8a, 0x25, 0x04,
	0x07, 0x28, 0x7e, 0x01, 0x40, 0x82, 0x41, 0x48, 0x6d, 0x16, 0x36, 0x29, 0x31, 0xe5, 0x81, 0xa4,
	0x1c, 0x81, 0xfa, 0x09, 0x00, 0x14, 0x2d, 0x10, 0x60, 0x40, 0x97, 0xdc, 0x88, 0x02, 0x4e, 0x17,
	0x98, 0x85, 0x44, 0x9c, 0xa2, 0x60, 0x91, 0x8a, 0x92, 0x68, 0x19, 0x0a, 0x40, 0x51, 0x80, 0x37,
	0x84, 0x15, 0x40, 0x62, 0x64, 0xb2, 0x0e, 0x91, 0x48, 0x2a, 0x00, 0x55, 0x48, 0x99, 0xb0, 0x08,
	0x85, 0x1a, 0x13, 0x10, 0x9b, 0x00, 0x5e, 0xc9, 0x50, 0x00, 0xe8, 0xa4, 0x00, 0x83, 0x45, 0x6c,
	0x40, 0x0b, 0x9d, 0xa0, 0x02, 0x2d, 0x34, 0x92, 0x01, 0x21, 0x81, 0x17, 0xa3, 0x46, 0xa0, 0x8a,
	0x85, 0xe8, 0x88, 0x47, 0x18, 0x96, 0x01, 0xb6, 0x41, 0x93, 0x84, 0x08, 0x02, 0x18, 0x02, 0x35,
	0x50, 0xe4, 0x3a, 0x81, 0x10, 0x11, 0xa1, 0xda, 0x82, 0x4c, 0x34, 0x32, 0x85, 0x00, 0x57, 0x4b,
	0x28, 0xa5, 0xa8, 0x38, 0x84, 0x1d, 0x0e, 0x30, 0xb3, 0x50, 0x22, 0x41, 0xc3, 0xc6, 0x06, 0xd0,
	0x20, 0x28, 0xa1, 0x05, 0x8a, 0x1a, 0x84, 0x22, 0x85, 0x0c, 0x46, 0x44, 0x72, 0x90, 0xc8, 0x17,
	0xd8, 0x41, 0x61, 0x64, 0x30, 0x39, 0x00, 0x0c, 0x97, 0xa3, 0x0e, 0xb2, 0x29, 0x02, 0x50, 0x88,
	0xa9, 0x5c, 0x22, 0xc1, 0x8e, 0x90, 0xc3, 0x47, 0x40, 0x31, 0x12, 0x2c, 0x38, 0x8c, 0x63, 0x64,
	0x86, 0xc0, 0xb3, 0x00, 0x4a, 0x2a, 0x38, 0x11, 0x91, 0x13, 0x38, 0x47, 0x49, 0x14, 0x81, 0x73,
	0x02, 0x2c, 0x4c, 0x8e, 0x52, 0xe5, 0xd1, 0x10, 0x10, 0x07, 0x4c, 0x06, 0x65, 0x61, 0x1a, 0x54,
	0x84, 0x01, 0x4a, 0xe2, 0x01, 0x2f, 0x10, 0x06, 0x09, 0x42, 0x44, 0x9a, 0x00, 0xa9, 0x52, 0x8c,
	0x28, 0x94, 0x61, 0x01, 0x99, 0x05, 0x20, 0x1c, 0x23, 0x78, 0xa2, 0x51, 0x18, 0x82, 0x3c, 0x41,
	0xd0, 0xb7, 0xc4, 0x43, 0x87, 0x44, 0xc6, 0x0a, 0x93, 0x68, 0xc1, 0x81, 0x30, 0x52, 0xb2, 0xa8,
	0xc1, 0x8a, 0x24, 0x1c, 0x21, 0x03, 0x33, 0x41, 0x14, 0x4a, 0x2e, 0x41, 0xe2, 0x82, 0x45, 0x01,
	0xac, 0x06, 0x40, 0x18, 0x20, 0x95, 0x4c, 0x67, 0x20, 0x26, 0x50, 0x94, 0x55, 0x92, 0x08, 0x1a,
	0x82, 0xe1, 0x8c, 0x10, 0x5b, 0x61, 0x02, 0x86, 0x72, 0x99, 0x30, 0x8d, 0x06, 0x2c, 0xb0, 0xa1,
	0x18, 0x28, 0x06, 0x27, 0x46, 0x20, 0x18, 0x82, 0x21, 0x9c, 0xeb, 0x18, 0xc4, 0x30, 0x81, 0x58,
	0xc1, 0x84, 0x06, 0x37, 0x9a, 0x24, 0x50, 0x0d, 0xa2, 0x40, 0x05, 0x29, 0x19, 0x09, 0x02, 0x84,
	0x52, 0x64, 0x11, 0x17, 0xec, 0xd1, 0x20, 0x78, 0x86, 0x63, 0x0e, 0xd1, 0x11, 0x2a, 0x60, 0x03,
	0xd1, 0x22, 0x60, 0x84, 0x06, 0x32, 0xa4, 0x68, 0x24, 0x81, 0xc7, 0xc0, 0x12, 0x42, 0x01, 0xbb,
	0xa8, 0x00, 0x41, 0x7c, 0x41, 0x21, 0x81, 0x91, 0x10, 0x85, 0x14, 0xa0, 0xa8, 0x2d, 0x5c, 0x15,
	0x47, 0x40, 0xd0, 0xfa, 0x2a, 0x84, 0x0e, 0x8f, 0x10, 0xd2, 0x80, 0x09, 0xd4, 0x49, 0x21, 0x26,
	0x32, 0xb0, 0x38, 0x20, 0x41, 0xa0, 0x4a, 0x11, 0x4b, 0xba, 0xac, 0x4e, 0x20, 0x34, 0x61, 0x60,
	0x15, 0x0d, 0x13, 0x6a, 0x70, 0x43, 0x42, 0x9c, 0x2d, 0x55, 0xe8, 0x00, 0x96, 0x98, 0x92, 0xe4,
	0xc8, 0x4c, 0x0c, 0xa5, 0x19, 0x81, 0x31, 0xca, 0x42, 0x02, 0x12, 0x4a, 0x30, 0x30, 0x51, 0x01,
	0x66, 0xa2, 0xe2, 0x16, 0x41, 0xcf, 0xa0, 0x2a, 0x84, 0x51, 0x18, 0x18, 0x41, 0xc6, 0x56, 0xa0,
	0x21, 0x8e, 0xe1, 0x96, 0x0b, 0x04, 0x60, 0x11, 0xb8, 0x4c, 0x93, 0x0d, 0x06, 0x04, 0x2a, 0x2a,
	0x4c, 0x04, 0x86, 0x2a, 0x51, 0x51, 0x31, 0x00, 0x01, 0x80, 0x76, 0x51, 0x98, 0x94, 0x2d, 0xcc,
	0x4a, 0x6a, 0xa4, 0x42, 0xb2, 0x69, 0x48, 0x01, 0x40, 0xb5, 0xd2, 0x32, 0x5c, 0x0e, 0x62, 0x20,
	0x16, 0x01, 0x37, 0xe4, 0xc1, 0xc2, 0x58, 0x24, 0x23, 0x08, 0x88, 0x02, 0x24, 0x14, 0x85, 0x98,
	0x06, 0xd0, 0x1d, 0xaa, 0x34, 0x80, 0x0a, 0x34, 0x61, 0x10, 0x4b, 0x48, 0x42, 0xc2, 0x02, 0x98,
	0x94, 0x00, 0x20, 0x57, 0xb3, 0xa1, 0x88, 0x1a, 0xce, 0x08, 0x05, 0xa0, 0xb0, 0x79, 0x4a, 0x01,
	0x14, 0x46, 0x03, 0x24, 0x24, 0x8d, 0x00, 0x44, 0x41, 0x41, 0x03, 0x6c, 0x8a, 0xc1, 0x02, 0xc1,
	0x59, 0x26, 0xd8, 0x41, 0x68, 0x56, 0x53, 0x43, 0x2a, 0x14, 0x04, 0x28, 0x0a, 0x25, 0x20, 0x8a,
	0x94, 0x47, 0x83, 0x1a, 0x85, 0xa0, 0x0f, 0x84, 0xd5, 0x08, 0x40, 0x70, 0x19, 0x06, 0x08, 0x51,
	0x80, 0x5a, 0x16, 0xa8, 0x08, 0x55, 0xd2, 0x28, 0x18, 0x41, 0x49, 0x09, 0x85, 0x02, 0x65, 0x50,
	0xb4, 0x80, 0x3d, 0x80, 0x81, 0x2a, 0x4a, 0x86, 0x80, 0x30, 0x01, 0x03, 0x86, 0x1c, 0x53, 0x93,
	0xa3, 0x61, 0x58, 0x2a, 0x54, 0x21, 0xb2, 0x97, 0xb0, 0x86, 0xab, 0x52, 0x44, 0xe9, 0x88, 0x59,
	0x00, 0x8b, 0x20, 0x10, 0xd2, 0x18, 0x18, 0x85, 0x08, 0x1c, 0x31, 0x50, 0x03, 0x64, 0x8c, 0x0a,
	0x40, 0x61, 0xc0, 0x0c, 0xa8, 0x08, 0x40, 0x32, 0x65, 0x0b, 0x9c, 0x88, 0x02, 0x42, 0x4c, 0x14,
	0x60, 0x34, 0x85, 0x13, 0x21, 0x50, 0x71, 0x48, 0xa1, 0xe5, 0x14, 0x6e, 0x48, 0x20, 0x32, 0x8a,
	0x18, 0x18, 0x45, 0x6a, 0x32, 0x20, 0x82, 0x71, 0x8b, 0xa3, 0x62, 0x16, 0x92, 0xbb, 0x01, 0x91,
	0x20, 0x76, 0x11, 0x21, 0x34, 0x54, 0x4c, 0x80, 0x16, 0xc6, 0x38, 0x10, 0xe4, 0x04, 0x84, 0x00,
	0x41, 0x01, 0x9a, 0x59, 0x48, 0x84, 0x1a, 0xa4, 0x68, 0x89, 0x51, 0x91, 0x48, 0x40, 0x46, 0xa9,
	0x09, 0x94, 0x04, 0x4d, 0x2a, 0x60, 0xb9, 0x18, 0x08, 0xc9, 0xc6, 0x40, 0xa4, 0x99, 0x34, 0x3c,
	0x11, 0x8d, 0x10, 0xe0, 0x20, 0xa4, 0x75, 0x0a, 0xe6, 0x44, 0x93, 0xb2, 0x94, 0x04, 0x81, 0xa0,
	0x48, 0x75, 0x2a, 0xa3, 0x48, 0x42, 0x05, 0x6e, 0x96, 0x0a, 0x08, 0x29, 0x91, 0x0c, 0x14, 0x05,
	0xf0, 0x22, 0x84, 0x01, 0x21, 0x04, 0xa1, 0x00, 0x3a, 0xd8, 0x15, 0x65, 0x10, 0x11, 0xc1, 0xa0,
	0x98, 0x8c, 0x2a, 0x16, 0xc1, 0x01, 0x88, 0xf5, 0x12, 0x42, 0x40, 0xa5, 0x01, 0x0b, 0xa0, 0xc2,
	0x09, 0x5c, 0xc4, 0x72, 0x00, 0x18, 0x06, 0xe4, 0x0c, 0x26, 0x5a, 0x3d, 0x80, 0x10, 0x40, 0x40,
	0x40, 0x11, 0x23, 0x30, 0x9c, 0x80, 0x74, 0x32, 0x82, 0x84, 0x85, 0x00, 0xaf, 0x20, 0x95, 0x20,
	0x8f, 0x51, 0x8e, 0xc3, 0x26, 0x84, 0x62, 0x03, 0xc1, 0x09, 0xa8, 0x34, 0x34, 0x83, 0x22, 0xcc,
	0x90, 0x23, 0x1a, 0x82, 0x22, 0x88, 0x48, 0x40, 0x6c, 0x32, 0xc5, 0x91, 0xa3, 0x35, 0x89, 0xa0,
	0x24, 0xa0, 0x58, 0x07, 0x21, 0x98, 0x0e, 0x0a, 0xf2, 0x6b, 0xb0, 0xac, 0x4a, 0x6c, 0x08, 0x92,
	0x29, 0x18, 0x40, 0x42, 0xc1, 0x2e, 0x04, 0x91, 0x30, 0xd1, 0x94, 0xa3, 0x42, 0x43, 0xd9, 0x20,
	0x59, 0x98, 0x2d, 0x20, 0x74, 0x00, 0x3d, 0x8c, 0x13, 0x0a, 0x46, 0x62, 0x00, 0x05, 0x34, 0x59,
	0x40, 0x26, 0x02, 0x58, 0x38, 0xad, 0x94, 0x2a, 0x18, 0x10, 0x0a, 0xa0, 0x69, 0x47, 0xe3, 0x18,
	0xe2, 0x70, 0x8c, 0x04, 0x54, 0x01, 0x24, 0x00, 0x8a, 0x10, 0x29, 0x06, 0xad, 0x02, 0x46, 0x28,
	0x0b, 0xd0, 0x50, 0xc5, 0x72, 0x50, 0xc1, 0xa9, 0x14, 0x14, 0x09, 0x60, 0xb0, 0x52, 0x12, 0x60,
	0x45, 0x20, 0x16, 0x06, 0xc3, 0x01, 0xa9, 0x93, 0xae, 0x04, 0x82, 0x1a, 0x0b, 0x58, 0x0e, 0x2c,
	0x64, 0x84, 0x30, 0x07, 0x55, 0x92, 0x09, 0x08, 0x90, 0xba, 0x91, 0x25, 0x02, 0x47, 0x66, 0x56,
	0x68, 0x21, 0x00, 0x9c, 0x06, 0x60, 0x20, 0x88, 0x34, 0x89, 0x9b, 0x88, 0x22, 0xc2, 0x52, 0x92,
	0x1d, 0x14, 0x80, 0x40, 0xd5, 0x19, 0x0b, 0xb4, 0xc4, 0xe0, 0x38, 0x45, 0x50, 0x1b, 0x44, 0x88,
	0x08, 0x08, 0x07, 0xe2, 0x0f, 0x24, 0x80, 0x65, 0x28, 0x72, 0x00, 0x0a, 0xe9, 0xc1, 0x08, 0x48,
	0xb0, 0x42, 0x0c, 0x41, 0x55, 0x26, 0x0e, 0x21, 0x1a, 0x84, 0xd1, 0x10, 0x42, 0x18, 0x07, 0xa1,
	0x13, 0x4c, 0xd0, 0xc0, 0x0c, 0x44, 0x2a, 0x83, 0x44, 0x01, 0x46, 0x06, 0x45, 0xf0, 0xbc, 0x04,
	0x8a, 0xa6, 0x0a, 0x30, 0x11, 0xb4, 0x61, 0xc3, 0x6b, 0x06, 0x07, 0x23, 0xb4, 0x6c, 0x19, 0x49,
	0x46, 0xa0, 0x18, 0x28, 0x60, 0x84, 0x8a, 0x12, 0xa5, 0x20, 0x04, 0x31, 0xda, 0x4c, 0x60, 0x21,
	0x0a, 0x17, 0x14, 0x02, 0x04, 0x32, 0x90, 0x92, 0x83, 0x89, 0x4b, 0x42, 0x68, 0x00, 0x53, 0x11,
	0x80, 0xd7, 0x88, 0x18, 0x72, 0xa1, 0x04, 0xb0, 0x00, 0x64, 0x08, 0x42, 0xc1, 0x2e, 0x54, 0x81,
	0x85, 0x68, 0xa4, 0x98, 0x92, 0x38, 0x59, 0x04, 0x44, 0x00, 0xa3, 0xa1, 0x64, 0x0f, 0x22, 0x3e,
	0x00, 0x90, 0x02, 0xdd, 0x1c, 0x8b, 0x1c, 0x60, 0xd0, 0x32, 0x84, 0x04, 0x03, 0x06, 0x74, 0x1a,
	0x31, 0x2d, 0xc9, 0x82, 0x22, 0xc3, 0x10, 0x82, 0x30, 0x8c, 0x41, 0x12, 0x12, 0x60, 0x35, 0x94,
	0x1f, 0x09, 0x32, 0xf1, 0xa8, 0x8c, 0x08, 0x90, 0x6b, 0x48, 0x20, 0x79, 0x3d, 0x54, 0x86, 0x04,
	0x4a, 0x71, 0xc8, 0x00, 0xcc, 0x02, 0xe9, 0x0c, 0x24, 0x21, 0x0a, 0x80, 0x52, 0xee, 0x00, 0xa4,
	0x32, 0x96, 0x1c, 0x92, 0x64, 0x20, 0x82, 0x8a, 0x88, 0xa1, 0x4b, 0x0e, 0x78, 0x35, 0x51, 0x00,
	0xa0, 0x49, 0x69, 0x72, 0x07, 0x23, 0x14, 0xa0, 0x45, 0x0a, 0x04, 0xd0, 0xd9, 0x82, 0xa1, 0x07,
	0xe5, 0x08, 0x03, 0x20, 0x3c, 0x70, 0xd4, 0x0c, 0x12, 0xc0, 0x49, 0x20, 0x08, 0xd1, 0x08, 0x62,
	0x63, 0x11, 0x02, 0x10, 0x98, 0x4f, 0x72, 0x20, 0x81, 0x11, 0xa8, 0x53, 0xab, 0x14, 0x02, 0x10,
	0x8e, 0x94, 0x86, 0x49, 0x20, 0x31, 0x02, 0x19, 0x41, 0x48, 0x62, 0x44, 0xc5, 0x80, 0x18, 0x14,
	0xd0, 0x83, 0x00, 0x57, 0x88, 0x25, 0xad, 0x42, 0x80, 0x3a, 0x30, 0x90, 0x15, 0xd5, 0x1a, 0xe1,
	0x4c, 0x24, 0x20, 0x80, 0xe1, 0x08, 0xc7, 0x14, 0x05, 0x8a, 0x11, 0xb0, 0x01, 0x28, 0x10, 0x33,
	0x21, 0x93, 0x04, 0xcf, 0x44, 0x22, 0xc0, 0xc9, 0x9a, 0x00, 0x4a, 0x0c, 0x2c, 0x01, 0xb1, 0x8e,
	0x10, 0x58, 0x21, 0x14, 0x96, 0x63, 0x34, 0x44, 0x43, 0x04, 0x44, 0xb0, 0x5b, 0x80, 0x28, 0x0c,
	0x00, 0x24, 0x33, 0x80, 0x0a, 0x1c, 0x0b, 0x65, 0x26, 0xe0, 0x2a, 0x1e, 0x09, 0x9d, 0xa5, 0x48,
	0x44, 0x13, 0x25, 0x2c, 0xc4, 0x22, 0x30, 0x00, 0x62, 0xa4, 0x48, 0x46, 0x40, 0x50, 0x27, 0x00,
	0xb8, 0xa4, 0x44, 0x0c, 0x26, 0x44, 0x98, 0x89, 0x81, 0x03, 0x0d, 0x1c, 0xc6, 0x58, 0x82, 0x40,
	0x18, 0x4b, 0x40, 0x85, 0x8b, 0x3b, 0x38, 0xc8, 0x2d, 0x64, 0x87, 0x61, 0x11, 0x58, 0x41, 0x88,
	0x16, 0x14, 0x89, 0x19, 0x9d, 0x10, 0xa0, 0x08, 0x22, 0x80, 0x02, 0x44, 0x86, 0x81, 0x04, 0x33,
	0x80, 0x10, 0x19, 0xc0, 0xc1, 0x48, 0x10, 0x80, 0x31, 0x40, 0x1f, 0x01, 0x24, 0x03, 0xe1, 0x3f,
	0x48, 0x4b, 0x8a, 0x10, 0x21, 0xd8, 0x06, 0x95, 0x19, 0x6b, 0x42, 0x80, 0x68, 0x16, 0x64, 0x48,
	0x22, 0x40, 0xd5, 0x18, 0xa9, 0x10, 0xc5, 0x0b, 0x1c, 0x74, 0x51, 0xb8, 0x25, 0x00, 0xc0, 0x62,
	0xa2, 0xa9, 0x10, 0xb4, 0x8a, 0x60, 0x2a, 0x77, 0xc1, 0x8e, 0x10, 0x83, 0x48, 0x76, 0x92, 0x42,
	0x25, 0x08, 0xc0, 0x2c, 0x1e, 0x30, 0x60, 0x21, 0x79, 0x02, 0x88, 0x30, 0x95, 0x30, 0x2a, 0x84,
	0x00, 0x4a, 0x4c, 0x43, 0x10, 0x17, 0x11, 0x94, 0xa2, 0x08, 0x80, 0x31, 0xb4, 0x6d, 0xc8, 0x80,
	0x6a, 0x40, 0x2b, 0x04, 0x70, 0x1b, 0x49, 0x78, 0xc0, 0x88, 0x04, 0x5c, 0x01, 0x25, 0x40, 0x44,
	0x41, 0x18, 0x05, 0x4f, 0x06, 0x0a, 0x20, 0x61, 0x37, 0xd0, 0x40, 0x05, 0x0a, 0x26, 0x48, 0xb8,
	0x88, 0x5b, 0x21, 0x10, 0x43, 0xda, 0x9e, 0x29, 0x81, 0xa3, 0x18, 0x00, 0x33, 0x09, 0x98, 0x93,
	0xcd, 0x52, 0x42, 0xa3, 0x25, 0x85, 0x1e, 0x28, 0x14, 0x35, 0x50, 0x01, 0xd0, 0xd0, 0x4a, 0x2c,
	0xa1, 0x22, 0xa0, 0x5d, 0x1c, 0xe2, 0x02, 0x54, 0x90, 0x02, 0x88, 0x5f, 0x28, 0x78, 0x04, 0x83,
	0x02, 0x40, 0x8c, 0x20, 0x0c, 0x82, 0x11, 0x04, 0x71, 0x4d, 0x26, 0x66, 0x32, 0x88, 0x8a, 0xa0,
	0x07, 0x8c, 0x00, 0xa3, 0x40, 0x09, 0x31, 0xc7, 0x81, 0x30, 0xd4, 0x43, 0x0a, 0x88, 0x9d, 0x49,
	0x4a, 0x81, 0x01, 0x8a, 0x18, 0x02, 0x67, 0x64, 0x36, 0x88, 0x18, 0x88, 0x05, 0xe8, 0x14, 0x40,
	0x79, 0x07, 0x49, 0x90, 0xc9, 0x2c, 0xd6, 0x02, 0x93, 0x80, 0x48, 0xa9, 0x0c, 0x67, 0x10, 0xa6,
	0x24, 0x51, 0x66, 0x04, 0x00, 0x5b, 0x13, 0xc8, 0xcd, 0x00, 0x62, 0x05, 0x2b, 0x32, 0x31, 0x19,
	0xc8, 0x20, 0x02, 0x90, 0x92, 0x21, 0x4d, 0xe0, 0x64, 0xc0, 0x43, 0x10, 0x84, 0x44, 0xa7, 0x1a,
	0x12, 0x18, 0x34, 0x01, 0x08, 0x07, 0x0a, 0xb4, 0xaa, 0x32, 0x1c, 0x16, 0x40, 0x06, 0xc1, 0xa8,
	0x1e, 0x21, 0x40, 0x41, 0x12, 0xb4, 0x13, 0x08, 0x85, 0x40, 0x0c, 0x02, 0xf0, 0x13, 0x28, 0x69,
	0x80, 0x45, 0x70, 0x75, 0x70, 0x80, 0x41, 0x02, 0x21, 0x00, 0x33, 0x88, 0xbc, 0x65, 0x80, 0x46,
	0x54, 0x82, 0x03, 0x13, 0x04, 0xc1, 0x08, 0x20, 0x50, 0x03, 0x23, 0x30, 0x1e, 0x82, 0x32, 0xa6,
	0xc8, 0x2a, 0x91, 0x48, 0x4c, 0x10, 0x94, 0xc1, 0x20, 0x9c, 0x03, 0x28, 0x34, 0x90, 0x70, 0xba,
	0xb1, 0x4a, 0xc4, 0x58, 0x44, 0xe0, 0xa8, 0xbc, 0x40, 0x05, 0x48, 0x35, 0x22, 0x10, 0x30, 0x16,
	0x27, 0x24, 0x20, 0x12, 0x38, 0x64, 0x07, 0x46, 0x08, 0xa3, 0xf0, 0x09, 0x14, 0x8a, 0xea, 0x20,
	0x07, 0xc3, 0x00, 0x85, 0x89, 0xc0, 0x22, 0xc0, 0x20, 0x8c, 0x68, 0x0a, 0x02, 0x26, 0x43, 0x48,
	0x8e, 0x4c, 0x03, 0xa2, 0x48, 0x75, 0xb2, 0x00, 0x61, 0x81, 0x60, 0x68, 0x40, 0x83, 0x90, 0xc5,
	0x50, 0x00, 0x50, 0x56, 0x02, 0xa3, 0x14, 0x44, 0x24, 0x3e, 0xa4, 0x49, 0x3a, 0x01, 0xd0, 0x0b,
	0x48, 0x63, 0x8a, 0xa4, 0x20, 0xd2, 0x4c, 0x1e, 0x44, 0x61, 0x11, 0x49, 0x44, 0x61, 0x36, 0x84,
	0x12, 0x38, 0xe8, 0x04, 0xa6, 0x50, 0x92, 0x69, 0x89, 0x35, 0x1c, 0xa1, 0x04, 0x75, 0x62, 0x2d,
	0xc0, 0x91, 0x82, 0x02, 0x32, 0x18, 0x05, 0x71, 0xc8, 0x66, 0x50, 0x06, 0x58, 0x3a, 0x04, 0x00,
	0x2c, 0x18, 0x21, 0x21, 0x07, 0x8c, 0x41, 0x42, 0x14, 0xc0, 0xb0, 0x2e, 0x08, 0x01, 0x0b, 0x10,
	0x03, 0x80, 0x91, 0x88, 0x02, 0xae, 0x0c, 0x14, 0x09, 0x84, 0x60, 0x5b, 0xc1, 0x40, 0x23, 0x63,
	0x08, 0x80, 0x10, 0x0d, 0x0e, 0x82, 0xc8, 0x00, 0xb9, 0xdd, 0x44, 0x2a, 0x30, 0x6a, 0x30, 0x69,
	0x10, 0x6c, 0x00, 0x93, 0x19, 0x82, 0xcc, 0x10, 0x80, 0x2c, 0x41, 0xd2, 0x85, 0x18, 0x88, 0x24,
	0x4e, 0xd7, 0x88, 0xa9, 0x0c, 0x86, 0xa1, 0x04, 0x43, 0x2a, 0x91, 0x60, 0x03, 0x04, 0x12, 0x50,
	0x50, 0x20, 0xb1, 0xcc, 0x02, 0x60, 0x87, 0xab, 0x16, 0x90, 0x10, 0xe4, 0x72, 0x51, 0x00, 0x2b,
	0x60, 0x4a, 0x05, 0x0a, 0x35, 0x00, 0x22, 0x40, 0x89, 0x8a, 0x48, 0x56, 0xc2, 0x0c, 0x00, 0xc2,
	0x2c, 0x40, 0x34, 0x3a, 0x08, 0x39, 0x9b, 0x85, 0x5a, 0x14, 0x53, 0x0d, 0x10, 0x01, 0x0f, 0x00,
	0x81, 0x93, 0x2e, 0x69, 0x94, 0x2e, 0x1c, 0x41, 0xc0, 0x17, 0x18, 0x91, 0x8e, 0x08, 0x44, 0x98,
	0xa3, 0x61, 0x02, 0x44, 0x26, 0x42, 0xe1, 0x80, 0x88, 0x16, 0x02, 0x00, 0x64, 0x0a, 0x30, 0x00,
	0x08, 0xe1, 0x04, 0x03, 0xd1, 0x38, 0x14, 0x19, 0x01, 0x72, 0x52, 0x51, 0x18, 0x05, 0x85, 0x4e,
	0x06, 0xa1, 0x30, 0x26, 0x84, 0x4e, 0x4b, 0x42, 0x71, 0x01, 0x98, 0x30, 0x44, 0x21, 0x0e, 0x70,
	0x62, 0x19, 0x11, 0x00, 0x2b, 0x10, 0x30, 0x22, 0x25, 0x6c, 0x14, 0xc0, 0x5e, 0xd2, 0xb8, 0x07,
	0x45, 0x90, 0x48, 0x50, 0xc5, 0xa3, 0x81, 0x85, 0x40, 0x22, 0x4a, 0x05, 0x9a, 0x16, 0x48, 0x8d,
	0x05, 0x32, 0x16, 0xe2, 0xa2, 0x21, 0x4c, 0x00, 0x06, 0x05, 0x60, 0xa2, 0xbd, 0x48, 0xcb, 0x50,
	0x04, 0x02, 0x8a, 0xe4, 0x08, 0xa0, 0x00, 0x41, 0x00, 0x19, 0x65, 0x05, 0x8d, 0x20, 0x00, 0x20,
	0x87, 0x30, 0x0d, 0x4a, 0x7a, 0xd3, 0x28, 0x07, 0x04, 0x84, 0x61, 0x14, 0xd0, 0x81, 0x88, 0xa1,
	0xde, 0x86, 0x22, 0x80, 0x69, 0x00, 0xa9, 0xc3, 0x00, 0x06, 0x33, 0xc8, 0x01, 0x51, 0x16, 0x08,
	0x20, 0x66, 0x10, 0x9e, 0xc4, 0x00, 0xc6, 0x44, 0xc6, 0x02, 0x02, 0x0d, 0xd1, 0x25, 0x74, 0xd2,
	0x41, 0x28, 0xe0, 0x18, 0x82, 0x04, 0x34, 0x52, 0xa5, 0x48, 0x84, 0x4a, 0x76, 0xa1, 0x0b, 0xb6,
	0x0c, 0x50, 0x20, 0x20, 0x04, 0x03, 0x88, 0xdc, 0x0c, 0x41, 0x0a, 0x06, 0x00, 0x31, 0x91, 0x02,
	0x89, 0x10, 0xe4, 0x23, 0x91, 0x10, 0x9f, 0x45, 0x6c, 0x54, 0xf9, 0x9f, 0x60, 0x0a, 0x80, 0x02,
	0x30, 0x71, 0x0d, 0x10, 0x42, 0x82, 0x46, 0x30, 0x12, 0x04, 0x0c, 0x12, 0x08, 0x70, 0xa1, 0x71,
	0xa2, 0x50, 0x00, 0xe4, 0x44, 0xd2, 0x28, 0x09, 0x0d, 0xda, 0x40, 0x40, 0x16, 0x78, 0x26, 0xa0,
	0x86, 0xa4, 0x08, 0x14, 0x90, 0x00, 0xd1, 0x0a, 0x00, 0x0a, 0xc3, 0x19, 0xac, 0xa5, 0x41, 0xc0,
	0x66, 0xb1, 0x50, 0x22, 0x28, 0x09, 0x04, 0x0a, 0xc6, 0x01, 0x96, 0x20, 0x5a, 0x4c, 0x08, 0x45,
	0xa2, 0x20, 0xac, 0x57, 0x0c, 0x20, 0x24, 0xa0, 0x81, 0x48, 0x50, 0x85, 0x4e, 0x14, 0x8a, 0x08,
	0x50, 0xd5, 0x4f, 0x46, 0xe3, 0x79, 0x0d, 0x0c, 0x88, 0x83, 0x10, 0x03, 0xa0, 0x0b, 0x85, 0x92,
	0x05, 0x60, 0xe2, 0x08, 0x20, 0x25, 0x15, 0x44, 0x1a, 0x45, 0x11, 0xbc, 0x80, 0x08, 0x80, 0x50,
	0x34, 0x22, 0x15, 0x80, 0x8b, 0x8a, 0x10, 0x41, 0x79, 0xa8, 0x64, 0x50, 0x4e, 0x04, 0xa4, 0x0a,
	0x18, 0xcc, 0x88, 0xa5, 0x04, 0xa2, 0x41, 0x8d, 0x40, 0x56, 0x00, 0x70, 0xa3, 0x41, 0x03, 0x30,
	0x88, 0x2c, 0x2c, 0x07, 0xcb, 0x92, 0xc1, 0x05, 0xae, 0x14, 0x82, 0xb8, 0x01, 0x58, 0x07, 0x09,
	0x10, 0xc2, 0x21, 0x2c, 0xf0, 0x18, 0x02, 0x40, 0x64, 0xa2, 0x29, 0x58, 0x41, 0x80, 0x28, 0xc4,
	0x88, 0x09, 0x64, 0x87, 0x82, 0x16, 0x90, 0x21, 0x09, 0x09, 0x5f, 0x06, 0x50, 0x00, 0xd0, 0x92,
	0xb8, 0x83, 0x81, 0x68, 0x01, 0x5a, 0x10, 0x3c, 0x05, 0x60, 0x20, 0xb0, 0x10, 0x80, 0x48, 0x88,
	0x2b, 0x04, 0x53, 0x38, 0xb3, 0x41, 0x92, 0x4c, 0x62, 0xa6, 0x21, 0x05, 0x2c, 0x48, 0x4d, 0x14,
	0x34, 0x32, 0x0b, 0x01, 0x1a, 0xa9, 0x48, 0x02, 0xca, 0x20, 0x21, 0x44, 0x47, 0x06, 0x40, 0x40,
	0x81, 0xd4, 0x12, 0x62, 0x14, 0x95, 0x61, 0xb1, 0x98, 0x00, 0x6b, 0x46, 0xa3, 0xb2, 0x1e, 0x10,
	0xd6, 0x21, 0x1c, 0x01, 0x88, 0x94, 0x25, 0x80, 0x80, 0x46, 0x61, 0xcb, 0xb3, 0x40, 0x84, 0x8b,
	0x50, 0x43, 0xd1, 0x34, 0x31, 0x1d, 0x04, 0x12, 0x15, 0x40, 0x30, 0x00, 0x48, 0xc2, 0x20, 0x95,
	0x19, 0x18, 0xf5, 0x8d, 0x84, 0x10, 0x31, 0x0a, 0xbc, 0x04, 0xd9, 0x20, 0x1c, 0x42, 0xf0, 0x01,
	0x68, 0x94, 0x2e, 0x0a, 0x86, 0xb0, 0x01, 0x4c, 0x40, 0x21, 0x4c, 0x91, 0x80, 0xaa, 0x34, 0x06,
	0xa4, 0x58, 0x01, 0x13, 0xa1, 0x50, 0x41, 0x43, 0x20, 0xc4, 0xa2, 0xa0, 0x20, 0x46, 0x04, 0x38,
	0x96, 0xaa, 0x82, 0x6c, 0x12, 0x0e, 0x4c, 0x21, 0x43, 0x13, 0x94, 0x1e, 0x02, 0x46, 0x66, 0x48,
	0x10, 0x44, 0x45, 0xe8, 0x06, 0x01, 0x10, 0x12, 0xcc, 0x0a, 0xcd, 0x08, 0x31, 0x10, 0x2f, 0xf0,
	0x19, 0x00, 0x70, 0x22, 0x63, 0x88, 0x0c, 0x13, 0x04, 0x48, 0xe0, 0x68, 0x86, 0xa8, 0xcf, 0x60,
	0x50, 0x00, 0xb2, 0x00, 0x81, 0x47, 0x22, 0x48, 0x40, 0x3b, 0x25, 0x40, 0x96, 0x45, 0x24, 0x62,
	0xa1, 0x08, 0x99, 0xc2, 0x44, 0x04, 0x32, 0x22, 0x01, 0x14, 0x96, 0xa5, 0x58, 0x16, 0x4b, 0x90,
	0x49, 0x01, 0x08, 0x32, 0x74, 0x61, 0x00, 0x6d, 0x17, 0x22, 0x12, 0x46, 0x41, 0x20, 0x79, 0x11,
	0x01, 0x56, 0xa6, 0x00, 0x02, 0x49, 0x46, 0x2f, 0x02, 0x07, 0x08, 0x0d, 0x45, 0x40, 0xc9, 0x68,
	0x07, 0x01, 0x98, 0x34, 0x00, 0x48, 0x28, 0x83, 0x00, 0x1c, 0x81, 0x41, 0xc8, 0x50, 0x20, 0x32,
	0x34, 0x95, 0x07, 0x45, 0x48, 0x60, 0x08, 0x0a, 0x54, 0x82, 0x24, 0x60, 0x37, 0xd0, 0xaa, 0xc5,
	0xd3, 0x42, 0x04, 0x32, 0xaa, 0x1a, 0x40, 0x05, 0xc1, 0x10, 0x44, 0x0a, 0x80, 0x2c, 0xd0, 0x88,
	0x4c, 0x31, 0x42, 0x06, 0x95, 0x07, 0xe2, 0x0c, 0x23, 0xdb, 0xa0, 0x81, 0x09, 0x64, 0x24, 0xf0,
	0x4a, 0x10, 0x30, 0x03, 0xc0, 0x1c, 0xc6, 0x40, 0x88, 0xa5, 0x5d, 0x45, 0x22, 0xc1, 0xc2, 0x03,
	0x20, 0x9a, 0x2c, 0x40, 0x76, 0x08, 0x14, 0x21, 0x8c, 0xe0, 0x26, 0xb4, 0xf2, 0x08, 0x74, 0x82,
	0x0d, 0x04, 0x90, 0x98, 0x2a, 0x48, 0x00, 0x09, 0x50, 0x31, 0xc2, 0x06, 0x08, 0x43, 0xe3, 0x22,
	0x14, 0x02, 0x89, 0x45, 0xc4, 0x23, 0x18, 0xd0, 0xb8, 0xa0, 0x89, 0x92, 0x2a, 0x58, 0x51, 0xa0,
	0x80, 0xed, 0x03, 0x2b, 0x20, 0x80, 0x18, 0x96, 0x8c, 0x08, 0x00, 0x02, 0x62, 0x83, 0x09, 0x61,
	0x01, 0x8d, 0x28, 0x04, 0x48, 0x85, 0x11, 0x58, 0x07, 0x50, 0x05, 0x08, 0x37, 0xb0, 0x96, 0x40,
	0x04, 0x05, 0x93, 0x01, 0x11, 0x0f, 0xa9, 0x74, 0x02, 0xb0, 0x04, 0xac, 0xd0, 0xa3, 0x00, 0x21,
	0xb0, 0x04, 0x68, 0x10, 0x63, 0x20, 0x60, 0x53, 0x98, 0x88, 0x11, 0x47, 0x28, 0x31, 0x20, 0x24,
	0x79, 0x03, 0x20, 0x66, 0x13, 0xa3, 0x36, 0xa1, 0x0f, 0x00, 0x42, 0x74, 0x50, 0x84, 0x70, 0x87,
	0x40, 0x28, 0x61, 0xd2, 0x06, 0x98, 0x18, 0x09, 0x44, 0x75, 0x88, 0x93, 0x81, 0x46, 0x82, 0x06,
	0xc4, 0x20, 0x8c, 0x44, 0x02, 0x8c, 0x62, 0x94, 0x4b, 0x80, 0x04, 0x0d, 0x49, 0x18, 0x80, 0x80,
	0x98, 0x29, 0xc0, 0x2a, 0x76, 0x80, 0x40, 0x15, 0xd1, 0x42, 0x2a, 0x0e, 0xd0, 0xe9, 0x81, 0x11,
	0x0a, 0x84, 0x58, 0x00, 0x00, 0x9f, 0x80, 0x80, 0x8e, 0x02, 0x45, 0x90, 0x10, 0x30, 0x53, 0x82,
	0x0e, 0x06, 0xa0, 0xb2, 0xa8, 0x90, 0x84, 0x04, 0x41, 0x99, 0x05, 0x11, 0x0f, 0xa1, 0x70, 0x82,
	0x29, 0x8e, 0xbc, 0x10, 0x41, 0x20, 0xe5, 0x5b, 0x32, 0x05, 0x86, 0x47, 0x02, 0x23, 0x20, 0x25,
	0xd0, 0x8c, 0x02, 0x30, 0x40, 0x4a, 0x32, 0x28, 0xc1, 0x40, 0x62, 0x52, 0x12, 0x19, 0xa8, 0x5a,
	0xc5, 0x78, 0x26, 0x4a, 0x0d, 0x65, 0x97, 0x08, 0x1c, 0x81, 0x03, 0x8c, 0xd1, 0x8c, 0x02, 0x0c,
	0xa4, 0xe1, 0x02, 0xc0, 0xc9, 0x2e, 0x20, 0x00, 0x32, 0x30, 0x59, 0x50, 0xa7, 0x20, 0xc1, 0x40,
	0x84, 0xe9, 0xd8, 0x08, 0x5a, 0x50, 0x91, 0x10, 0x50, 0x1a, 0xc8, 0x28, 0x80, 0x4a, 0x0a, 0x14,
	0x09, 0x86, 0x70, 0x80, 0x51, 0x13, 0xc8, 0x02, 0xc4, 0x34, 0x54, 0x60, 0x8a, 0x95, 0x80, 0x07,
	0x28, 0x64, 0x22, 0x8c, 0x1c, 0x8a, 0x25, 0x18, 0x03, 0xe2, 0x97, 0x90, 0x16, 0x61, 0x00, 0x26,
	0x08, 0x38, 0x24, 0xc1, 0xe8, 0x50, 0x82, 0x12, 0x00, 0x09, 0x90, 0x84, 0x4c, 0x80, 0x92, 0x01,
	0x04, 0x0a, 0x6f, 0x68, 0x34, 0x9a, 0xa8, 0x30, 0x58, 0x05, 0x12, 0x84, 0xa3, 0x02, 0x0c, 0xd1,
	0xa4, 0x2a, 0x10, 0x09, 0x03, 0x45, 0x15, 0x61, 0x30, 0xa7, 0x00, 0x98, 0x01, 0x44, 0x88, 0x16,
	0x13, 0x93, 0x28, 0x11, 0xc6, 0x44, 0x04, 0x77, 0x68, 0x18, 0x00, 0x8b, 0x03, 0x4a, 0xb3, 0x60,
	0x2e, 0x90, 0x85, 0x29, 0x42, 0x36, 0x38, 0x9c, 0xa0, 0x00, 0x2a, 0x40, 0x04, 0x2b, 0x14, 0x05,
	0x86, 0xa1, 0x4c, 0xd1, 0x0a, 0x27, 0xc0, 0x04, 0x4a, 0x60, 0x04, 0xb1, 0xa1, 0x04, 0x10, 0xe9,
	0x62, 0xb6, 0xb2, 0x04, 0x74, 0x48, 0x61, 0x4c, 0x01, 0x12, 0x87, 0x05, 0x91, 0xa4, 0x16, 0x30,
	0x09, 0xb4, 0x68, 0x54, 0x03, 0x04, 0x82, 0x02, 0x22, 0x45, 0x54, 0x88, 0x00, 0x81, 0xda, 0x8a,
	0xb5, 0x00, 0x42, 0x30, 0x41, 0x30, 0xb1, 0x04, 0x06, 0x0e, 0x1a, 0xa0, 0xe8, 0x09, 0x00, 0x48,
	0x40, 0x0e, 0x86, 0x10, 0x02, 0xe0, 0x0e, 0x07, 0x60, 0x10, 0x10, 0x29, 0x08, 0xd3, 0xec, 0x12,
	0x71, 0x88, 0x22, 0x8c, 0x92, 0xa4, 0x08, 0x20, 0x53, 0x0f, 0xc0, 0x81, 0x00, 0x0a, 0x87, 0x08,
	0x03, 0x0d, 0x08, 0x20, 0x7c, 0x40, 0x08, 0x2b, 0xa1, 0x46, 0x24, 0x60, 0x70, 0x22, 0x85, 0x11,
	0x81, 0xc1, 0x6a, 0x84, 0x80, 0x06, 0xa4, 0x14, 0xcb, 0x04, 0xa2, 0x02, 0xb8, 0x10, 0x05, 0x4b,
	0x30, 0x55, 0x28, 0x84, 0xc4, 0x00, 0x42, 0x2a, 0xc0, 0xc2, 0x18, 0x80, 0x04, 0x25, 0x4a, 0x65,
	0x13, 0x1d, 0x39, 0x90, 0x0c, 0x58, 0x84, 0xc3, 0x0c, 0x38, 0x83, 0xe2, 0x00, 0x21, 0x0a, 0x8c,
	0x38, 0x86, 0x4d, 0x74, 0xc2, 0x10, 0x04, 0x09, 0x4b, 0xa1, 0x40, 0xb4, 0x0a, 0xa2, 0x2c, 0x52,
	0x22, 0x10, 0xc0, 0x63, 0x08, 0xc1, 0x82, 0x82, 0x0e, 0x21, 0x28, 0x12, 0x94, 0xd8, 0x02, 0x0c,
	0x46, 0x09, 0x3a, 0x84, 0x11, 0xc8, 0x12, 0x30, 0x89, 0x90, 0x35, 0x48, 0xe8, 0x24, 0x55, 0x10,
	0xa7, 0x80, 0x03, 0x43, 0x02, 0x17, 0x09, 0x9a, 0x10, 0x8d, 0x28, 0x62, 0xe4, 0x2a, 0x11, 0x68,
	0x04, 0x20, 0x22, 0x10, 0x42, 0x01, 0xcc, 0x04, 0x88, 0x1e, 0xc1, 0x83, 0x28, 0x20, 0x0c, 0x24,
	0x14, 0x63, 0xa1, 0x90, 0x09, 0x19, 0x23, 0x28, 0x20, 0x2a, 0x38, 0x0c, 0x1d, 0x42, 0x4c, 0x90,
	0xc0, 0x0b, 0x41, 0x8f, 0x06, 0x14, 0x55, 0xb8, 0x92, 0x2d, 0xc8, 0x49, 0x42, 0xa0, 0x23, 0x92,
	0x48, 0x05, 0xc7, 0x74, 0x84, 0x80, 0x8b, 0x4d, 0x8d, 0x40, 0x38, 0x00, 0x20, 0x9c, 0xa1, 0x06,
	0x09, 0x32, 0x54, 0xa8, 0x85, 0x88, 0x41, 0x05, 0x44, 0xd3, 0x13, 0x04, 0x21, 0xd8, 0x8f, 0x02,
	0x90, 0xc1, 0x08, 0x78, 0x54, 0x26, 0x40, 0x50, 0x4a, 0x24, 0x39, 0x16, 0xa0, 0x00, 0x12, 0x41,
	0xb0, 0x14, 0x50, 0x82, 0x6c, 0xa3, 0x80, 0xa3, 0x71, 0x82, 0x03, 0x04, 0x04, 0xd9, 0x21, 0x09,
	0x5e, 0xa8, 0x08, 0x70, 0x82, 0x15, 0x68, 0x04, 0x8b, 0x52, 0x63, 0x29, 0x3a, 0x54, 0x51, 0x4b,
	0x62, 0x80, 0x99, 0x18, 0x34, 0x44, 0x2e, 0x30, 0x30, 0x40, 0xb2, 0x41, 0x4e, 0x44, 0x10, 0xc0,
	0xca, 0xa4, 0x10, 0x1a, 0x01, 0x40, 0x66, 0xf2, 0x8c, 0x01, 0x84, 0x2c, 0x18, 0x30, 0xaa, 0x11,
	0x05, 0xd0, 0x40, 0x52, 0xa1, 0x79, 0x83, 0xb0, 0x80, 0x2b, 0x64, 0x01, 0x80, 0x9e, 0x50, 0x18,
	0x01, 0x04, 0x00, 0xa2, 0x17, 0x28, 0xca, 0x24, 0x30, 0xd3, 0x22, 0xa2, 0x6d, 0x50, 0x20, 0x46,
	0x24, 0x80, 0x84, 0xe8, 0x0a, 0x00, 0x02, 0x01, 0x33, 0x8c, 0x74, 0x00, 0x07, 0x42, 0xd6, 0x13,
	0x11, 0x38, 0x0c, 0x8c, 0x18, 0x32, 0x49, 0x29, 0x61, 0x82, 0x8a, 0x20, 0x50, 0x41, 0x08, 0xac,
	0xc1, 0x41, 0x30, 0x25, 0x58, 0x8a, 0xb0, 0x42, 0x24, 0x5c, 0x02, 0x5a, 0x00, 0xc5, 0x12, 0x82,
	0x1c, 0x20, 0x42, 0x07, 0xad, 0x04, 0x21, 0x64, 0x52, 0x00, 0x9f, 0x48, 0x48, 0x09, 0x24, 0x31,
	0xa2, 0xa9, 0x14, 0x58, 0xe1, 0x72, 0x15, 0x58, 0x26, 0x89, 0x0d, 0x0e, 0x04, 0x00, 0x00, 0x26,
	0x84, 0x85, 0xa0, 0x26, 0x03, 0x00, 0x32, 0xb9, 0x00, 0xa2, 0x50, 0x05, 0x80, 0x11, 0x10, 0x0e,
	0x85, 0x00, 0x62, 0x61, 0x85, 0x74, 0x99, 0x8a, 0x00, 0x31, 0x4a, 0x01, 0x90, 0x09, 0x0c, 0x26,
	0xd0, 0x90, 0x07, 0xc1, 0x50, 0x41, 0x26, 0xb6, 0xd9, 0x3c, 0x19, 0x83, 0x09, 0x08, 0x71, 0x10,
	0x2a, 0x68, 0x88, 0x25, 0x00, 0xb2, 0xb0, 0x00, 0xc4, 0x82, 0xc1, 0x48, 0x07, 0x92, 0x16, 0x0c,
	0x01, 0xe4, 0x22, 0x02, 0x03, 0x88, 0x0c, 0x81, 0x24, 0x4e, 0x24, 0x48, 0x20, 0x75, 0x41, 0xa2,
	0x28, 0x80, 0x90, 0x12, 0x59, 0x15, 0x47, 0x52, 0x80, 0x00, 0xb1, 0x15, 0x09, 0xc0, 0x18, 0x81,
	0x18, 0x21, 0x44, 0x58, 0x80, 0x28, 0xc2, 0x00, 0xb6, 0xac, 0x58, 0x00, 0x50, 0x24, 0x32, 0x81,
	0x18, 0x15, 0x6a, 0x64, 0x84, 0xc9, 0x21, 0xd0, 0x01, 0x4e, 0x16, 0x31, 0xe8, 0x2e, 0xc1, 0x98,
	0x81, 0x04, 0x03, 0x30, 0xa4, 0x8c, 0xc0, 0x0f, 0x02, 0xe1, 0x08, 0x13, 0x49, 0x00, 0xe0, 0x40,
	0x52, 0xd2, 0x08, 0x09, 0x8c, 0x02, 0x44, 0x60, 0x89, 0xb3, 0x29, 0x93, 0x89, 0x1c, 0x24, 0x9a,
	0x04, 0x71, 0x18, 0x08, 0x30, 0x91, 0xc2, 0x2a, 0x51, 0x40, 0xa6, 0x02, 0x04, 0x19, 0x18, 0xc1,
	0xc8, 0x41, 0x12, 0x06, 0xc0, 0x05, 0x18, 0x47, 0x6c, 0x24, 0x91, 0x21, 0x08, 0x90, 0x4d, 0x6d,
	0x10, 0x24, 0xe0, 0x01, 0x0d, 0x92, 0x8b, 0x16, 0xc0, 0x20, 0xa1, 0x40, 0x00, 0x80, 0x24, 0xd4,
	0x72, 0x00, 0x88, 0x80, 0xc1, 0x20, 0x43, 0x28, 0x3a, 0x58, 0x42, 0xc5, 0x24, 0x54, 0x69, 0xa6,
	0x81, 0x06, 0xac, 0x2a, 0x24, 0x13, 0x12, 0x8c, 0x8a, 0x03, 0x0c, 0x80, 0x32, 0x86, 0xc5, 0x44,
	0xa8, 0x00, 0x30, 0x13, 0x19, 0xc8, 0x43, 0x80, 0x1e, 0x10, 0x08, 0x0b, 0x44, 0x5b, 0xc4, 0x10,
	0x60, 0x09, 0x25, 0x24, 0xd4, 0x45, 0x24, 0x02, 0xc0, 0x1f, 0x89, 0x14, 0x61, 0x18, 0x30, 0x48,
	0x25, 0xac, 0x91, 0xa4, 0x04, 0x51, 0x13, 0xa8, 0x39, 0x14, 0x48, 0x04, 0x20, 0x23, 0x1e, 0x48,
	0x0a, 0x83, 0x08, 0x86, 0x20, 0x35, 0x00, 0x09, 0x01, 0x48, 0x90, 0x01, 0xb6, 0xe0, 0x08, 0x08,
	0x44, 0x41, 0x61, 0x02, 0x50, 0x1c, 0x29, 0x62, 0x03, 0xd0, 0xaa, 0x00, 0x59, 0xad, 0x30, 0x15,
	0x40, 0x0a, 0xfc, 0x81, 0x80, 0x20, 0x63, 0x10, 0x1a, 0x01, 0x15, 0x41, 0x40, 0x47, 0x08, 0x21,
	0x00, 0x96, 0x28, 0x22, 0x92, 0x88, 0x8e, 0x80, 0x0b, 0xe8, 0x22, 0x10, 0x11, 0x00, 0x04, 0x07,
	0xee, 0x46, 0xb2, 0x48, 0x88, 0x01, 0x8c, 0x42, 0x7c, 0x07, 0xa0, 0x84, 0xd0, 0x82, 0xce, 0x06,
	0x97, 0x3a, 0xa0, 0x18, 0x43, 0x81, 0x34, 0x80, 0xa3, 0x01, 0x44, 0xca, 0x2e, 0x44, 0x01, 0x51,
	0x00, 0x18, 0x5d, 0x41, 0x0a, 0x84, 0x90, 0x34, 0x24, 0x00, 0xcc, 0x26, 0x03, 0x08, 0x11, 0xc8,
	0x0a, 0xc6, 0x20, 0x42, 0x01, 0x18, 0x11, 0xca, 0x46, 0x40, 0x51, 0x00, 0x21, 0x88, 0x0b, 0x25,
	0x58, 0x21, 0x80, 0x91, 0x09, 0x18, 0xc2, 0x54, 0x24, 0x41, 0x00, 0xc4, 0xd0, 0x09, 0x0a, 0x10,
	0xb9, 0x80, 0xb8, 0x18, 0x21, 0x24, 0x86, 0x30, 0xac, 0x40, 0xd0, 0x63, 0x64, 0xa0, 0x10, 0x10,
	0x69, 0x41, 0x41, 0x42, 0x11, 0xaa, 0x16, 0x8d, 0x40, 0x8c, 0x2a, 0x50, 0x8b, 0x80, 0xe1, 0x58,
	0x03, 0x46, 0x24, 0x52, 0x84, 0x45, 0x04, 0xa2, 0x52, 0xe6, 0x98, 0x28, 0x10, 0xcf, 0x69, 0x08,
	0x14, 0x48, 0x2a, 0x30, 0x83, 0xc0, 0x50, 0x83, 0x20, 0x07, 0xa4, 0x1b, 0x64, 0x52, 0x43, 0x23,
	0x14, 0x18, 0x01, 0xc2, 0x78, 0x30, 0x09, 0x04, 0xbc, 0x40, 0x40, 0x4c, 0xc1, 0x90, 0x04, 0x40,
	0x0c, 0xa2, 0x70, 0x05, 0xa2, 0x29, 0x09, 0x90, 0xc6, 0x2c, 0xd2, 0x08, 0x0e, 0x68, 0x19, 0xc4,
	0x26, 0xd0, 0x12, 0x00, 0x24, 0x87, 0xa0, 0x18, 0x10, 0x9a, 0x85, 0x78, 0x88, 0x60, 0x52, 0x45,
	0x43, 0x20, 0xdd, 0x01, 0x20, 0x32, 0x44, 0xc9, 0x9a, 0xc8, 0x4a, 0x83, 0x06, 0x25, 0x10, 0x83,
	0xe0, 0x56, 0x00, 0x08, 0x60, 0x2b, 0x8c, 0x28, 0x94, 0x00, 0x26, 0xd0, 0xb1, 0x80, 0x58, 0x9c,
	0x00, 0x32, 0x00, 0x80, 0x01, 0x48, 0x07, 0xe4, 0x44, 0x20, 0x22, 0xa6, 0x99, 0x8a, 0x0c, 0x78,
	0x90, 0x22, 0x15, 0x94, 0x4a, 0x80, 0x46, 0x32, 0x30, 0x37, 0x05, 0xd1, 0x22, 0x10, 0x04, 0x71,
	0xa4, 0x84, 0x13, 0x82, 0x50, 0x44, 0x32, 0x07, 0x50, 0x80, 0x89, 0x18, 0xe3, 0x28, 0x08, 0x91,
	0x5c, 0x20, 0x24, 0x24, 0x42, 0x93, 0x89, 0x80, 0x49, 0x20, 0xb4, 0x41, 0x20, 0x01, 0x00, 0x84,
	0x00, 0x13, 0x29, 0x8a, 0x04, 0x4c, 0x41, 0x4a, 0xc4, 0x60, 0x08, 0x08, 0x04, 0x8d, 0x48, 0xa6,
	0x18, 0x38, 0x0c, 0x84, 0xca, 0x08, 0xa3, 0xb0, 0x8c, 0x0c, 0x0e, 0x4f, 0x34, 0xc0, 0xc0, 0x21,
	0x55, 0x53, 0x04, 0x60, 0x76, 0x9a, 0x28, 0x39, 0x81, 0x83, 0x3a, 0x95, 0x43, 0xb4, 0x21, 0x05,
	0x82, 0x0c, 0x54, 0xb0, 0xb3, 0x80, 0x02, 0x62, 0x5c, 0x04, 0x2a, 0x2e, 0x08, 0x00, 0x65, 0x06,
	0x50, 0x80, 0x8b, 0x90, 0x08, 0x61, 0x12, 0x60, 0x20, 0x0f, 0x50, 0x0b, 0xc1, 0x08, 0x20, 0x41,
	0x10, 0x30, 0x01, 0x60, 0x0c, 0x16, 0x89, 0x01, 0x78, 0x9a, 0x68, 0x22, 0x04, 0x31, 0x30, 0x4c,
	0x12, 0x85, 0x18, 0x42, 0xb1, 0x81, 0x14, 0x8a, 0x05, 0x58, 0x82, 0x83, 0x34, 0x09, 0x83, 0x07,
	0x6e, 0x63, 0x00, 0x1d, 0x00, 0x18, 0xc4, 0x3a, 0xc1, 0x98, 0x3a, 0x2d, 0x41, 0x02, 0x64, 0x15,
	0xa8, 0x26, 0x08, 0x0b, 0x68, 0x48, 0x24, 0x03, 0x16, 0x94, 0x4c, 0x86, 0x46, 0x65, 0x82, 0x8a,
	0xc8, 0x4d, 0x48, 0x34, 0x71, 0x30, 0x2a, 0x20, 0xd4, 0x8b, 0x00, 0xf0, 0x8a, 0x9e, 0xa4, 0x43,
	0x48, 0x1c, 0x02, 0x10, 0x84, 0x01, 0xc5, 0x63, 0x34, 0x16, 0x13, 0x25, 0xc1, 0x10, 0x48, 0x54,
	0xc1, 0xc3, 0x00, 0xd0, 0x10, 0x22, 0x04, 0x84, 0xc1, 0x38, 0x01, 0x02, 0x09, 0x02, 0xa0, 0x00,
	0x8c, 0x24, 0x0c, 0x03, 0x08, 0x11, 0xc8, 0x2d, 0x48, 0x58, 0x0e, 0x00, 0x04, 0xb2, 0x84, 0x30,
	0x80, 0x49, 0x62, 0x00, 0xb8, 0x20, 0x30, 0x08, 0xa7, 0x74, 0x32, 0x08, 0x02, 0x8c, 0xc9, 0x46,
	0x12, 0x91, 0x31, 0x33, 0xa0, 0xc7, 0x85, 0x32, 0x24, 0x68, 0x84, 0x90, 0xc0, 0x09, 0x30, 0x50,
	0x49, 0x8e, 0xa8, 0x90, 0x84, 0x6a, 0x92, 0x39, 0x14, 0x0d, 0x10, 0xa8, 0x48, 0x21, 0x13, 0x2a,
	0x74, 0x98, 0x84, 0x40, 0x96, 0x82, 0x81, 0x00, 0x11, 0x44, 0x00, 0x62, 0xa8, 0xb3, 0x28, 0xd9,
	0x02, 0x30, 0xc5, 0xf2, 0x19, 0x80, 0x57, 0x04, 0x02, 0x41, 0x73, 0x00, 0x0c, 0x90, 0x28, 0x00,
	0x07, 0x32, 0xa8, 0x2c, 0x58, 0x64, 0x42, 0x03, 0x43, 0x18, 0xb4, 0x04, 0x85, 0x2c, 0x21, 0x19,
	0x21, 0x80, 0x02, 0x43, 0x32, 0x54, 0xc0, 0x16, 0x30, 0x8e, 0x21, 0x4a, 0x55, 0x08, 0x02, 0xc0,
	0x8b, 0x20, 0x4c, 0x80, 0x32, 0x38, 0x14, 0xd4, 0xa3, 0x40, 0x63, 0xd3, 0x00, 0x49, 0x10, 0xe4,
	0x18, 0xe0, 0x01, 0x81, 0x04, 0x52, 0x81, 0x6e, 0x33, 0x28, 0x04, 0x05, 0xce, 0x46, 0x72, 0x82,
	0xc2, 0x8d, 0x60, 0x58, 0x0a, 0x36, 0x51, 0x6a, 0x26, 0x04, 0xda, 0x80, 0x0e, 0x06, 0xb1, 0x2a,
	0x89, 0x15, 0x08, 0x30, 0x83, 0x10, 0xa1, 0x01, 0x00, 0x84, 0x1c, 0xa1, 0x70, 0xa4, 0x34, 0x4a,
	0x4e, 0x28, 0x00, 0xa1, 0x2b, 0x10, 0x90, 0x00, 0x28, 0xc2, 0x70, 0x05, 0xf9, 0x41, 0x67, 0x74,
	0x02, 0x18, 0x08, 0xb0, 0xd4, 0x8a, 0x4a, 0xc1, 0x03, 0x0a, 0x91, 0x08, 0x02, 0x3c, 0x25, 0x22,
	0x05, 0x44, 0x50, 0x40, 0x08, 0x55, 0x32, 0x07, 0x49, 0x02, 0x27, 0x00, 0x00, 0x02, 0x9a, 0xa4,
	0x02, 0x80, 0x44, 0x65, 0x4b, 0xa4, 0x20, 0x13, 0x8b, 0x48, 0x81, 0x0a, 0x88, 0xc0, 0x44, 0x08,
	0x56, 0xa7, 0x40, 0x22, 0x05, 0x4e, 0x2e, 0x04, 0xd1, 0x19, 0x88, 0x34, 0x0c, 0x03, 0x00, 0xa2,
	0x21, 0x22, 0xa8, 0x1c, 0x01, 0x06, 0x72, 0x91, 0x15, 0x39, 0xd8, 0x8c, 0x2a, 0x22, 0xc8, 0x38,
	0x48, 0x05, 0x47, 0x48, 0x12, 0x30, 0x0a, 0x2c, 0x16, 0x89, 0x30, 0xd3, 0x02, 0x06, 0x09, 0x18,
	0x24, 0x22, 0x04, 0x02, 0x28, 0x45, 0x46, 0x25, 0x32, 0xc6, 0x00, 0x00, 0x01, 0xc2, 0x0a, 0x78,
	0x51, 0xa8, 0x32, 0xa0, 0x03, 0xe8, 0x58, 0xc6, 0x0a, 0x84, 0x65, 0x01, 0x80, 0x10, 0xd4, 0x12,
	0xa1, 0x60, 0x0e, 0x89, 0x00, 0x96, 0x01, 0x31, 0x80, 0x09, 0x00, 0x08, 0xf4, 0x0b, 0xb3, 0x1c,
	0x8d, 0x20, 0x14, 0x90, 0xe3, 0x12, 0x30, 0x48, 0x0d, 0x1e, 0x04, 0x60, 0x21, 0x88, 0x82, 0x8a,
	0x5a, 0x00, 0x4b, 0x89, 0x44, 0x0e, 0xe0, 0x64, 0x01, 0x72, 0x1c, 0x58, 0x11, 0x04, 0x0c, 0x30,
	0xa0, 0x1a, 0x1d, 0x9d, 0x03, 0x12, 0x02, 0xd0, 0x12, 0x25, 0x43, 0x2a, 0x3a, 0x50, 0x21, 0x35,
	0x69, 0x44, 0x43, 0x08, 0xc0, 0x4a, 0x2c, 0x01, 0x18, 0x41, 0x04, 0x12, 0xd2, 0x0b, 0x48, 0xc7,
	0x40, 0x0a, 0xa4, 0x18, 0x9e, 0x80, 0x88, 0x40, 0x18, 0x55, 0xe1, 0x0c, 0x94, 0x5d, 0x01, 0x70,
	0x83, 0x48, 0x12, 0x90, 0x59, 0x29, 0x24, 0x22, 0x0a, 0x20, 0x38, 0x80, 0x08, 0x08, 0x03, 0x08,
	0x09, 0x64, 0x10, 0x43, 0x20, 0x85, 0x00, 0x2a, 0x1d, 0xda, 0x20, 0x06, 0xe0, 0x22, 0x3c, 0x24,
	0x43, 0x40, 0x26, 0xc0, 0x78, 0x94, 0x08, 0x17, 0x0e, 0x58, 0x20, 0x29, 0x07, 0xa4, 0xcc, 0x80,
	0x54, 0x42, 0xa2, 0x18, 0x91, 0x01, 0xc4, 0x00, 0x80, 0x10, 0x23, 0x7c, 0x05, 0x87, 0x02, 0xc0,
	0x58, 0xaa, 0x01, 0x1e, 0x0b, 0x68, 0x41, 0x03, 0x83, 0xa0, 0x57, 0x01, 0x10, 0x34, 0x19, 0x14,
	0x80, 0x02, 0xa0, 0x28, 0x82, 0x61, 0x2c, 0x61, 0x42, 0x40, 0x0e, 0xa3, 0x92, 0x84, 0x81, 0x10,
	0x43, 0x2c, 0x94, 0x02, 0x04, 0xcc, 0x48, 0x60, 0x2a, 0x51, 0x92, 0x01, 0x18, 0x49, 0xe6, 0x38,
	0x81, 0xa3, 0x2d, 0x60, 0x44, 0x20, 0x10, 0x21, 0x71, 0xb0, 0x04, 0x07, 0xaa, 0x0c, 0xc4, 0x2a,
	0x18, 0x14, 0x00, 0x8e, 0x54, 0xf2, 0x01, 0x18, 0x19, 0x48, 0x23, 0x22, 0xc0, 0x18, 0x0c, 0x35,
	0x06, 0x49, 0x32, 0x10, 0x63, 0x32, 0xac, 0x44, 0x60, 0x08, 0xe1, 0x48, 0x96, 0x88, 0x84, 0x08,
	0x24, 0x00, 0xfb, 0x25, 0x80, 0x81, 0xce, 0x16, 0x02, 0xb9, 0x00, 0xc1, 0x00, 0x0c, 0x44, 0x06,
	0x10, 0x83, 0x40, 0x51, 0xc2, 0x60, 0x23, 0x1a, 0xb8, 0x41, 0x80, 0x42, 0x0c, 0x11, 0xe2, 0x12,
	0xa4, 0x0d, 0x8a, 0x2e, 0x41, 0x98, 0x20, 0x0d, 0x14, 0xe0, 0x6a, 0x41, 0x10, 0x10, 0xc1, 0x54,
	0x05, 0x16, 0x42, 0x18, 0x28, 0x55, 0x0d, 0x03, 0x0a, 0x43, 0x40, 0x00, 0xe0, 0x96, 0x80, 0x10,
	0xa1, 0x48, 0x01, 0xa4, 0x12, 0x40, 0x34, 0xe5, 0x30, 0x00, 0x39, 0x06, 0x23, 0x08, 0x94, 0x21,
	0x19, 0xc8, 0x41, 0x06, 0x14, 0x82, 0x82, 0x80, 0x4c, 0x90, 0x41, 0x3c, 0x03, 0x12, 0xa6, 0xc4,
	0x40, 0x4b, 0x24, 0x44, 0x32, 0x97, 0x7c, 0x02, 0x64, 0x66, 0x52, 0x29, 0x06, 0x85, 0x1c, 0xa2,
	0x34, 0x00, 0x18, 0x21, 0x09, 0x90, 0x21, 0x62, 0x06, 0xcb, 0x00, 0x01, 0x45, 0xa0, 0x20, 0x12,
	0x41, 0x33, 0x94, 0x08, 0x62, 0x1e, 0x82, 0x50, 0x84, 0xb0, 0x10, 0x8d, 0x50, 0x23, 0x8b, 0x14,
	0x00, 0x10, 0x04, 0x48, 0x70, 0x8b, 0x80, 0x88, 0x8f, 0x4d, 0x5c, 0xb0, 0xa1, 0x01, 0x00, 0x84,
	0xa8, 0x04, 0x12, 0xf9, 0x00, 0x08, 0x00, 0x8f, 0x30, 0x41, 0x62, 0x90, 0x05, 0x8a, 0xa3, 0x6a,
	0x90, 0x02, 0x14, 0x58, 0x4d, 0x00, 0x40, 0x04, 0x80, 0xa5, 0xc1, 0x17, 0x02, 0x76, 0x04, 0x83,
	0x07, 0x50, 0x98, 0x49, 0x04, 0x22, 0x90, 0x88, 0x01, 0x5d, 0x04, 0x30, 0x11, 0x58, 0x11, 0x68,
	0x80, 0x4c, 0x28, 0x51, 0x61, 0x15, 0x11, 0x4d, 0x42, 0x02, 0x92, 0x22, 0x02, 0x34, 0x5e, 0x49,
	0x5a, 0xa2, 0xe2, 0x80, 0x98, 0x0a, 0x0f, 0x40, 0x04, 0x60, 0x08, 0x50, 0x12, 0x60, 0x40, 0x90,
	0x1b, 0x8d, 0xd4, 0x86, 0x0e, 0x40, 0x40, 0x70, 0x2b, 0x99, 0xc9, 0x65, 0x00, 0x87, 0x88, 0x2e,
	0x10, 0xd3, 0x84, 0x42, 0x01, 0x58, 0x98, 0x05, 0x80, 0x00, 0x20, 0x60, 0xd8, 0x03, 0x80, 0x92,
	0xcb, 0x0c, 0x40, 0x98, 0x06, 0x29, 0x40, 0x23, 0x20, 0x36, 0x90, 0x91, 0x50, 0x83, 0x0d, 0x16,
	0x24, 0x48, 0xaa, 0x95, 0x0f, 0xc7, 0x60, 0xa0, 0x48, 0x2d, 0xa0, 0x41, 0x28, 0x30, 0x45, 0x82,
	0x02, 0x40, 0x55, 0x0e, 0x2e, 0x90, 0x52, 0x20, 0xa5, 0x03, 0x20, 0x5a, 0x41, 0x71, 0xa1, 0x18,
	0x90, 0x8e, 0x40, 0xf1, 0xe0, 0xad, 0x40, 0x90, 0x21, 0x06, 0x52, 0x32, 0xa4, 0x20, 0x90, 0x02,
	0x0a, 0x90, 0x11, 0x0e, 0x0c, 0x1c, 0xa4, 0x40, 0x21, 0x40, 0x90, 0xa8, 0x4c, 0x8b, 0x3a, 0x20,
	0x53, 0x3a, 0x45, 0x01, 0xa5, 0x14, 0x82, 0xc8, 0xb8, 0x11, 0x8c, 0xa2, 0x10, 0x33, 0x08, 0x86,
	0xe4, 0x11, 0x07, 0x02, 0xb2, 0x28, 0x1b, 0x24, 0x43, 0x04, 0x66, 0x66, 0x62, 0x8e, 0x60, 0x53,
	0x8a, 0x70, 0x90, 0x01, 0x1d, 0x11, 0x14, 0x27, 0x58, 0x10, 0x80, 0x21, 0x89, 0x04, 0xe0, 0x24,
	0x44, 0xc0, 0x99, 0x98, 0x00, 0xcf, 0x08, 0x03, 0x10, 0xa0, 0x5d, 0x18, 0x20, 0x0c, 0xd2, 0xb8,
	0x15, 0x44, 0xc3, 0xa8, 0x60, 0x40, 0x50, 0x91, 0xc0, 0x03, 0x88, 0x4c, 0x82, 0x60, 0x0e, 0x0d,
	0x19, 0x03, 0x26, 0x15, 0x80, 0xba, 0x01, 0xce, 0xa0, 0x10, 0xa0, 0x10, 0x84, 0x50, 0x05, 0x43,
	0x38, 0x62, 0x00, 0x19, 0x00, 0x88, 0x08, 0x64, 0x52, 0x23, 0x14, 0x00, 0xc6, 0x0a, 0x50, 0x84,
	0x0b, 0x01, 0x00, 0x10, 0x05, 0x52, 0x71, 0xd1, 0x0a, 0x80, 0x94, 0xe0, 0x08, 0x22, 0x83, 0xac,
	0x40, 0xc9, 0x2a, 0x4a, 0x34, 0xa2, 0xa2, 0x41, 0x13, 0x80, 0x14, 0x05, 0xc8, 0x18, 0xa0, 0x4c,
	0xa6, 0x00, 0x40, 0x13, 0x01, 0x24, 0x54, 0x22, 0x14, 0xa1, 0x78, 0x98, 0x38, 0x4d, 0x29, 0x06,
	0x60, 0x43, 0x22, 0x2c, 0x8a, 0x40, 0x22, 0x00, 0x49, 0x25, 0xc0, 0xd4, 0x8c, 0x32, 0x85, 0x81,
	0x18, 0x24, 0x5b, 0x04, 0x68, 0x61, 0x39, 0x16, 0x61, 0x42, 0x41, 0x60, 0x10, 0x0a, 0x00, 0x44,
	0x12, 0x40, 0x4c, 0x61, 0x03, 0xa0, 0xc4, 0x90, 0x22, 0x2c, 0xe4, 0x10, 0x98, 0x45, 0x1a, 0x05,
	0x48, 0x02, 0x02, 0x28, 0x74, 0x40, 0x42, 0x06, 0x06, 0x4a, 0x0b, 0xa8, 0x94, 0x28, 0x0c, 0x40,
	0x02, 0x05, 0xd9, 0x8a, 0xc2, 0x00, 0x43, 0x60, 0x20, 0xb0, 0x10, 0x2c, 0x06, 0x05, 0x01, 0x89,
	0x08, 0x8c, 0x03, 0x12, 0x74, 0x21, 0x88, 0x44, 0x87, 0x47, 0x20, 0x25, 0x82, 0x35, 0x10, 0x0a,
	0x0d, 0x0a, 0x22, 0x01, 0x07, 0xd0, 0x80, 0xe3, 0x44, 0x04, 0xf0, 0x38, 0x3d, 0x92, 0xa5, 0x50,
	0xa0, 0x30, 0x80, 0x39, 0x80, 0x40, 0x14, 0x17, 0xd2, 0x02, 0x45, 0xc0, 0xa0, 0x00, 0x34, 0x18,
	0xa0, 0x41, 0x04, 0x82, 0x1e, 0x16, 0x2b, 0x04, 0x41, 0x8a, 0x80, 0x02, 0x00, 0x50, 0x25, 0x71,
	0xd2, 0x21, 0x60, 0x87, 0x81, 0x24, 0x39, 0x15, 0x06, 0x74, 0x22, 0x41, 0xa2, 0xe8, 0x42, 0x02,
	0x08, 0xc3, 0x71, 0x31, 0xc5, 0x44, 0x8b, 0x48, 0x20, 0x02, 0x0a, 0x04, 0x46, 0x0d, 0x40, 0x95,
	0x30, 0x18, 0x19, 0x44, 0x6a, 0x02, 0x92, 0xaa, 0x30, 0xf0, 0x10, 0x87, 0x1e, 0xb2, 0xa8, 0x06,
	0x25, 0x14, 0xcf, 0x74, 0xe0, 0x01, 0x0a, 0x00, 0x08, 0xc7, 0x22, 0x10, 0x08, 0x20, 0x15, 0x0a,
	0x67, 0x64, 0x05, 0x82, 0x01, 0x8c, 0xca, 0xa8, 0x40, 0x00, 0x58, 0xa3, 0x04, 0x12, 0x69, 0x06,
	0x84, 0xb9, 0x9e, 0x2c, 0x40, 0x00, 0x04, 0xa2, 0x5b, 0x01, 0x80, 0x0c, 0xae, 0x2a, 0x05, 0x41,
	0x13, 0x00, 0xc0, 0x0d, 0x50, 0x95, 0x22, 0xb0, 0x10, 0x43, 0x0c, 0x38, 0x03, 0xfa, 0x12, 0x08,
	0x52, 0x86, 0x40, 0x84, 0x00, 0x1d, 0xb0, 0xc6, 0x8c, 0x0a, 0x70, 0x92, 0x0d, 0x38, 0x00, 0x82,
	0x60, 0xd4, 0x32, 0xa3, 0x88, 0x10, 0x04, 0x28, 0x51, 0x18, 0x10, 0x29, 0x98, 0x20, 0x50, 0x04,
	0xc0, 0x2e, 0x88, 0x51, 0x0a, 0x28, 0x55, 0x32, 0xb1, 0x90, 0x0d, 0x62, 0x60, 0x04, 0x00, 0xb6,
	0xdc, 0x58, 0xc9, 0x34, 0x14, 0x41, 0xa8, 0xc1, 0x44, 0x82, 0x38, 0x90, 0x00, 0x19, 0x90, 0x11,
	0x01, 0x3a, 0x04, 0xc0, 0x2f, 0x00, 0x88, 0x21, 0x28, 0x20, 0x51, 0x93, 0x01, 0x89, 0x61, 0x14,
	0x26, 0x29, 0x15, 0xe4, 0x12, 0x64, 0x00, 0xc1, 0x28, 0x0b, 0x45, 0x02, 0xa8, 0x44, 0x63, 0x80,
	0x8a, 0x94, 0x8a, 0x04, 0x42, 0x04, 0x28, 0x95, 0x25, 0x86, 0x42, 0x40, 0x02, 0xb3, 0xa0, 0xc8,
	0x1e, 0x00, 0x1e, 0x51, 0xb0, 0x02, 0x7c, 0x4c, 0x00, 0x06, 0x86, 0xe8, 0x38, 0x80, 0x0c, 0xad,
	0x02, 0xc4, 0x02, 0x89, 0xb1, 0x81, 0xa4, 0x20, 0x21, 0x20, 0x2d, 0xd1, 0x54, 0x02, 0x08, 0x62,
	0x68, 0x81, 0xa8, 0x11, 0x05, 0x42, 0x50, 0x10, 0x92, 0x91, 0x50, 0x4c, 0x18, 0x04, 0x71, 0x04,
	0x24, 0xd5, 0x2a, 0x14, 0x00, 0x89, 0xaa, 0x78, 0x08, 0x04, 0x5c, 0x50, 0x00, 0x04, 0x11, 0x08,
	0x64, 0x0c, 0xd1, 0x92, 0x91, 0x0c, 0x13, 0x61, 0x00, 0x40, 0xd3, 0x22, 0xc1, 0x8e, 0x86, 0x1c,
	0x34, 0xd9, 0x31, 0x4c, 0xc1, 0xa2, 0x08, 0x80, 0x02, 0x82, 0x60, 0x08, 0x00, 0x06, 0x62, 0x48,
	0x20, 0x11, 0x4a, 0x26, 0x00, 0x71, 0x49, 0x14, 0x84, 0x07, 0xc7, 0x40, 0xa0, 0x43, 0xa7, 0x80,
	0x8f, 0x20, 0x56, 0x35, 0x12, 0x10, 0x50, 0x1c, 0x43, 0x38, 0x20, 0xab, 0x1c, 0xcd, 0xc0, 0xc1,
	0x08, 0x12, 0x00, 0x09, 0xd8, 0x18, 0xa5, 0x58, 0x72, 0x43, 0x20, 0x41, 0x41, 0xe8, 0x48, 0xa0,
	0x90, 0xa2, 0x70, 0xc1, 0x45, 0x36, 0x00, 0x69, 0x93, 0xec, 0x87, 0x28, 0x36, 0x00, 0x0a, 0xb0,
	0x8c, 0x41, 0x02, 0x62, 0x26, 0x53, 0x2a, 0x80, 0x48, 0x2b, 0x42, 0xc5, 0x91, 0x99, 0x78, 0x00,
	0x61, 0x26, 0x14, 0x20, 0x1d, 0x10, 0x01, 0x4a, 0x48, 0xa0, 0x08, 0x2f, 0x90, 0x8a, 0x4c, 0x04,
	0xe0, 0xa1, 0x82, 0x31, 0x45, 0x22, 0x30, 0x04, 0x20, 0x10, 0x08, 0x81, 0xe3, 0x50, 0xb1, 0x22,
	0xa0, 0x4c, 0x8c, 0x49, 0x60, 0x66, 0xa1, 0x0e, 0x09, 0x11, 0xa4, 0x00, 0x80, 0xa0, 0x9c, 0x21,
	0x43, 0x61, 0x18, 0x03, 0x58, 0x80, 0xc1, 0xc5, 0x22, 0x08, 0x31, 0x12, 0x80, 0x00, 0x18, 0x08,
	0x1a, 0x85, 0xc3, 0x34, 0x90, 0x48, 0x20, 0x06, 0x13, 0x49, 0x28, 0xa1, 0x01, 0x0d, 0x02, 0x65,
	0x50, 0x15, 0x95, 0x10, 0x0c, 0x02, 0x04, 0x80, 0x09, 0x18, 0x8c, 0x49, 0x04, 0x25, 0x91, 0x18,
	0x38, 0x1b, 0x01, 0x72, 0x02, 0x90, 0x20, 0x80, 0x03, 0xa4, 0x06, 0xc1, 0x78, 0x08, 0x98, 0x10,
	0x00, 0x10, 0x85, 0x01, 0x8f, 0x81, 0x48, 0x82, 0x08, 0xc2, 0x08, 0xa5, 0x14, 0x5f, 0x83, 0x20,
	0x55, 0x70, 0x0d, 0x41, 0x00, 0x04, 0x5e, 0x20, 0xb1, 0x03, 0xb1, 0x88, 0xc0, 0x48, 0x40, 0xcb,
	0x20, 0xe5, 0x41, 0xc6, 0x40, 0x12, 0x91, 0xb3, 0x78, 0x40, 0x23, 0x24, 0x03, 0x20, 0x87, 0x04,
	0x0d, 0x88, 0x30, 0x90, 0x20, 0x10, 0x80, 0x03, 0x68, 0x68, 0xc6, 0x10, 0x15, 0x01, 0xcc, 0x20,
	0x08, 0x06, 0x78, 0x0c, 0x20, 0x94, 0x85, 0x02, 0x91, 0x10, 0x06, 0xc8, 0x12, 0x86, 0x48, 0x02,
	0xb0, 0x04, 0x04, 0x1a, 0xe8, 0x00, 0x96, 0x82, 0x20, 0x35, 0x98, 0x82, 0x48, 0x80, 0x10, 0x29,
	0x84, 0xcb, 0x22, 0x6c, 0x50, 0x00, 0xb0, 0x9d, 0x02, 0x02, 0x4c, 0x24, 0x48, 0x8e, 0x01, 0x50,
	0xcf, 0x40, 0x42, 0x10, 0x1b, 0x29, 0x09, 0x29, 0x24, 0x00, 0x10, 0x82, 0x00, 0x42, 0x05, 0x22,
	0x01, 0xc8, 0x15, 0x98, 0x18, 0x05, 0x04, 0x34, 0x0a, 0x85, 0x80, 0x1e, 0xa6, 0x34, 0x80, 0xe9,
	0x00, 0x75, 0x05, 0xcb, 0x12, 0x03, 0x51, 0x25, 0x6c, 0x10, 0x8a, 0x44, 0x87, 0xa0, 0x11, 0x08,
	0x01, 0xa2, 0x42, 0xc5, 0x38, 0x22, 0x24, 0x81, 0xc6, 0x70, 0x82, 0xe1, 0x10, 0x04, 0x1a, 0x88,
	0x14, 0x20, 0x12, 0x26, 0x04, 0x5c, 0x49, 0x12, 0x60, 0x12, 0x38, 0xd0, 0x05, 0x81, 0x20, 0x67,
	0x92, 0x12, 0x49, 0x01, 0xa1, 0x0c, 0x43, 0x60, 0x18, 0xc4, 0xc3, 0x08, 0x60, 0x52, 0x82, 0x16,
	0xbc, 0x88, 0x64, 0x24, 0x61, 0xd0, 0x00, 0x70, 0x19, 0x20, 0x74, 0x24, 0x00, 0x19, 0xc0, 0xc4,
	0x02, 0x04, 0xb1, 0x42, 0x85, 0x10, 0x94, 0x6a, 0x28, 0x10, 0x21, 0xa9, 0xcc, 0x08, 0xc8, 0x02,
	0x42, 0x92, 0xb5, 0x08, 0x12, 0x81, 0x0a, 0x82, 0xe0, 0x05, 0x49, 0x5b, 0x0c, 0x0c, 0x24, 0xc9,
	0x20, 0x1c, 0x8a, 0x83, 0x0e, 0x60, 0x99, 0x06, 0x91, 0x00, 0x4d, 0x56, 0x10, 0x5b, 0x98, 0x25,
	0xc9, 0x89, 0x00, 0xb0, 0x48, 0xa1, 0x10, 0x0d, 0x06, 0x42, 0xc6, 0x80, 0x20, 0x10, 0xc3, 0x28,
	0x74, 0x62, 0xa1, 0x09, 0x01, 0x44, 0xe6, 0x22, 0x26, 0x88, 0x11, 0xa8, 0xd2, 0x00, 0x58, 0x02,
	0x19, 0x00, 0xa1, 0x04, 0x01, 0x18, 0xa5, 0x91, 0x16, 0xd0, 0xd0, 0x60, 0x00, 0x05, 0x08, 0x02,
	0x0d, 0x1d, 0xe4, 0x02, 0xd6, 0x08, 0x3a, 0x48, 0x19, 0x0c, 0x3c, 0x00, 0x20, 0xa3, 0xa0, 0x54,
	0x21, 0x14, 0x41, 0x88, 0x28, 0x49, 0x00, 0x86, 0x24, 0x33, 0x82, 0x30, 0x58, 0x01, 0x4f, 0x28,
	0x93, 0x69, 0x29, 0x54, 0x45, 0x82, 0x50, 0xc3, 0x8a, 0x38, 0x30, 0x46, 0x45, 0x2a, 0x14, 0x08,
	0x8e, 0x99, 0x06, 0x60, 0x02, 0x10, 0xe8, 0x00, 0x19, 0x82, 0x21, 0x5c, 0x20, 0x00, 0xa7, 0x24,
	0x86, 0x00, 0x28, 0x36, 0x02, 0x1c, 0xc4, 0x0b, 0x0c, 0x0e, 0xb4, 0x08, 0x0d, 0x64, 0xd4, 0xc4,
	0x00, 0x81, 0x09, 0xb1, 0x01, 0x41, 0x2e, 0x14, 0x20, 0x08, 0x82, 0x0d, 0x51, 0xca, 0x0a, 0x07,
	0x23, 0xb0, 0x28, 0x05, 0x09, 0x20, 0x60, 0x90, 0x99, 0x90, 0x82, 0x0e, 0x0e, 0xc1, 0x70, 0x24,
	0x60, 0xcb, 0xc3, 0x00, 0xa0, 0xea, 0x17, 0x24, 0x14, 0x04, 0x70, 0x22, 0x00, 0x01, 0x48, 0xc1,
	0xa4, 0x20, 0x24, 0x70, 0x28, 0x8d, 0xc4, 0x44, 0x00, 0xa1, 0x51, 0x20, 0x7d, 0x88, 0x01, 0x1c,
	0xa3, 0xa0, 0x85, 0x8c, 0x50, 0xa8, 0x44, 0x60, 0x02, 0x3c, 0x44, 0x55, 0xa3, 0x30, 0x06, 0xb3,
	0x0c, 0x44, 0x12, 0xac, 0x0a, 0x14, 0x21, 0x03, 0x15, 0x59, 0x20, 0x58, 0xa0, 0x9a, 0x94, 0x11,
	0x08, 0x63, 0x62, 0xe0, 0x42, 0x23, 0xb5, 0xce, 0x00, 0x00, 0x15, 0x10, 0x82, 0xa0, 0x1a, 0x4e,
	0x00, 0x23, 0x63, 0x18, 0x10, 0x94, 0x04, 0x52, 0x82, 0xbb, 0x08, 0x70, 0xc6, 0x41, 0x68, 0x02,
	0x90, 0x14, 0x08, 0x93, 0x88, 0x10, 0x51, 0x78, 0x05, 0x44, 0x18, 0x8e, 0x50, 0xd6, 0x43, 0x84,
	0x08, 0x8a, 0x80, 0x00, 0xc0, 0x22, 0x14, 0x40, 0x04, 0xc0, 0x48, 0x05, 0xa0, 0x01, 0x08, 0x40,
	0x80, 0x40, 0x40, 0xcb, 0x94, 0x0d, 0xd2, 0x8a, 0x46, 0x86, 0x80, 0x80, 0xb9, 0x48, 0x40, 0x74,
	0x95, 0x10, 0xa8, 0x34, 0x8f, 0x0d, 0x0e, 0x16, 0x08, 0x2a, 0x50, 0x90, 0x0b, 0x5a, 0xc5, 0x6a,
	0x14, 0x80, 0x8d, 0x48, 0x02, 0x03, 0x02, 0x81, 0x21, 0x4c, 0x86, 0x5a, 0x82, 0x83, 0x09, 0x99,
	0x02, 0x20, 0x04, 0x60, 0x6b, 0x89, 0x48, 0x08, 0x6a, 0x68, 0x84, 0x40, 0xa5, 0xc4, 0x0a, 0x81,
	0x40, 0x21, 0x28, 0x16, 0x04, 0x02, 0x61, 0x16, 0x94, 0x00, 0x3a, 0x80, 0xd6, 0x2c, 0x22, 0x64,
	0x48, 0x80, 0x08, 0xc1, 0x40, 0x38, 0x00, 0x80, 0x18, 0x08, 0x10, 0xc1, 0x34, 0x03, 0xda, 0x82,
	0x44, 0x03, 0x4b, 0x0a, 0x74, 0x38, 0x85, 0x41, 0x42, 0xc5, 0x32, 0x65, 0x40, 0x26, 0xb4, 0x90,
	0x21, 0x6a, 0x94, 0x0b, 0x09, 0x20, 0x14, 0x21, 0x72, 0x80, 0x82, 0x25, 0x20, 0x80, 0x2a, 0x0a,
	0x12, 0x81, 0x81, 0x10, 0x02, 0x08, 0x18, 0x62, 0x80, 0x88, 0x55, 0x58, 0x09, 0x0e, 0xe2, 0x22,
	0x31, 0x61, 0x53, 0x62, 0x68, 0x54, 0x12, 0x9b, 0xc0, 0x9d, 0x82, 0x2e, 0x00, 0x88, 0x20, 0x45,
	0xc0, 0x40, 0x72, 0x62, 0x0b, 0x04, 0x31, 0x18, 0xe6, 0x22, 0x60, 0x48, 0x1a, 0x40, 0x80, 0xa7,
	0x12, 0x72, 0x10, 0x9d, 0x80, 0x8d, 0x82, 0x08, 0x66, 0xc2, 0xb4, 0x24, 0x00, 0x20, 0x54, 0x90,
	0x28, 0x0c, 0x08, 0x81, 0xe9, 0x10, 0x04, 0xb3, 0x28, 0x60, 0x05, 0x22, 0x0c, 0x10, 0x81, 0x09,
	0x01, 0x98, 0x0c, 0x24, 0x12, 0x41, 0x02, 0x88, 0xc2, 0x42, 0x04, 0x95, 0x80, 0xa1, 0x48, 0x1b,
	0x85, 0x0c, 0x85, 0x03, 0x9a, 0x88, 0x8e, 0x20, 0x04, 0x04, 0x91, 0x10, 0x31, 0x1e, 0x01, 0x40,
	0x03, 0x10, 0xbe, 0x48, 0x04, 0x0a, 0x74, 0x60, 0x01, 0x03, 0xf4, 0x46, 0x46, 0x10, 0x41, 0x09,
	0x8a, 0xb0, 0x43, 0x88, 0x58, 0xd1, 0x20, 0x26, 0x94, 0xc4, 0x04, 0x5e, 0x83, 0x18, 0x1b, 0x59,
	0x88, 0xc2, 0x10, 0x02, 0x11, 0x30, 0xa0, 0x56, 0xa5, 0x08, 0x00, 0x20, 0x0c, 0xf4, 0x0a, 0xad,
	0x00, 0x14, 0x20, 0x21, 0x80, 0x13, 0x25, 0x2c, 0x80, 0x12, 0x13, 0x50, 0x88, 0x67, 0x12, 0x15,
	0x4a, 0x04, 0x0d, 0xc9, 0x06, 0x02, 0x10, 0xd8, 0x10, 0xcc, 0x0b, 0x02, 0x24, 0x04, 0x02, 0xa4,
	0x75, 0x04, 0x28, 0x00, 0xa2, 0x40, 0x90, 0x3c, 0x44, 0xc3, 0x00, 0xe2, 0x61, 0x83, 0x20, 0x06,
	0x03, 0x58, 0x50, 0x09, 0x31, 0x80, 0xce, 0x08, 0x04, 0xa5, 0x98, 0x1c, 0x89, 0x95, 0x04, 0x02,
	0x30, 0x00, 0x28, 0x05, 0x05, 0xc1, 0x52, 0xa0, 0x10, 0x2e, 0x19, 0x80, 0x28, 0x50, 0x56, 0x21,
	0x0f, 0x08, 0x52, 0x21, 0x46, 0x26, 0x08, 0x02, 0x14, 0x10, 0x80, 0x5c, 0x10, 0x82, 0xa1, 0xc0,
	0xd5, 0x0e, 0x50, 0x10, 0x43, 0x05, 0x48, 0x95, 0x21, 0x26, 0x41, 0xa0, 0x20, 0x2d, 0x4c, 0x42,
	0x02, 0xc3, 0x03, 0x99, 0xc8, 0x03, 0x0c, 0x22, 0x40, 0x50, 0x26, 0x90, 0x02, 0x81, 0x08, 0x42,
	0x2b, 0xa5, 0x00, 0x46, 0x65, 0x22, 0x72, 0x4a, 0x08, 0x80, 0x98, 0xc5, 0x0a, 0x80, 0xc9, 0x19,
	0x54, 0x94, 0x05, 0x16, 0x02, 0xc0, 0x82, 0x01, 0x88, 0xc3, 0x38, 0xa1, 0x51, 0x1d, 0x08, 0x8a,
	0xc1, 0x44, 0x45, 0x3a, 0xb0, 0x00, 0x05, 0xc0, 0x28, 0x41, 0xe9, 0x04, 0x09, 0x50, 0x08, 0x02,
	0x30, 0x28, 0x86, 0x21, 0x8b, 0x00, 0x02, 0xe4, 0xaa, 0x0e, 0xb1, 0x00, 0xe5, 0x20, 0xa1, 0x11,
	0x1b, 0x11, 0x80, 0x0c, 0x2a, 0xd5, 0x40, 0x02, 0x35, 0x1d, 0x40, 0x38, 0x00, 0x61, 0x1a, 0x24,
	0x49, 0x0c, 0x1c, 0x27, 0xd0, 0x01, 0x00, 0xc0, 0xa3, 0x12, 0x14, 0x2a, 0x15, 0x4d, 0x01, 0x01,
	0x06, 0xc2, 0x38, 0x2e, 0xec, 0x94, 0xc2, 0x50, 0x00, 0x21, 0x24, 0x1d, 0x40, 0xac, 0x04, 0x15,
	0x30, 0xb0, 0x01, 0x81, 0x00, 0x62, 0x40, 0x43, 0x3f, 0x25, 0x80, 0x02, 0x06, 0x31, 0x18, 0xb4,
	0x54, 0x1a, 0xcb, 0x10, 0x22, 0x00, 0x02, 0x6c, 0x10, 0x6a, 0x72, 0x20, 0x83, 0x13, 0x44, 0x88,
	0xcd, 0x2c, 0xa1, 0x19, 0x0f, 0x95, 0x54, 0x8d, 0x3a, 0x37, 0xc0, 0xb3, 0x08, 0x55, 0x61, 0x50,
	0xd2, 0x8b, 0x84, 0x01, 0x48, 0xc0, 0x28, 0x04, 0x48, 0x08, 0xa1, 0x06, 0x42, 0x46, 0x03, 0x40,
	0x00, 0x40, 0x80, 0x20, 0x50, 0xb3, 0x13, 0x08, 0x50, 0x50, 0x04, 0x08, 0x64, 0x32, 0x2f, 0x7c,
	0x0c, 0x41, 0x62, 0x13, 0xe0, 0x38, 0xa8, 0xd1, 0x20, 0x10, 0x05, 0xf2, 0xa4, 0x15, 0x03, 0x01,
	0x52, 0x05, 0x7a, 0xa8, 0x05, 0x09, 0xc0, 0x62, 0xc4, 0x10, 0x0a, 0x45, 0x08, 0x69, 0x24, 0x61,
	0x01, 0x00, 0x81, 0x16, 0x88, 0x0a, 0x14, 0x81, 0x83, 0x0c, 0x02, 0x4d, 0x1a, 0x84, 0xaa, 0x81,
	0x91, 0xc6, 0x0a, 0x24, 0x10, 0x51, 0x0d, 0x88, 0x90, 0x4e, 0x5a, 0x21, 0xc1, 0xa2, 0xa4, 0x90,
	0x0c, 0x34, 0x44, 0x21, 0x80, 0x48, 0x43, 0x0b, 0x48, 0x43, 0x92, 0x90, 0x30, 0xc0, 0x06, 0x14,
	0x45, 0xb9, 0x00, 0x08, 0x1b, 0x88, 0x20, 0x44, 0xa9, 0x11, 0x60, 0x0c, 0x8a, 0x30, 0x03, 0x48,
	0x28, 0x88, 0x11, 0x86, 0x64, 0x23, 0x8a, 0x22, 0x30, 0xc1, 0xa1, 0x28, 0x40, 0x00, 0x05, 0xe4,
	0x0d, 0x86, 0x3a, 0x22, 0x62, 0xaf, 0x8c, 0x56, 0x60, 0x10, 0x10, 0xb8, 0x18, 0x41, 0x04, 0x24,
	0x48, 0x30, 0x82, 0x15, 0x70, 0x00, 0x88, 0x42, 0x53, 0x20, 0xad, 0x81, 0x98, 0x4b, 0x50, 0x86,
	0x02, 0x0b, 0x40, 0x88, 0x2d, 0x4c, 0xa0, 0x02, 0x00, 0x08, 0x42, 0x40, 0x16, 0x84, 0x00, 0x99,
	0x4c, 0x00, 0xa2, 0x22, 0x40, 0x52, 0x14, 0x10, 0x84, 0xa9, 0x0c, 0x26, 0x92, 0x08, 0x24, 0x49,
	0x8e, 0x42, 0x93, 0x49, 0x8a, 0x54, 0x04, 0x88, 0x06, 0x40, 0x08, 0x1d, 0x21, 0x90, 0x00, 0x60,
	0x21, 0x02, 0x94, 0x98, 0x89, 0x04, 0x1a, 0x94, 0x5b, 0x87, 0x20, 0x44, 0x47, 0x48, 0x90, 0x2a,
	0x18, 0x80, 0xd1, 0x43, 0x14, 0x02, 0x29, 0xa0, 0x8c, 0x10, 0xe1, 0x60, 0xf0, 0x81, 0x09, 0xc5,
	0xc0, 0x2d, 0x08, 0x95, 0x82, 0xa4, 0x04, 0x4a, 0x40, 0x12, 0x00, 0x11, 0xaf, 0xc1, 0x94, 0x26,
	0x3e, 0x60, 0x41, 0x10, 0x41, 0x87, 0xa0, 0x16, 0x42, 0x60, 0x9a, 0x8c, 0x04, 0xe0, 0x14, 0x62,
	0x12, 0x18, 0xf4, 0xc0, 0x02, 0x0c, 0x10, 0x21, 0x98, 0x01, 0xde, 0x82, 0x08, 0xb3, 0x60, 0x11,
	0x18, 0x01, 0x49, 0x32, 0x00, 0xd0, 0x0a, 0x58, 0x91, 0x29, 0x26, 0x04, 0x48, 0x00, 0x35, 0x00,
	0xc8, 0x08, 0x31, 0x32, 0x0e, 0x70, 0x0a, 0x46, 0x00, 0x17, 0x70, 0x88, 0x90, 0xc0, 0x8b, 0x00,
	0x04, 0x28, 0x22, 0x45, 0x11, 0x64, 0x38, 0x10, 0xb1, 0x1a, 0xa4, 0x00, 0x24, 0x4a, 0x50, 0x08,
	0x82, 0xa0, 0x0f, 0xc0, 0x0e, 0x85, 0x9b, 0x04, 0x31, 0x11, 0x4c, 0x02, 0xf4, 0xc2, 0x90, 0xc5,
	0x03, 0x02, 0x04, 0xc3, 0x10, 0x08, 0xa0, 0x57, 0xc0, 0x42, 0x05, 0x80, 0x00, 0x94, 0x42, 0x00,
	0x52, 0x45, 0x81, 0x01, 0x09, 0x1d, 0x4b, 0x2a, 0xa6, 0xa0, 0x30, 0x04, 0x95, 0x00, 0x0a, 0x52,
	0x08, 0x28, 0x24, 0x12, 0x65, 0x14, 0x24, 0x70, 0xa4, 0x00, 0x98, 0xe2, 0x68, 0x10, 0x0a, 0x33,
	0x41, 0x80, 0x01, 0x44, 0x90, 0x0a, 0x36, 0xc1, 0x5b, 0x22, 0x40, 0x71, 0x23, 0xa4, 0x01, 0x14,
	0x8a, 0x64, 0x62, 0xab, 0x9a, 0x10, 0x48, 0xc8, 0x04, 0x10, 0xc1, 0x82, 0x11, 0xca, 0xc1, 0x14,
	0x96, 0x58, 0x04, 0xe0, 0x01, 0x00, 0x20, 0xa6, 0x8b, 0x81, 0x1c, 0xcf, 0x08, 0x30, 0x12, 0x53,
	0x1b, 0xb8, 0x12, 0x0a, 0x5c, 0x22, 0x61, 0x25, 0xc0, 0x84, 0x0f, 0x42, 0x12, 0x00, 0xa4, 0x09,
	0x94, 0x0e, 0x20, 0xd0, 0x71, 0x22, 0x04, 0x0b, 0x00, 0x60, 0xa3, 0x10, 0x00, 0x75, 0x5b, 0x00,
	0x70, 0x90, 0x50, 0x86, 0x24, 0x1e, 0x80, 0x18, 0x35, 0x02, 0x81, 0x38, 0xc7, 0x60, 0x4c, 0x06,
	0x00, 0x92, 0x08, 0x00, 0x20, 0x50, 0x01, 0x42, 0x30, 0x25, 0x44, 0x6c, 0x34, 0x34, 0x20, 0x12,
	0x04, 0xc1, 0xc1, 0x1a, 0x83, 0x09, 0x20, 0x90, 0x86, 0x40, 0x08, 0x35, 0x48, 0x94, 0xe1, 0x09,
	0x05, 0x5e, 0x34, 0x08, 0x01, 0x91, 0x12, 0xca, 0x10, 0xd2, 0x30, 0x23, 0x45, 0x0e, 0x48, 0x34,
	0x24, 0xc3, 0xbf, 0x50, 0xd8, 0x2c, 0x02, 0x31, 0x12, 0x90, 0x20, 0x42, 0x21, 0x24, 0xc5, 0x21,
	0x85, 0x24, 0x14, 0x04, 0x3c, 0x11, 0x08, 0x10, 0x00, 0x09, 0x6b, 0x10, 0xc6, 0xb8, 0x80, 0x8c,
	0x0d, 0x0b, 0x32, 0x85, 0x5a, 0x01, 0x98, 0x01, 0x01, 0x00, 0x62, 0x70, 0xa6, 0xc0, 0x4c, 0x40,
	0x20, 0x40, 0x81, 0x00, 0x28, 0x80, 0x04, 0x08, 0x30, 0x19, 0x15, 0xb8, 0xda, 0x28, 0x5c, 0x80,
	0xe1, 0x08, 0x41, 0x52, 0x45, 0x4c, 0x23, 0x09, 0x81, 0x08, 0x00, 0x02, 0x40, 0x63, 0x00, 0x32,
	0xd0, 0x12, 0xa6, 0x2a, 0x81, 0x38, 0xa3, 0x15, 0x54, 0x03, 0x58, 0x00, 0x22, 0x00, 0x0d, 0x90,
	0x82, 0x10, 0x21, 0x80, 0x25, 0x01, 0x11, 0xc0, 0x44, 0x81, 0x5b, 0x02, 0xc4, 0x00, 0x02, 0x42,
	0x60, 0x91, 0x20, 0x10, 0x80, 0xe5, 0x10, 0x13, 0x48, 0x1a, 0xa0, 0xc1, 0x48, 0x0a, 0x14, 0x29,
	0xb0, 0x24, 0x0b, 0x00, 0x70, 0xe1, 0x02, 0x0c, 0x51, 0x40, 0xca, 0x0c, 0x02, 0x8a, 0x11, 0xa8,
	0x87, 0xad, 0x18, 0xe0, 0xb1, 0x85, 0x65, 0x08, 0x6c, 0x48, 0xa3, 0xc0, 0x24, 0x11, 0x8a, 0xc8,
	0x22, 0x04, 0x88, 0x98, 0x18, 0x50, 0x22, 0x00, 0x56, 0x61, 0x21, 0x00, 0xde, 0x84, 0x02, 0x10,
	0x53, 0x22, 0xdd, 0x13, 0x08, 0x2a, 0x06, 0xc1, 0xa4, 0xc8, 0x00, 0x00, 0x12, 0xa6, 0xc8, 0xa0,
	0x1c, 0x8c, 0x22, 0x2e, 0x02, 0x18, 0x1f, 0xf0, 0x03, 0x07, 0x02, 0x03, 0x09, 0x33, 0xa4, 0x10,
	0x08, 0x60, 0xd1, 0x0a, 0x0e, 0x90, 0x5c, 0x40, 0x70, 0xa4, 0x02, 0x18, 0xd1, 0x00, 0xa3, 0x1e,
	0x80, 0x70, 0x02, 0xc5, 0x84, 0xe2, 0x00, 0x20, 0x30, 0x91, 0x05, 0x43, 0x81, 0x04, 0x62, 0x98,
	0x92, 0x41, 0x00, 0x64, 0x02, 0x13, 0xa2, 0x0d, 0x40, 0x13, 0x0c, 0x40, 0x25, 0x42, 0xa4, 0x40,
	0x19, 0x09, 0x10, 0xa1, 0x6b, 0x10, 0x25, 0x19, 0x04, 0x06, 0xd4, 0xc8, 0x13, 0x28, 0x42, 0x23,
	0x26, 0x22, 0x20, 0x09, 0xe0, 0x08, 0x04, 0x00, 0xd5, 0xc2, 0x30, 0x38, 0x54, 0x01, 0x2e, 0x00,
	0xa1, 0x14, 0x80, 0x08, 0xc5, 0x4a, 0x10, 0x40, 0x00, 0x60, 0xc6, 0x24, 0x06, 0xd1, 0x20, 0x0a,
	0x09, 0x06, 0x0c, 0x28, 0x00, 0x11, 0xa8, 0xc9, 0xc8, 0x02, 0x08, 0x86, 0xb2, 0x21, 0x19, 0x87,
	0x62, 0x3c, 0x56, 0x40, 0x0b, 0x24, 0x94, 0xa0, 0x28, 0x44, 0x00, 0x80, 0xb4, 0x08, 0x08, 0x04,
	0x47, 0xeb, 0x24, 0xa0, 0x50, 0x64, 0x70, 0xa0, 0x13, 0xb8, 0xc0, 0x01, 0x29, 0x08, 0x90, 0x21,
	0x92, 0x54, 0xc1, 0x08, 0x0a, 0x02, 0xe3, 0x21, 0x00, 0x18, 0x44, 0x06, 0x05, 0x00, 0x08, 0x68,
	0x10, 0xa4, 0x12, 0x20, 0x5b, 0x3d, 0x2c, 0xd0, 0x45, 0x08, 0x13, 0x03, 0x89, 0x90, 0x82, 0x40,
	0x44, 0xd1, 0xe1, 0x22, 0x00, 0xca, 0x02, 0x00, 0x06, 0xb8, 0xb8, 0x00, 0x8f, 0x07, 0x10, 0xc4,
	0x58, 0xa8, 0x65, 0x0e, 0x86, 0x4e, 0x00, 0x80, 0x25, 0x81, 0x1d, 0x60, 0x20, 0x63, 0x42, 0x08,
	0x25, 0x04, 0x40, 0x56, 0x20, 0x10, 0x80, 0x14, 0x0b, 0x21, 0x32, 0xc3, 0x48, 0x00, 0x24, 0xdb,
	0x84, 0x40, 0x91, 0x02, 0x82, 0x98, 0x41, 0x09, 0x64, 0x06, 0x41, 0x86, 0x41, 0xc5, 0x08, 0x2a,
	0x00, 0xb8, 0x11, 0x4d, 0x12, 0x41, 0x18, 0xe2, 0x60, 0xa0, 0x11, 0x04, 0x0a, 0x08, 0x80, 0xe0,
	0x14, 0x05, 0x91, 0x8f, 0x48, 0x30, 0x82, 0x31, 0x54, 0x01, 0x02, 0x50, 0x00, 0x28, 0x33, 0x68,
	0x90, 0x0a, 0x12, 0x24, 0x43, 0x20, 0x04, 0x03, 0x63, 0x70, 0x61, 0x31, 0x10, 0x3c, 0x44, 0x23,
	0x30, 0x40, 0x0a, 0x08, 0x8d, 0x45, 0x29, 0x14, 0x10, 0x40, 0x06, 0xc5, 0x40, 0x85, 0x4a, 0x52,
	0x08, 0x88, 0xa0, 0x90, 0x09, 0x0e, 0xe4, 0x02, 0x82, 0x71, 0x19, 0x02, 0x42, 0x26, 0x08, 0x3d,
	0x01, 0x90, 0x6c, 0x40, 0x31, 0x4b, 0x81, 0xe9, 0x86, 0xa2, 0x0c, 0x94, 0x62, 0x80, 0x41, 0x1b,
	0x42, 0x22, 0xc3, 0x00, 0x18, 0x29, 0x0e, 0x24, 0x1e, 0x02, 0x88, 0x8c, 0x0c, 0xc1, 0x28, 0x62,
	0x71, 0x01, 0x01, 0x70, 0x85, 0xc2, 0x62, 0x41, 0x01, 0x2e, 0x60, 0x08, 0x0d, 0x12, 0x56, 0xca,
	0x80, 0xbc, 0x00, 0x08, 0x00, 0x44, 0x08, 0x8e, 0x41, 0x03, 0xc4, 0x32, 0x22, 0x00, 0x80, 0x94,
	0x88, 0x40, 0x50, 0x00, 0xd9, 0x9b, 0x20, 0x42, 0x04, 0x44, 0x90, 0x22, 0x05, 0x5c, 0x43, 0xc4,
	0x14, 0xa1, 0xab, 0xae, 0x60, 0x88, 0x83, 0x60, 0x03, 0x02, 0x02, 0x10, 0xd9, 0x4a, 0x06, 0xb7,
	0x20, 0x05, 0x68, 0x1c, 0xa0, 0x0e, 0x55, 0x38, 0x38, 0xe0, 0x49, 0x86, 0x10, 0x41, 0x92, 0x20,
	0x21, 0x12, 0x83, 0x40, 0x62, 0xa9, 0x38, 0x25, 0x10, 0xcc, 0x40, 0x54, 0xc0, 0x20, 0x0c, 0x84,
	0x4a, 0x0c, 0xe3, 0x30, 0x26, 0x55, 0xc6, 0x01, 0x22, 0x44, 0x09, 0x82, 0xa8, 0x54, 0x25, 0x0a,
	0x31, 0x62, 0x1e, 0xd8, 0x84, 0x01, 0x6e, 0x82, 0x40, 0x2d, 0x19, 0x00, 0xa0, 0x54, 0x10, 0x11,
	0xa0, 0xcc, 0x02, 0x6f, 0x24, 0xf4, 0x02, 0x09, 0x09, 0x59, 0xc5, 0x40, 0x00, 0x30, 0xac, 0x45,
	0x08, 0xa1, 0x56, 0x00, 0x52, 0x02, 0x81, 0xc4, 0x24, 0x40, 0x01, 0xa9, 0x32, 0x8c, 0x80, 0x6a,
	0x00, 0x81, 0xb1, 0x92, 0x90, 0x15, 0xca, 0x44, 0x63, 0x41, 0x32, 0x00, 0x48, 0x81, 0x1c, 0xa2,
	0x08, 0x81, 0x34, 0x1a, 0xcb, 0x28, 0x74, 0x00, 0xa1, 0x0c, 0x90, 0x4c, 0x24, 0xc2, 0x31, 0x01,
	0xc8, 0xdb, 0x04, 0x30, 0x14, 0xb1, 0x08, 0x04, 0x05, 0xc4, 0x0a, 0xd0, 0x20, 0x2e, 0x21, 0x0e,
	0x41, 0x20, 0x00, 0x50, 0x89, 0x0c, 0x89, 0x20, 0x20, 0x40, 0x12, 0x05, 0x44, 0x93, 0x66, 0x00,
	0xc0, 0x80, 0x08, 0xcc, 0x5b, 0x20, 0x08, 0x44, 0x12, 0x82, 0xb5, 0x13, 0x61, 0x12, 0x84, 0x41,
	0x04, 0x49, 0x00, 0x09, 0x64, 0x82, 0x03, 0x22, 0xf8, 0x04, 0xa0, 0x2e, 0x83, 0x60, 0x31, 0x15,
	0x52, 0x08, 0x08, 0xa5, 0x63, 0x1c, 0x80, 0x06, 0x09, 0x18, 0xa6, 0x49, 0x8c, 0x21, 0x8b, 0xc0,
	0x02, 0x16, 0x50, 0x14, 0xa0, 0x11, 0x09, 0x1c, 0x82, 0x80, 0x03, 0x90, 0x80, 0xc3, 0x00, 0x07,
	0x33, 0x80, 0x14, 0xc0, 0x0b, 0x48, 0x41, 0x08, 0x8e, 0x10, 0xc8, 0x62, 0x24, 0x07, 0xd0, 0x16,
	0x01, 0x42, 0x08, 0x18, 0x75, 0x03, 0x25, 0xe1, 0x01, 0xc3, 0x04, 0x86, 0x20, 0x92, 0x54, 0x01,
	0x80, 0x44, 0x42, 0xd8, 0x0a, 0x10, 0x80, 0x6c, 0x28, 0x23, 0x41, 0x22, 0x50, 0x08, 0x00, 0x38,
	0x37, 0xc0, 0x00, 0x28, 0x1e, 0x0c, 0x3a, 0x00, 0x19, 0x03, 0x30, 0x11, 0x00, 0x54, 0x04, 0xc2,
	0x19, 0xcc, 0x14, 0x01, 0x04, 0xf0, 0x63, 0x8d, 0x2c, 0x0c, 0x69, 0x44, 0x56, 0x02, 0x21, 0x80,
	0x12, 0x4c, 0x2c, 0x71, 0x08, 0x38, 0x2c, 0x52, 0xc2, 0x20, 0x80, 0x51, 0x90, 0x80, 0x0f, 0xa6,
	0x28, 0x14, 0x00, 0x83, 0x70, 0x4e, 0x00, 0x26, 0x24, 0x8a, 0x16, 0x4d, 0x41, 0xa3, 0x00, 0x00,
	0x03, 0x12, 0x68, 0xc7, 0xca, 0x30, 0xa4, 0x20, 0x97, 0x41, 0x0a, 0x80, 0x60, 0xe0, 0x22, 0x8d,
	0xa4, 0x5f, 0x04, 0x60, 0x02, 0x9a, 0x82, 0x89, 0xc0, 0x6a, 0x1e, 0x20, 0xa0, 0x34, 0x80, 0x43,
	0x44, 0x02, 0x43, 0xc2, 0x20, 0x75, 0x18, 0x05, 0x40, 0x66, 0x10, 0x35, 0x50, 0x02, 0x81, 0x40,
	0x52, 0x18, 0x28, 0x0c, 0x92, 0x42, 0x2a, 0x02, 0x80, 0x29, 0x08, 0x96, 0x04, 0x08, 0x20, 0x99,
	0x30, 0x91, 0x15, 0x09, 0x36, 0xa6, 0xe0, 0x18, 0x09, 0x14, 0x0f, 0x60, 0x81, 0x40, 0x21, 0x14,
	0xcc, 0x02, 0x22, 0x13, 0x08, 0x1c, 0x51, 0x92, 0xc0, 0x08, 0x62, 0x69, 0x8c, 0x04, 0x07, 0x61,
	0x48, 0x44, 0xa8, 0x88, 0xc1, 0x03, 0x01, 0x56, 0x00, 0x99, 0x0c, 0x75, 0x02, 0xa8, 0x4a, 0x31,
	0xd0, 0x21, 0x99, 0x10, 0x07, 0x44, 0x25, 0x42, 0x33, 0x40, 0x80, 0xa4, 0x22, 0x02, 0xba, 0x00,
	0x5c, 0x4c, 0x81, 0x24, 0xc3, 0xc0, 0x09, 0x20, 0x0a, 0xae, 0x06, 0x44, 0x41, 0x95, 0x0d, 0xdc,
	0x02, 0x26, 0x02, 0x42, 0x0c, 0xf0, 0x19, 0x43, 0x00, 0x10, 0x18, 0x12, 0xc4, 0xc5, 0x61, 0x22,
	0xb5, 0x69, 0x32, 0x10, 0x98, 0x44, 0x52, 0x40, 0x08, 0xb0, 0x24, 0x81, 0x44, 0x74, 0x45, 0xd0,
	0x01, 0x89, 0x4b, 0x41, 0x2c, 0x36, 0x20, 0x20, 0x04, 0x96, 0x26, 0x18, 0x72, 0xa2, 0x08, 0x45,
	0x82, 0xa0, 0x40, 0x63, 0x03, 0x26, 0xd1, 0x89, 0xaa, 0x24, 0x14, 0x92, 0x15, 0x20, 0xc0, 0x27,
	0x70, 0x52, 0x00, 0xb4, 0x21, 0x40, 0x86, 0x16, 0x50, 0x89, 0x91, 0x59, 0x0c, 0x2b, 0x6c, 0x20,
	0xe0, 0x30, 0x70, 0x4c, 0xa2, 0x00, 0x32, 0x11, 0x21, 0x54, 0x40, 0x29, 0x0c, 0x84, 0x11, 0xb2,
	0xd0, 0x08, 0x84, 0x02, 0x01, 0x69, 0x15, 0x20, 0x99, 0x09, 0x00, 0x12, 0x53, 0x18, 0x21, 0x40,
	0x28, 0x6a, 0x94, 0x02, 0x20, 0xed, 0x85, 0x46, 0x4a, 0xc2, 0x30, 0x80, 0x00, 0x8a, 0x43, 0x34,
	0x11, 0x21, 0x30, 0x49, 0x83, 0x20, 0x6e, 0x06, 0x20, 0x05, 0x30, 0x41, 0x65, 0x30, 0xd3, 0x98,
	0x81, 0x80, 0x41, 0x88, 0x3a, 0x05, 0x80, 0xa2, 0x01, 0xd0, 0x62, 0x60, 0x00, 0x69, 0xa2, 0xcc,
	0x51, 0x8e, 0x20, 0x80, 0x10, 0x92, 0x0d, 0x02, 0xa0, 0x14, 0x85, 0x21, 0x29, 0xf0, 0x10, 0x08,
	0x5a, 0x41, 0x23, 0x00, 0x14, 0xc8, 0x41, 0x00, 0xc0, 0xa1, 0x0f, 0x28, 0x0c, 0x0e, 0x30, 0x90,
	0x90, 0x35, 0x08, 0x07, 0x80, 0x16, 0x80, 0x82, 0x8e, 0x10, 0x80, 0x60, 0x58, 0x14, 0x10, 0x04,
	0x41, 0x8a, 0xc6, 0x44, 0x85, 0xa0, 0x8b, 0x04, 0xc0, 0xa1, 0x04, 0x01, 0x8b, 0xb6, 0x61, 0x54,
	0x2a, 0x10, 0x70, 0x30, 0x16, 0x89, 0x12, 0x48, 0x4a, 0xa5, 0x81, 0x82, 0x61, 0x01, 0x65, 0x70,
	0xa7, 0x43, 0x89, 0x09, 0x40, 0x48, 0x34, 0xb1, 0x21, 0x04, 0xb4, 0xc8, 0x80, 0x00, 0x85, 0x6a,
	0x12, 0x18, 0x0e, 0x04, 0x12, 0x46, 0x50, 0x9d, 0x28, 0x8e, 0x0e, 0x0a, 0x20, 0xd1, 0x05, 0x61,
	0x03, 0x2a, 0x08, 0x50, 0x98, 0x29, 0xfc, 0x84, 0x88, 0x00, 0x03, 0x80, 0x08, 0x8c, 0x00, 0x00,
	0x26, 0x22, 0x0a, 0xb0, 0x1c, 0x99, 0x43, 0x70, 0x54, 0x03, 0x06, 0x28, 0x00, 0x04, 0x78, 0x41,
	0x88, 0x07, 0xc8, 0x57, 0x80, 0x24, 0x00, 0x0a, 0x88, 0xc8, 0x01, 0x46, 0x22, 0x36, 0x1b, 0x00,
	0xb4, 0x49, 0xe8, 0x2c, 0x23, 0x38, 0x8d, 0xa0, 0x10, 0x8b, 0x10, 0x31, 0x82, 0xb0, 0xb8, 0x8c,
	0x25, 0x50, 0x44, 0x62, 0x10, 0x50, 0x1d, 0xc8, 0x34, 0x00, 0x03, 0x08, 0x28, 0x54, 0x09, 0x4e,
	0xc2, 0x11, 0xa5, 0x00, 0x04, 0x2c, 0x08, 0xa0, 0x81, 0x39, 0x11, 0x82, 0x04, 0x08, 0x36, 0x88,
	0x32, 0x40, 0x87, 0x05, 0x04, 0x81, 0x10, 0x28, 0x2c, 0x45, 0x08, 0x12, 0x35, 0x48, 0x32, 0xb9,
	0x46, 0x02, 0x50, 0x21, 0x01, 0x8c, 0x88, 0x08, 0x03, 0x56, 0xb2, 0x50, 0x1b, 0xd4, 0x00, 0xa9,
	0x0c, 0x00, 0x58, 0x24, 0x00, 0x97, 0xc2, 0x30, 0x56, 0x03, 0x0a, 0xac, 0x10, 0x60, 0x72, 0x03,
	0x31, 0x8c, 0x91, 0xc0, 0x60, 0x2e, 0x20, 0x80, 0x00, 0xd0, 0x10, 0x46, 0x4c, 0xe1, 0x8a, 0x85,
	0x10, 0x0e, 0x8c, 0x38, 0x92, 0x12, 0x08, 0x40, 0x42, 0x46, 0x08, 0x21, 0x10, 0x2b, 0x09, 0x1a,
	0xa5, 0x50, 0x94, 0xb8, 0x30, 0x24, 0x08, 0x06, 0x20, 0x65, 0x7a, 0xb0, 0x68, 0x00, 0x09, 0x1c,
	0x81, 0x88, 0x06, 0x21, 0x0c, 0xc4, 0x32, 0x51, 0x52, 0xb0, 0x81, 0x88, 0x84, 0x18, 0x90, 0x61,
	0x9d, 0x01, 0x49, 0xc2, 0x02, 0x00, 0xc0, 0x14, 0x14, 0x9f, 0x60, 0x00, 0x74, 0x00, 0x0b, 0x01,
	0x90, 0x84, 0x28, 0x22, 0x09, 0x10, 0x41, 0x43, 0x44, 0x06, 0x70, 0xe0, 0x25, 0x04, 0x1e, 0x24,
	0x04, 0x92, 0x50, 0x88, 0x99, 0x40, 0x27, 0x42, 0x82, 0xaa, 0x83, 0x5c, 0xc2, 0x40, 0x24, 0x40,
	0x11, 0x2a, 0xc5, 0x11, 0xa2, 0x4e, 0x04, 0x10, 0x01, 0xc4, 0x84, 0x88, 0x02, 0xc5, 0x22, 0x28,
	0x50, 0x10, 0xab, 0x00, 0x11, 0x81, 0x82, 0x8c, 0x88, 0x4a, 0x3c, 0x02, 0x50, 0x0e, 0x34, 0x01,
	0x88, 0x50, 0x15, 0x29, 0x16, 0x2c, 0x5b, 0x00, 0x08, 0x91, 0x03, 0x04, 0x48, 0x8e, 0x64, 0x16,
	0x26, 0x5a, 0x21, 0x0d, 0x50, 0x81, 0x44, 0x21, 0x08, 0x03, 0x0c, 0x94, 0xe0, 0x18, 0x90, 0x22,
	0x92, 0x84, 0x40, 0xc9, 0x64, 0xa0, 0x18, 0x04, 0x70, 0x49, 0x82, 0x18, 0x02, 0x19, 0x80, 0x80,
	0x85, 0x20, 0x6c, 0x41, 0x0b, 0x21, 0x9d, 0x4e, 0x00, 0x12, 0x40, 0x09, 0x8a, 0x10, 0x08, 0x64,
	0x74, 0x44, 0xd3, 0x08, 0x69, 0xc6, 0x08, 0x02, 0x76, 0x68, 0x38, 0x50, 0x91, 0x85, 0x00, 0xd1,
	0x42, 0xa5, 0x20, 0x15, 0x04, 0x08, 0x20, 0x79, 0x00, 0x71, 0x45, 0xe2, 0x34, 0x10, 0x11, 0x18,
	0x30, 0x94, 0x0e, 0x40, 0x30, 0x2a, 0x25, 0x15, 0x04, 0x25, 0x44, 0x10, 0xc2, 0x0f, 0x9d, 0x80,
	0x62, 0x20, 0x52, 0x00, 0x00, 0x20, 0xc6, 0x47, 0x5a, 0x14, 0xb9, 0x9a, 0x8c, 0x83, 0x28, 0x4e,
	0x21, 0x19, 0x02, 0x44, 0xd8, 0x81, 0x00, 0xa0, 0x18, 0x12, 0x51, 0x19, 0x09, 0x20, 0x13, 0x88,
	0x10, 0x20, 0xc0, 0x40, 0x20, 0x06, 0x19, 0x33, 0x45, 0x02, 0x89, 0x52, 0x20, 0x42, 0x05, 0x88,
	0x40, 0x08, 0x10, 0x04, 0x92, 0x10, 0x28, 0x19, 0xe3, 0x2c, 0x10, 0x82, 0x25, 0x20, 0x97, 0x41,
	0x10, 0x02, 0x88, 0x00, 0x11, 0x96, 0xce, 0x40, 0x37, 0x80, 0x08, 0x05, 0x02, 0x43, 0x02, 0x31,
	0x20, 0x9b, 0x20, 0x0e, 0x06, 0x20, 0x84, 0x78, 0x85, 0xc8, 0x06, 0x8a, 0x08, 0x10, 0x72, 0x25,
	0xa0, 0x90, 0xea, 0x3e, 0x82, 0x20, 0x12, 0x71, 0x18, 0x40, 0x04, 0x24, 0x89, 0x0a, 0x60, 0x4b,
	0xcd, 0x12, 0x00, 0x40, 0x98, 0x11, 0x15, 0x41, 0x22, 0x52, 0x2a, 0x23, 0x14, 0x81, 0x28, 0x1c,
	0x40, 0x23, 0x89, 0x00, 0x11, 0x6e, 0x48, 0x00, 0xb2, 0x1d, 0x14, 0x44, 0x80, 0x56, 0xc1, 0x11,
	0x8c, 0x20, 0x9e, 0xac, 0x40, 0x81, 0x31, 0x11, 0x14, 0x8a, 0xc0, 0x02, 0xd0, 0x28, 0x03, 0x08,
	0x06, 0x64, 0x44, 0xc3, 0xa1, 0x80, 0x20, 0x84, 0x80, 0x10, 0x45, 0x03, 0x10, 0xc9, 0xc2, 0x20,
	0x4a, 0x83, 0x78, 0x1a, 0xc0, 0x0d, 0x49, 0x00, 0x24, 0x90, 0x99, 0xe1, 0x05, 0xa6, 0x14, 0x82,
	0x10, 0x80, 0x91, 0xd9, 0x01, 0x00, 0x47, 0x68, 0x09, 0x10, 0x86, 0x08, 0x08, 0x14, 0x40, 0x9f,
	0x70, 0xd5, 0x6b, 0x0c, 0x14, 0x4a, 0x01, 0xd0, 0x40, 0x60, 0x02, 0x40, 0x10, 0x22, 0xa5, 0x08,
	0x4d, 0x20, 0xe0, 0x00, 0x97, 0x41, 0x4a, 0x8a, 0x06, 0xf1, 0x10, 0x35, 0x14, 0x82, 0x20, 0x68,
	0x00, 0x09, 0x34, 0x60, 0x01, 0x86, 0x00, 0x20, 0x40, 0x04, 0xcd, 0x02, 0x43, 0x06, 0x87, 0x03,
	0x9a, 0xa8, 0x14, 0xae, 0x62, 0x44, 0x0b, 0x88, 0x34, 0x42, 0x42, 0x20, 0x42, 0x60, 0x81, 0x14,
	0xc3, 0x83, 0x10, 0x53, 0x80, 0x11, 0x28, 0x95, 0x64, 0x3c, 0xe6, 0x8b, 0x00, 0x81, 0xd8, 0x81,
	0x14, 0xa2, 0x33, 0x20, 0x85, 0x44, 0x40, 0x00, 0x21, 0x1a, 0x09, 0x68, 0x0c, 0x40, 0x40, 0xf5,
	0x81, 0x80, 0x0d, 0x5b, 0x26, 0x2a, 0x03, 0x32, 0x22, 0x21, 0x00, 0x05, 0x0a, 0x80, 0xe0, 0x28,
	0x8c, 0x86, 0x24, 0x04, 0x01, 0x92, 0x91, 0x2c, 0x44, 0x81, 0x40, 0x26, 0x60, 0xb4, 0x7c, 0x15,
	0x49, 0x50, 0x02, 0x80, 0x08, 0x85, 0x86, 0xab, 0x10, 0x42, 0x40, 0x82, 0x94, 0x5d, 0x0f, 0x02,
	0x10, 0x6a, 0x19, 0x90, 0x0a, 0x05, 0x20, 0x24, 0xaa, 0x00, 0xd1, 0x04, 0x28, 0x74, 0x22, 0x80,
	0x11, 0x60, 0x81, 0xa9, 0x44, 0x00, 0x9b, 0x06, 0x04, 0x00, 0x49, 0x54, 0xe2, 0xf3, 0x09, 0x00,
	0xcb, 0xa2, 0x40, 0xa2, 0x08, 0x10, 0x18, 0x90, 0x22, 0x28, 0x50, 0xa9, 0x27, 0x04, 0xd8, 0xae,
	0x18, 0x41, 0xe0, 0x00, 0x70, 0x06, 0x82, 0x20, 0xc1, 0x90, 0x02, 0x09, 0x00, 0x06, 0x06, 0xc4,
	0xdb, 0x00, 0x08, 0x81, 0x0c, 0x0a, 0x26, 0x50, 0x82, 0xa4, 0x17, 0xc6, 0x28, 0x40, 0x21, 0x27,
	0x00, 0x43, 0x2d, 0x44, 0xd7, 0x31, 0x02, 0x09, 0x83, 0x00, 0x2a, 0xb0, 0x70, 0x05, 0x9c, 0x41,
	0x47, 0x12, 0xa2, 0x50, 0x22, 0x44, 0x90, 0x00, 0x48, 0x03, 0x11, 0x8e, 0xc9, 0x50, 0x21, 0x66,
	0x10, 0xa8, 0x81, 0x04, 0x11, 0xc1, 0x62, 0x91, 0x49, 0x00, 0x44, 0x91, 0x22, 0x0e, 0x24, 0x8a,
	0x36, 0xe1, 0x0d, 0x61, 0x6c, 0x44, 0x28, 0xa0, 0x94, 0x18, 0x04, 0x04, 0x81, 0x19, 0x01, 0x38,
	0x47, 0x42, 0x30, 0xa4, 0x41, 0x04, 0x05, 0x9a, 0x84, 0x50, 0x32, 0x29, 0x0c, 0x10, 0x08, 0x08,
	0x66, 0x21, 0x20, 0x85, 0xc8, 0x8c, 0x04, 0x06, 0x12, 0x40, 0x35, 0x19, 0x96, 0x04, 0x12, 0x52,
	0xd2, 0x08, 0xb0, 0x14, 0xa7, 0x40, 0x00, 0x53, 0x1f, 0x08, 0x03, 0xe1, 0x28, 0x81, 0x00, 0x3e,
	0x01, 0xdb, 0x42, 0x5c, 0x05, 0x61, 0xad, 0x00, 0xc0, 0x0c, 0x14, 0x20, 0x18, 0x93, 0x0c, 0x52,
	0x89, 0x24, 0xe0, 0x82, 0x0e, 0x04, 0x40, 0x6d, 0x32, 0xb6, 0x4a, 0xaa, 0x91, 0x00, 0x41, 0x04,
	0x00, 0x01, 0x12, 0x65, 0x8c, 0x00, 0x12, 0x51, 0x88, 0xbb, 0xa4, 0x0c, 0x21, 0x48, 0xa1, 0x10,
	0x0e, 0x48, 0x8a, 0x24, 0x54, 0x02, 0x61, 0x38, 0x31, 0x00, 0x88, 0x00, 0x42, 0xc9, 0x0e, 0x0c,
	0x82, 0x40, 0x0c, 0x22, 0x51, 0x04, 0x90, 0x00, 0x6c, 0x22, 0x24, 0x88, 0x9c, 0x04, 0x04, 0x42,
	0x40, 0x01, 0x23, 0x01, 0x88, 0x05, 0x20, 0x0a, 0x70, 0xa2, 0xa6, 0x00, 0x11, 0x68, 0x38, 0x24,
	0xa0, 0x30, 0xa1, 0x41, 0x4d, 0x20, 0xf0, 0x90, 0x00, 0x15, 0x40, 0xc0, 0x02, 0xe4, 0x59, 0x2c,
	0xe0, 0xd7, 0x08, 0x3a, 0x90, 0x8b, 0xaa, 0x20, 0x08, 0x08, 0x00, 0xd2, 0x02, 0x04, 0xe1, 0xc0,
	0x00, 0x38, 0x04, 0xe9, 0x08, 0xb8, 0x47, 0x86, 0x40, 0x00, 0x32, 0x23, 0x99, 0x00, 0x05, 0x0c,
	0x57, 0x10, 0x30, 0x55, 0x59, 0x8a, 0x48, 0x84, 0x90, 0x33, 0x3c, 0x04, 0xc1, 0x6a, 0x12, 0xc1,
	0x00, 0x85, 0x5a, 0x06, 0x30, 0x44, 0x98, 0x34, 0x19, 0x18, 0x22, 0x32, 0x24, 0x70, 0x24, 0xa0,
	0x0c, 0x23, 0x44, 0x51, 0x00, 0x29, 0x51, 0x43, 0x00, 0x10, 0x21, 0x10, 0x21, 0x00, 0x14, 0x42,
	0x62, 0x84, 0x01, 0x00, 0x38, 0x1b, 0x68, 0x10, 0x73, 0x52, 0x02, 0x00, 0x11, 0x21, 0x06, 0x90,
	0x01, 0x05, 0x60, 0x04, 0x62, 0x00, 0x00, 0x80, 0xa0, 0x41, 0x1e, 0xcc, 0x00, 0x03, 0xf0, 0xb8,
	0x4c, 0x08, 0x20, 0x0c, 0x01, 0x20, 0x08, 0x7c, 0x06, 0x24, 0x40, 0x93, 0x4b, 0x80, 0x45, 0xc7,
	0x84, 0x46, 0x25, 0x11, 0x00, 0xd0, 0x89, 0x02, 0x18, 0xc2, 0xf0, 0x02, 0x08, 0x1c, 0xac, 0x42,
	0xd4, 0x48, 0x10, 0xac, 0xc4, 0x02, 0x1a, 0x11, 0x49, 0x19, 0x00, 0xcd, 0xc0, 0x5a, 0x47, 0xa2,
	0xa9, 0x08, 0x00, 0x01, 0x24, 0x21, 0x60, 0x10, 0x40, 0x52, 0xec, 0x00, 0x04, 0x00, 0x19, 0x50,
	0x11, 0x8d, 0x1a, 0x30, 0x9a, 0x03, 0x80, 0x80, 0xc7, 0x20, 0x03, 0x90, 0x03, 0x98, 0x88, 0x2f,
	0x0e, 0x34, 0x08, 0x15, 0x68, 0xd5, 0x26, 0x24, 0x80, 0x50, 0x81, 0x04, 0x57, 0xa0, 0x60, 0x01,
	0x31, 0x15, 0xb4, 0x92, 0x00, 0x06, 0xe0, 0x40, 0x30, 0x01, 0x59, 0x48, 0x04, 0x86, 0x49, 0x99,
	0xc0, 0x08, 0x4b, 0x26, 0x81, 0x48, 0x0e, 0x44, 0x14, 0x43, 0x22, 0x26, 0x82, 0x2e, 0x00, 0x81,
	0x40, 0x68, 0xa1, 0x51, 0x84, 0x58, 0x86, 0xc0, 0x20, 0xa2, 0x92, 0x28, 0x44, 0x84, 0x03, 0x5c,
	0x50, 0x90, 0x02, 0x6c, 0x04, 0xaf, 0x70, 0x16, 0x32, 0x06, 0x91, 0x81, 0xa8, 0x00, 0xb0, 0x28,
	0x23, 0x30, 0x01, 0x40, 0x60, 0x81, 0x08, 0xa9, 0x41, 0x10, 0x0a, 0x2a, 0x74, 0x29, 0x97, 0xa0,
	0x52, 0x8a, 0x1c, 0x40, 0xa2, 0x38, 0x9c, 0x10, 0x07, 0x02, 0xc0, 0x02, 0x01, 0x50, 0x4e, 0x0b,
	0x00, 0x06, 0x21, 0xad, 0x24, 0x53, 0x44, 0x4a, 0x96, 0x8b, 0x20, 0x10, 0x9c, 0x40, 0x24, 0xa3,
	0x89, 0x19, 0x70, 0x01, 0x22, 0x14, 0x10, 0x98, 0x00, 0x21, 0x95, 0x09, 0x4a, 0x91, 0x49, 0x22,
	0xc5, 0x04, 0x27, 0x60, 0x65, 0xe2, 0x20, 0x80, 0x02, 0x00, 0x24, 0x22, 0xaa, 0x96, 0x64, 0x84,
	0x02, 0x4e, 0xc0, 0x0a, 0x18, 0xe0, 0x80, 0x04, 0x58, 0x04, 0x51, 0x25, 0x81, 0x98, 0x40, 0x0e,
	0x20, 0x90, 0x0a, 0xc8, 0x0d, 0x08, 0x42, 0x21, 0x81, 0x12, 0x00, 0xca, 0x00, 0x36, 0x82, 0x58,
	0x20, 0x15, 0x01, 0xca, 0x48, 0x21, 0x41, 0x01, 0xb0, 0x00, 0x20, 0x06, 0x80, 0x18, 0x04, 0x00,
	0x92, 0x09, 0x28, 0x24, 0x01, 0x0c, 0x74, 0x05, 0x42, 0x18, 0x90, 0x61, 0x2b, 0x09, 0x16, 0x45,
	0x44, 0xa2, 0xc0, 0x1a, 0x14, 0x01, 0x00, 0x00, 0x40, 0x22, 0x9d, 0x45, 0x4b, 0x84, 0x12, 0x05,
	0xea, 0x04, 0xcc, 0x0a, 0x08, 0x20, 0x40, 0x09, 0x02, 0x6c, 0x83, 0x80, 0x00, 0x00, 0x30, 0x90,
	0x99, 0x00, 0x4d, 0x30, 0x41, 0x9a, 0x03, 0x65, 0xc5, 0x29, 0x38, 0x10, 0x50, 0x24, 0x61, 0x80,
	0x44, 0x02, 0xd2, 0xc0, 0x80, 0x20, 0x02, 0x20, 0x02, 0x54, 0x63, 0x10, 0xb0, 0x15, 0x85, 0x48,
	0xa2, 0x03, 0x10, 0x95, 0xd0, 0x00, 0x08, 0xc1, 0xe3, 0x0c, 0x40, 0x16, 0x66, 0x40, 0x45, 0x11,
	0x89, 0x00, 0x00, 0x8e, 0x42, 0x93, 0xb0, 0x28, 0x69, 0xd0, 0xe2, 0x22, 0x07, 0x11, 0x0c, 0x2c,
	0x98, 0x80, 0x48, 0x04, 0x10, 0x14, 0xdd, 0x8e, 0x01, 0x08, 0x07, 0xe2, 0x8e, 0x08, 0x00, 0x6a,
	0x30, 0x11, 0x40, 0x03, 0x40, 0x85, 0x26, 0x08, 0x06, 0x18, 0x0a, 0x40, 0x82, 0x01, 0x28, 0x50,
	0x29, 0xa3, 0xa0, 0xc3, 0x68, 0x54, 0xa2, 0x98, 0x88, 0x20, 0x00, 0x2e, 0x46, 0x26, 0xa0, 0x00,
	0x1c, 0x41, 0x04, 0x14, 0x02, 0x42, 0x80, 0x89, 0x1e, 0xa9, 0x44, 0xc5, 0x92, 0xb0, 0x80, 0xca,
	0xa0, 0x08, 0x76, 0xb0, 0xa8, 0x40, 0xcf, 0x83, 0x2a, 0xc0, 0x58, 0x3e, 0x0c, 0x04, 0x84, 0x0e,
	0x51, 0xc0, 0x13, 0x81, 0x59, 0xe8, 0x40, 0x42, 0x19, 0x0a, 0x38, 0x40, 0xc6, 0x22, 0x40, 0x11,
	0x80, 0x41, 0x01, 0x66, 0x2e, 0xa1, 0x08, 0x31, 0x50, 0x0a, 0x89, 0x00, 0x07, 0x40, 0x13, 0x00,
	0x41, 0x28, 0x02, 0xc0, 0x0b, 0x85, 0x11, 0xd8, 0x28, 0x2e, 0x82, 0xa2, 0x24, 0xb0, 0x50, 0x67,
	0x56, 0x80, 0x03, 0x8f, 0x28, 0x00, 0x05, 0x24, 0x81, 0xc0, 0x12, 0x4c, 0x82, 0x04, 0x0a, 0x16,
	0xb0, 0x8a, 0x51, 0x00, 0x80, 0x14, 0x41, 0x53, 0x0e, 0x29, 0x4a, 0x08, 0x14, 0x14, 0x22, 0xa5,
	0x24, 0x46, 0x41, 0x02, 0x01, 0x40, 0x84, 0x64, 0x04, 0xc4, 0x04, 0x92, 0x99, 0x08, 0xa8, 0x89,
	0x2a, 0x10, 0x40, 0x21, 0x14, 0xa0, 0x18, 0xc8, 0x48, 0xa4, 0x49, 0xa1, 0x00, 0x47, 0x41, 0x28,
	0x86, 0x88, 0x0e, 0x80, 0x48, 0xa1, 0x64, 0x12, 0x72, 0x28, 0x40, 0x53, 0x48, 0x52, 0xb2, 0x02,
	0x00, 0xd8, 0x90, 0x48, 0x20, 0x36, 0xc2, 0x25, 0x80, 0x09, 0x25, 0x2c, 0x12, 0x88, 0x98, 0x69,
	0x1c, 0x04, 0x12, 0x14, 0xe2, 0x90, 0x68, 0x06, 0x88, 0x38, 0x00, 0x23, 0x01, 0x90, 0x15, 0xa0,
	0x1e, 0x04, 0x31, 0xa6, 0x71, 0x40, 0x0a, 0x50, 0xd4, 0x41, 0x08, 0x08, 0x0a, 0x04, 0x2c, 0x15,
	0x00, 0x8a, 0x40, 0x10, 0x8a, 0x70, 0xb0, 0x81, 0x22, 0x14, 0x0b, 0x25, 0x12, 0x15, 0x0a, 0x00,
	0x30, 0x81, 0xcc, 0x08, 0x30, 0x61, 0x18, 0xdc, 0xc4, 0x60, 0x14, 0x83, 0x78, 0xa1, 0x00, 0x82,
	0x83, 0x08, 0x52, 0xb0, 0x06, 0x0c, 0x02, 0x29, 0x00, 0x14, 0x2a, 0x90, 0x0d, 0x11, 0x64, 0x42,
	0x02, 0x40, 0x21, 0x80, 0x45, 0x06, 0x08, 0x04, 0x4b, 0x04, 0x30, 0x50, 0x40, 0x26, 0x64, 0xe2,
	0x04, 0xc5, 0x0c, 0xc8, 0x30, 0xc6, 0x02, 0x0b, 0x18, 0x4f, 0x80, 0x28, 0x84, 0x20, 0x29, 0x85,
	0x8e, 0x0b, 0x58, 0x24, 0x80, 0x01, 0x14, 0x55, 0x04, 0x02, 0xc1, 0xb0, 0x80, 0xc0, 0x80, 0x8b,
	0x42, 0x82, 0x59, 0x34, 0x4c, 0xc0, 0xc0, 0x12, 0x53, 0x39, 0x20, 0x84, 0x0a, 0xc5, 0x18, 0x91,
	0x20, 0xa3, 0x50, 0x48, 0x43, 0x4a, 0x10, 0x3a, 0xb2, 0x11, 0x89, 0x42, 0x18, 0x10, 0xe0, 0x93,
	0x40, 0x02, 0xa6, 0x64, 0x04, 0x08, 0x87, 0x18, 0xc3, 0x00, 0x16, 0x07, 0x20, 0x2a, 0x04, 0x09,
	0x44, 0x22, 0xa2, 0x83, 0xb0, 0x29, 0xc1, 0x40, 0x20, 0xa3, 0x01, 0x07, 0x81, 0x5c, 0x42, 0x00,
	0x45, 0x21, 0x83, 0xac, 0x13, 0x25, 0x0e, 0x80, 0x80, 0x01, 0x49, 0x14, 0x8b, 0x10, 0x16, 0x62,
	0x10, 0x14, 0x82, 0x07, 0x48, 0xb0, 0xe9, 0x09, 0x80, 0x9a, 0x88, 0x70, 0xe0, 0xc0, 0x0d, 0x8d,
	0x03, 0x84, 0x04, 0xd1, 0x20, 0x82, 0x25, 0x07, 0x62, 0x68, 0x11, 0x10, 0x20, 0x20, 0x01, 0x06,
	0x14, 0x40, 0x72, 0x12, 0x11, 0xd4, 0x20, 0x10, 0x65, 0x72, 0x10, 0xc8, 0x04, 0x8a, 0x04, 0x04,
	0x0a, 0x98, 0x08, 0xc8, 0x20, 0x04, 0x12, 0x48, 0x85, 0x70, 0x10, 0xc3, 0x08, 0x25, 0x80, 0x18,
	0x00, 0x1c, 0x40, 0x1e, 0x20, 0x00, 0x13, 0xa1, 0x43, 0xe0, 0x1e, 0x04, 0xc3, 0x18, 0xc4, 0x00,
	0x69, 0x44, 0x00, 0x20, 0xaf, 0xa0, 0x04, 0x0a, 0x00, 0x01, 0x43, 0x3c, 0x11, 0x10, 0x66, 0x48,
	0x82, 0x20, 0x0f, 0x1c, 0x0c, 0x44, 0x38, 0x13, 0xa8, 0x14, 0x24, 0x8f, 0x80, 0x06, 0x60, 0x72,
	0x00, 0x90, 0x84, 0x01, 0x12, 0x61, 0x50, 0x2e, 0xbc, 0x04, 0x24, 0x64, 0x90, 0x48, 0x28, 0x49,
	0x83, 0xa0, 0x10, 0x54, 0x19, 0x1b, 0xa5, 0x01, 0x08, 0x52, 0xf6, 0x00, 0x07, 0xac, 0x09, 0x40,
	0x24, 0x42, 0xd8, 0x8a, 0xd1, 0x99, 0x88, 0x6e, 0x90, 0x08, 0x04, 0x00, 0x50, 0x0c, 0x18, 0x73,
	0x09, 0x85, 0x24, 0x84, 0x6a, 0x00, 0xc0, 0x83, 0xa1, 0x85, 0x5a, 0x40, 0x42, 0x64, 0x82, 0x30,
	0x31, 0x48, 0x05, 0x14, 0xd1, 0x7a, 0x00, 0x0d, 0x10, 0x20, 0x42, 0x20, 0xc0, 0x26, 0x84, 0x0c,
	0xc3, 0x1c, 0x20, 0x51, 0x10, 0xa9, 0x19, 0x21, 0x14, 0x22, 0x41, 0xa8, 0x90, 0x0d, 0xe9, 0x10,
	0x64, 0x29, 0xbd, 0x31, 0xc4, 0x46, 0x28, 0x40, 0x0a, 0x9a, 0x88, 0xc4, 0x20, 0x28, 0x21, 0xc2,
	0x80, 0x40, 0x86, 0x03, 0x38, 0x96, 0x83, 0x08, 0x39, 0xd0, 0x0c, 0x1a, 0x81, 0x78, 0x2a, 0x51,
	0x06, 0xc7, 0x00, 0x13, 0x91, 0x05, 0x01, 0x59, 0x4c, 0x00, 0xc1, 0x02, 0x86, 0x15, 0x5f, 0x84,
	0x22, 0x56, 0x62, 0xa3, 0x84, 0x10, 0x00, 0x7a, 0x01, 0xa1, 0x11, 0x91, 0x92, 0xa0, 0x48, 0x20,
	0x28, 0x98, 0x10, 0x45, 0xa4, 0x04, 0x81, 0x02, 0x3a, 0x88, 0x01, 0x08, 0x24, 0xc3, 0x20, 0x34,
	0x84, 0x5c, 0x40, 0x20, 0xf6, 0x22, 0x10, 0xbc, 0x02, 0x21, 0x02, 0x44, 0x38, 0x0b, 0x98, 0x82,
	0x60, 0x2e, 0x96, 0x88, 0x05, 0x44, 0x45, 0xca, 0x0c, 0x00, 0x0a, 0x82, 0xe9, 0x00, 0x61, 0x58,
	0x00, 0x11, 0x15, 0x10, 0x13, 0x21, 0x26, 0x02, 0x08, 0x80, 0x41, 0x96, 0x04, 0x28, 0x01, 0x81,
	0x0d, 0x21, 0x99, 0x00, 0x42, 0x71, 0x82, 0xa2, 0xc0, 0x45, 0x60, 0x38, 0x40, 0xd2, 0x80, 0x80,
	0x58, 0x43, 0x06, 0xe1, 0x13, 0x22, 0xe8, 0x08, 0x00, 0x00, 0x40, 0x00, 0x1d, 0x20, 0x5b, 0x44,
	0x42, 0x12, 0x4b, 0x28, 0x10, 0x0c, 0x0c, 0x54, 0xc2, 0xc0, 0x0e, 0x10, 0xc7, 0x00, 0x70, 0xa0,
	0x60, 0x30, 0x3c, 0x82, 0x03, 0x48, 0x40, 0x12, 0x2c, 0x7c, 0x80, 0x86, 0x20, 0x02, 0x12, 0x21,
	0x49, 0x01, 0xe9, 0x68, 0x12, 0xb8, 0x28, 0x04, 0xd0, 0x82, 0x54, 0x42, 0x89, 0x9b, 0x40, 0x52,
	0x84, 0x1e, 0x21, 0xe0, 0x22, 0x85, 0x15, 0x80, 0x4c, 0x84, 0x81, 0x04, 0xc0, 0x09, 0x0a, 0x42,
	0x46, 0x81, 0x22, 0x40, 0x89, 0x26, 0x16, 0x94, 0x49, 0x11, 0x94, 0x41, 0x0f, 0x00, 0x40, 0xa0,
	0x2d, 0x28, 0x40, 0x2d, 0x18, 0x11, 0x1b, 0x19, 0x18, 0x92, 0x81, 0x26, 0x26, 0x0b, 0x00, 0x0c,
	0x91, 0x0c, 0x46, 0xe0, 0x20, 0x0c, 0x31, 0x8a, 0x08, 0x14, 0x90, 0x12, 0x3a, 0x11, 0x53, 0x25,
	0x00, 0x85, 0x22, 0x11, 0x05, 0x40, 0x80, 0x20, 0x96, 0x40, 0x83, 0x41, 0x8d, 0x28, 0x00, 0x40,
	0xe2, 0x04, 0x08, 0xc6, 0x08, 0x10, 0xa2, 0x98, 0x12, 0x29, 0x00, 0x29, 0x24, 0x54, 0x80, 0x82,
	0x25, 0x03, 0x45, 0x16, 0x00, 0x18, 0x90, 0x30, 0x11, 0x0a, 0x30, 0x84, 0x21, 0x06, 0x08, 0x0c,
	0x60, 0x22, 0x14, 0xc2, 0x82, 0x38, 0x54, 0xc0, 0x6a, 0x84, 0x11, 0x3c, 0x00, 0x10, 0x8c, 0x48,
	0x82, 0x8a, 0x8d, 0x65, 0x88, 0x40, 0x00, 0x42, 0x33, 0x25, 0x5c, 0x09, 0x01, 0x00, 0x03, 0x20,
	0x3f, 0x50, 0x0a, 0xe6, 0x04, 0x40, 0x38, 0x00, 0x0d, 0xd0, 0x8e, 0x50, 0x40, 0x61, 0x04, 0x20,
	0x4c, 0x83, 0x24, 0x26, 0x28, 0x06, 0xc0, 0x00, 0x88, 0x04, 0x72, 0x12, 0x19, 0x81, 0x84, 0x43,
	0x26, 0x10, 0x18, 0x90, 0x21, 0x93, 0x04, 0x08, 0xa3, 0x89, 0x27, 0x04, 0x84, 0x68, 0x00, 0x93,
	0x43, 0x81, 0x71, 0x84, 0x0c, 0x10, 0xb4, 0xe0, 0x21, 0xc1, 0x52, 0x2e, 0x02, 0x10, 0x00, 0x00,
	0xc1, 0x08, 0x00, 0x68, 0x80, 0xd2, 0x01, 0x00, 0x00, 0xa5, 0x0e, 0x85, 0x32, 0x8c, 0x01, 0x40,
	0x82, 0x20, 0x95, 0x8b, 0x92, 0x40, 0x1c, 0x08, 0x54, 0x30, 0x40, 0x36, 0x34, 0x09, 0x82, 0x06,
	0x84, 0xf2, 0xac, 0x84, 0x18, 0x21, 0x32, 0xd0, 0x41, 0x11, 0xb0, 0x82, 0x43, 0x12, 0x50, 0x08,
	0x0a, 0x80, 0x0b, 0x02, 0x50, 0x06, 0x60, 0xa6, 0x04, 0x51, 0x08, 0x02, 0x84, 0x1b, 0x91, 0x79,
	0x01, 0x8c, 0x44, 0x30, 0xa2, 0x00, 0x20, 0x16, 0x82, 0x48, 0x10, 0x00, 0x21, 0x09, 0x08, 0x22,
	0x08, 0x20, 0x62, 0x30, 0x99, 0x89, 0x4a, 0x26, 0x07, 0xb2, 0x0a, 0x01, 0x02, 0xc4, 0x2c, 0x03,
	0xd0, 0x22, 0xc4, 0x94, 0x80, 0x5a, 0x14, 0xc0, 0x15, 0x49, 0x86, 0x28, 0x0a, 0xa2, 0x43, 0x24,
	0x24, 0x14, 0x01, 0x16, 0x60, 0xd0, 0x2b, 0x45, 0x09, 0x04, 0x16, 0xa1, 0x48, 0x1e, 0xa4, 0x14,
	0xc0, 0x48, 0x00, 0x42, 0x8b, 0x90, 0xcd, 0x40, 0x68, 0xc2, 0x02, 0x91, 0x00, 0x4c, 0xc8, 0x24,
	0x12, 0x4b, 0x28, 0x21, 0x04, 0x48, 0x12, 0x10, 0x82, 0x87, 0x01, 0x9a, 0xa1, 0x14, 0x60, 0x41,
	0x8b, 0xd0, 0x08, 0xa8, 0x22, 0x20, 0x02, 0x8c, 0x69, 0x88, 0x06, 0x58, 0x05, 0xa3, 0x27, 0x48,
	0x15, 0xaa, 0x20, 0x40, 0x50, 0x30, 0x2c, 0x0c, 0x08, 0x6c, 0x82, 0x21, 0x8a, 0x29, 0x58, 0x0d,
	0x40, 0x91, 0xd3, 0x00, 0x05, 0x02, 0x2e, 0x24, 0x11, 0x11, 0xb5, 0xc0, 0x94, 0x08, 0x02, 0x60,
	0x20, 0x08, 0x88, 0x0f, 0x25, 0x46, 0xc4, 0x42, 0x12, 0x18, 0x41, 0x2d, 0x10, 0x04, 0x12, 0x25,
	0x20, 0xc1, 0xe0, 0x44, 0x30, 0xd0, 0x2a, 0x1c, 0x06, 0x62, 0x38, 0x73, 0x81, 0xa0, 0x4c, 0x01,
	0x08, 0x60, 0x16, 0x02, 0x95, 0x3c, 0x51, 0xa1, 0x58, 0x12, 0x71, 0x2d, 0x60, 0x98, 0x08, 0x68,
	0x00, 0x12, 0x01, 0x50, 0x54, 0x00, 0x02, 0x87, 0xa9, 0x32, 0x19, 0x00, 0x82, 0x00, 0x41, 0x02,
	0xa1, 0x00, 0x0d, 0xe0, 0x10, 0x82, 0x50, 0x88, 0x75, 0x8c, 0x8c, 0x42, 0x16, 0x48, 0x19, 0x28,
	0x00, 0x08, 0x00, 0x94, 0x08, 0x11, 0x00, 0x1b, 0x83, 0x12, 0x20, 0x08, 0x2c, 0x2c, 0xc2, 0x60,
	0x10, 0x82, 0xb8, 0x02, 0x09, 0x04, 0x04, 0x38, 0x61, 0x51, 0x36, 0x04, 0x82, 0xe0, 0x06, 0xa1,
	0x0a, 0x8c, 0x11, 0xc3, 0x42, 0x0c, 0x17, 0x42, 0x84, 0x28, 0x03, 0x86, 0x4c, 0x70, 0x3b, 0x15,
	0xf8, 0xc8, 0x40, 0x10, 0xe1, 0xc8, 0xa0, 0x14, 0x41, 0x24, 0x64, 0x92, 0x41, 0x2b, 0x31, 0x09,
	0x41, 0x0c, 0x80, 0x40, 0x80, 0x85, 0x10, 0x82, 0x22, 0x52, 0xc8, 0x12, 0x38, 0x46, 0x65, 0x16,
	0x26, 0x20, 0x98, 0x61, 0x00, 0x29, 0x04, 0x94, 0xab, 0x28, 0x09, 0x00, 0x23, 0x02, 0xf0, 0x02,
	0x84, 0xe0, 0x0a, 0x05, 0x40, 0x20, 0xf0, 0x3b, 0x8c, 0x18, 0x4f, 0x44, 0xc0, 0x08, 0x2c, 0x20,
	0x42, 0x20, 0x00, 0x01, 0x02, 0xa9, 0x41, 0x03, 0x00, 0x52, 0x20, 0x72, 0x02, 0x80, 0xc2, 0x0a,
	0x64, 0x47, 0x92, 0x04, 0x49, 0x14, 0x03, 0x74, 0x80, 0x10, 0x83, 0x8c, 0x09, 0x29, 0x0e, 0x50,
	0x29, 0x04, 0x81, 0x1f, 0x49, 0x08, 0x85, 0xc0, 0x8c, 0x00, 0xd5, 0x41, 0x32, 0x46, 0xe1, 0x82,
	0x20, 0x90, 0xa6, 0x22, 0x80, 0xa8, 0x08, 0xf5, 0x43, 0x05, 0x18, 0x22, 0x41, 0x04, 0xc0, 0x00,
	0x0e, 0x1c, 0x12, 0xa0, 0x84, 0x44, 0xd9, 0x2c, 0x2a, 0xc7, 0x20, 0x2b, 0x04, 0x1b, 0x00, 0x3e,
	0x10, 0x00, 0x14, 0xc5, 0x05, 0x84, 0x04, 0x55, 0x22, 0x04, 0x08, 0xd1, 0xa8, 0x5a, 0x41, 0x12,
	0x9e, 0xf4, 0x08, 0x04, 0x50, 0x05, 0x48, 0x90, 0x0d, 0x07, 0x40, 0x08, 0x74, 0x28, 0x80, 0x81,
	0x16, 0x08, 0x0a, 0x80, 0x08, 0x0c, 0x04, 0xc9, 0x41, 0x7a, 0x65, 0x43, 0x82, 0x21, 0x92, 0x61,
	0x04, 0x94, 0xc1, 0x08, 0x00, 0x54, 0x01, 0x54, 0xa2, 0xe1, 0x01, 0x3c, 0x80, 0xa0, 0x04, 0x12,
	0xa2, 0x98, 0x80, 0x49, 0x4c, 0x62, 0x26, 0x02, 0x04, 0x28, 0x48, 0xa4, 0x04, 0x91, 0x4a, 0xa8,
	0x49, 0x11, 0x80, 0x00, 0x30, 0x69, 0x06, 0xf4, 0x93, 0x01, 0x08, 0x03, 0x11, 0x00, 0x51, 0x40,
	0x40, 0x12, 0x20, 0xd1, 0xba, 0x8d, 0x06, 0x40, 0x28, 0xe4, 0x10, 0x86, 0x85, 0xc8, 0x42, 0x02,
	0xc1, 0x20, 0x00, 0x90, 0x5b, 0x01, 0x14, 0x10, 0x20, 0x07, 0xe0, 0x81, 0x0a, 0x02, 0xb6, 0x39,
	0x19, 0x80, 0x10, 0x29, 0x48, 0x43, 0x2b, 0x0b, 0xc0, 0x84, 0x64, 0x34, 0xb6, 0x42, 0x14, 0x80,
	0x48, 0x26, 0x2e, 0xd1, 0x00, 0x33, 0x14, 0x11, 0x20, 0x02, 0x52, 0x82, 0xa9, 0x45, 0x08, 0xaa,
	0x18, 0x00, 0x21, 0x91, 0x9d, 0x12, 0x80, 0x4c, 0xa6, 0xd2, 0x90, 0x40, 0x19, 0x0f, 0x40, 0x64,
	0x01, 0x10, 0x68, 0x04, 0x45, 0x20, 0xb2, 0x18, 0x06, 0x20, 0x94, 0x8a, 0x30, 0x90, 0x03, 0xb4,
	0x2c, 0x83, 0x69, 0x20, 0x14, 0x50, 0x90, 0x30, 0x09, 0x43, 0x30, 0x80, 0x12, 0x18, 0xc1, 0x95,
	0x62, 0x40, 0x52, 0x70, 0x0c, 0x8c, 0x84, 0x89, 0x08, 0xc4, 0x00, 0x09, 0x04, 0x09, 0x00, 0x60,
	0xc5, 0x30, 0xa7, 0x60, 0x81, 0x20, 0x32, 0x06, 0x00, 0x99, 0x09, 0x04, 0x0c, 0x18, 0x61, 0x08,
	0x10, 0x25, 0x41, 0xc0, 0x14, 0x03, 0x20, 0x02, 0x10, 0x4d, 0x08, 0x04, 0x07, 0x01, 0x33, 0x74,
	0xce, 0x22, 0x00, 0x61, 0x61, 0x38, 0x05, 0x0c, 0x46, 0x60, 0xf0, 0x41, 0x02, 0x84, 0x04, 0x08,
	0x3e, 0x60, 0x30, 0x0a, 0xa1, 0x41, 0xee, 0x16, 0x02, 0xd8, 0x04, 0x20, 0x83, 0x01, 0x00, 0x01,
	0x5b, 0x04, 0x34, 0x08, 0xa0, 0x70, 0xc0, 0xa0, 0x33, 0x5c, 0xd8, 0x03, 0x48, 0x00, 0x3a, 0x1b,
	0x00, 0x87, 0x41, 0x70, 0x80, 0x88, 0x00, 0x85, 0x00, 0x8c, 0x28, 0x24, 0xbb, 0x00, 0xe0, 0x50,
	0x4b, 0x00, 0x85, 0x10, 0x36, 0x48, 0x49, 0x20, 0x00, 0x14, 0x02, 0x12, 0x08, 0x40, 0xa4, 0x32,
	0x04, 0x11, 0x1b, 0x21, 0x0f, 0x80, 0x30, 0x83, 0x62, 0x86, 0x38, 0xd2, 0x25, 0x46, 0xb4, 0x00,
	0x89, 0x79, 0x84, 0x0e, 0x40, 0x14, 0x1a, 0x0c, 0xb4, 0x97, 0x4b, 0x0c, 0xf1, 0x00, 0x06, 0x0d,
	0x84, 0x02, 0x40, 0x00, 0x80, 0xb2, 0x88, 0x48, 0x02, 0x08, 0x51, 0x08, 0x90, 0x1c, 0x48, 0xa4,
	0x0c, 0x80, 0x08, 0xb4, 0x88, 0x8f, 0xa8, 0x64, 0x14, 0x69, 0x33, 0x81, 0x82, 0x23, 0x26, 0x46,
	0x8a, 0x02, 0x54, 0x08, 0xc9, 0x10, 0x33, 0x12, 0xa1, 0xc0, 0x86, 0xc0, 0x34, 0x20, 0x40, 0x09,
	0x10, 0x51, 0x07, 0x70, 0x41, 0x82, 0x81, 0x10, 0x1e, 0x0c, 0x3a, 0x47, 0x61, 0x02, 0x48, 0x43,
	0xcb, 0x0a, 0x02, 0xf1, 0x1c, 0x60, 0x10, 0x05, 0x16, 0x51, 0xaa, 0x88, 0x45, 0x98, 0x82, 0x64,
	0x91, 0xb0, 0x32, 0x08, 0x83, 0x01, 0x40, 0xd3, 0x00, 0x39, 0x40, 0x92, 0x40, 0x42, 0x81, 0xe1,
	0x08, 0x88, 0xc5, 0x2a, 0x0a, 0x05, 0x50, 0x83, 0xe4, 0x07, 0xa1, 0x10, 0x67, 0x28, 0xa4, 0x88,
	0x01, 0x0b, 0x20, 0x14, 0x10, 0x2a, 0xcd, 0x41, 0x28, 0x22, 0x16, 0x18, 0x90, 0xd1, 0x00, 0x44,
	0x18, 0x81, 0xa1, 0x00, 0x14, 0x1e, 0x04, 0x22, 0x25, 0xd1, 0x14, 0x58, 0x09, 0x4a, 0x3a, 0x06,
	0x83, 0x25, 0x24, 0x01, 0x4c, 0x18, 0x01, 0x22, 0xa8, 0x30, 0x08, 0x41, 0x58, 0xa7, 0x52, 0x81,
	0xdc, 0x52, 0x26, 0x00, 0x86, 0x00, 0x38, 0x64, 0x85, 0x07, 0x0c, 0x52, 0x1a, 0x12, 0xa8, 0x04,
	0x8e, 0x32, 0x01, 0x41, 0x20, 0xe9, 0x4b, 0x60, 0x76, 0x21, 0x20, 0x2c, 0xa9, 0x00, 0x82, 0x32,
	0x64, 0x4a, 0x30, 0x48, 0x4a, 0x02, 0x04, 0x17, 0x10, 0x16, 0x05, 0x10, 0x41, 0x68, 0x71, 0x40,
	0x02, 0x20, 0x92, 0x60, 0x0e, 0xc2, 0x31, 0x04, 0x11, 0x83, 0x41, 0x0a, 0xb2, 0x11, 0x00, 0x25,
	0x45, 0x84, 0x40, 0x00, 0xda, 0x28, 0x41, 0x00, 0x4b, 0x0c, 0xe6, 0x00, 0x85, 0x50, 0xc8, 0x0a,
	0x04, 0x54, 0x30, 0x09, 0x00, 0x0e, 0xa6, 0x6a, 0x02, 0x02, 0x20, 0x0d, 0x56, 0xa0, 0x48, 0x04,
	0xa0, 0x90, 0x14, 0x81, 0x08, 0x2a, 0x02, 0x91, 0x30, 0x00, 0x01, 0x4c, 0x20, 0x40, 0x49, 0x29,
	0x15, 0x89, 0x04, 0x10, 0xa5, 0x60, 0x90, 0x20, 0x5e, 0x84, 0x20, 0x45, 0x03, 0x07, 0x14, 0x09,
	0x24, 0x08, 0x70, 0x8a, 0x92, 0x08, 0x46, 0xe3, 0x30, 0x00, 0x08, 0x01, 0x48, 0x51, 0x89, 0x10,
	0x40, 0xa8, 0x08, 0xa0, 0x12, 0x81, 0x34, 0x61, 0x41, 0x24, 0x84, 0x5a, 0x44, 0x24, 0x14, 0x9a,
	0x28, 0x68, 0x95, 0x44, 0x60, 0x43, 0x89, 0x80, 0x64, 0x85, 0x88, 0x02, 0x50, 0x01, 0x81, 0x74,
	0x18, 0x23, 0x24, 0x41, 0xe1, 0x02, 0xe9, 0x00, 0x0d, 0x04, 0x36, 0xd2, 0xb1, 0x01, 0x00, 0x8d,
	0x0e, 0x54, 0x41, 0x09, 0x30, 0x85, 0x08, 0x48, 0x81, 0x29, 0x38, 0x84, 0x4c, 0x28, 0x74, 0x03,
	0xa2, 0x0e, 0x20, 0x56, 0x04, 0x22, 0xa0, 0x01, 0x08, 0xc1, 0x84, 0x20, 0x1a, 0xd2, 0x10, 0x2a,
	0x11, 0x02, 0x4a, 0x10, 0x01, 0x32, 0xb1, 0x01, 0x12, 0x4a, 0x42, 0x04, 0xb8, 0x0e, 0x24, 0x90,
	0x84, 0x16, 0x47, 0x93, 0x0a, 0x44, 0x81, 0x0a, 0x18, 0x30, 0x80, 0x92, 0xb0, 0x11, 0x03, 0x3c,
	0xa4, 0x08, 0x00, 0x64, 0x44, 0xca, 0x26, 0xd2, 0x0b, 0x21, 0x88, 0x00, 0xcc, 0x16, 0x83, 0x71,
	0x22, 0xd0, 0xd0, 0x87, 0x08, 0xd7, 0x48, 0x0d, 0x00, 0xcb, 0x20, 0x6a, 0x43, 0x82, 0x04, 0x48,
	0x1d, 0xc6, 0x20, 0x20, 0x30, 0x05, 0xe1, 0x15, 0x01, 0x10, 0xf1, 0xa9, 0x82, 0x09, 0x98, 0x83,
	0x18, 0xa0, 0x60, 0x07, 0xd1, 0x83, 0x00, 0x08, 0x75, 0x02, 0x3c, 0x00, 0x0c, 0xc1, 0x6c, 0x02,
	0xba, 0x9a, 0x20, 0x42, 0x26, 0x14, 0x44, 0x20, 0x10, 0x4c, 0x43, 0xe1, 0x5a, 0x81, 0x42, 0x26,
	0x84, 0x0d, 0x20, 0x56, 0x00, 0x91, 0x98, 0x80, 0x00, 0x65, 0x12, 0x21, 0x08, 0x00, 0x50, 0x4a,
	0x82, 0x02, 0x06, 0xca, 0x82, 0x8c, 0x02, 0x64, 0x00, 0x83, 0xa1, 0x82, 0x50, 0x54, 0x01, 0x54,
	0x30, 0xc0, 0x00, 0x09, 0x14, 0xc4, 0x04, 0xa0, 0x03, 0x07, 0xcc, 0x88, 0x48, 0x48, 0xb0, 0x11,
	0x14, 0x19, 0x08, 0x88, 0x24, 0xd0, 0x00, 0x12, 0x00, 0x18, 0xc1, 0x0a, 0x80, 0x22, 0x08, 0x8d,
	0x45, 0x84, 0x08, 0x25, 0x40, 0x22, 0x94, 0x18, 0xc0, 0x18, 0x47, 0x72, 0x00, 0x64, 0x41, 0x00,
	0x24, 0xe2, 0x19, 0x81, 0x14, 0x81, 0x01, 0x14, 0x06, 0x50, 0x0c, 0xc0, 0x82, 0x0c, 0x3a, 0x21,
	0x4a, 0x03, 0x08, 0x41, 0x49, 0x06, 0x52, 0x08, 0x85, 0xb1, 0xd2, 0x02, 0x24, 0x00, 0x68, 0x01,
	0xd9, 0x12, 0x04, 0x04, 0x00, 0x20, 0x01, 0x01, 0x12, 0x0e, 0x10, 0x52, 0x50, 0x81, 0x40, 0x83,
	0xc8, 0x6e, 0x10, 0x22, 0x20, 0x78, 0x01, 0x04, 0x4a, 0x92, 0x61, 0x0d, 0x2c, 0xc2, 0x2c, 0x62,
	0x45, 0x4a, 0x84, 0x34, 0x86, 0x08, 0x00, 0xc0, 0x3a, 0x84, 0x24, 0x48, 0xe7, 0x00, 0x84, 0x4a,
	0x12, 0x09, 0x81, 0x00, 0x24, 0x65, 0x31, 0xa5, 0x00, 0x56, 0x4c, 0x12, 0x24, 0xa8, 0x9a, 0x30,
	0xd2, 0x05, 0x28, 0x53, 0x49, 0x05, 0xc1, 0x4b, 0x60, 0x50, 0x94, 0x11, 0x2c, 0x10, 0x91, 0x02,
	0x14, 0xf2, 0xa3, 0x23, 0xdc, 0x98, 0x28, 0x04, 0x43, 0x20, 0x23, 0xd8, 0x00, 0x20, 0x0e, 0x03,
	0x20, 0x30, 0x6c, 0x54, 0x04, 0x12, 0x81, 0x01, 0x84, 0x60, 0x54, 0x20, 0x24, 0x11, 0xba, 0xa5,
	0x08, 0x0a, 0xc9, 0x40, 0x83, 0x28, 0x2e, 0x39, 0x14, 0x82, 0x52, 0x01, 0x1a, 0x00, 0x81, 0x05,
	0x45, 0x2a, 0xa0, 0x18, 0x20, 0x40, 0x42, 0x01, 0x48, 0x00, 0x82, 0xbd, 0x80, 0x12, 0x61, 0x00,
	0x31, 0x01, 0x81, 0x08, 0x52, 0x05, 0x62, 0x00, 0x08, 0x18, 0x28, 0xc0, 0x40, 0x08, 0x90, 0x58,
	0x06, 0xb1, 0x92, 0x82, 0x60, 0x95, 0x10, 0x36, 0x00, 0x01, 0x05, 0x00, 0x10, 0xa8, 0x81, 0x11,
	0x8f, 0x46, 0x38, 0x10, 0x02, 0x30, 0x48, 0x06, 0x22, 0x4a, 0x21, 0x51, 0x21, 0x2d, 0x8c, 0x80,
	0x00, 0x25, 0x88, 0xac, 0x51, 0x10, 0x4a, 0x04, 0x87, 0x82, 0x02, 0x0c, 0x08, 0x0b, 0x1c, 0x30,
	0x28, 0x12, 0xc0, 0x80, 0x0d, 0x60, 0x82, 0x40, 0xb8, 0x80, 0x91, 0x45, 0x70, 0x44, 0x41, 0x0a,
	0xc0, 0x40, 0xa0, 0x16, 0x84, 0x1a, 0x2c, 0x84, 0xc6, 0x09, 0x44, 0x23, 0x0b, 0xad, 0x41, 0x16,
	0xc2, 0x70, 0x10, 0x22, 0x1c, 0x15, 0x02, 0xc1, 0x20, 0x86, 0x08, 0x21, 0x44, 0x89, 0xe2, 0x4a,
	0x12, 0x01, 0xb1, 0x80, 0x11, 0x08, 0x48, 0x01, 0x48, 0x80, 0x60, 0x12, 0x28, 0x22, 0x07, 0x60,
	0xaa, 0x38, 0x15, 0x4e, 0x74, 0x60, 0x02, 0x12, 0x38, 0x84, 0x05, 0x00, 0x87, 0x41, 0xa0, 0x20,
	0x4d, 0xc1, 0x02, 0x36, 0x82, 0x0c, 0x28, 0x89, 0x44, 0x36, 0x90, 0x28, 0x18, 0x79, 0x45, 0xa0,
	0x00, 0x16, 0xd0, 0x04, 0x30, 0x41, 0xe5, 0x12, 0x81, 0x10, 0xa0, 0x98, 0x02, 0x84, 0x48, 0x40,
	0x80, 0x83, 0x90, 0xda, 0x00, 0x60, 0x00, 0xa8, 0x93, 0x20, 0x00, 0x80, 0x5a, 0x50, 0x82, 0x03,
	0x20, 0x50, 0x04, 0x4c, 0x70, 0x28, 0xa5, 0xc4, 0x08, 0xa1, 0x10, 0x80, 0x93, 0x80, 0x88, 0x14,
	0x26, 0x72, 0x57, 0xca, 0x13, 0x80, 0x41, 0x02, 0x30, 0x81, 0x68, 0x10, 0x41, 0x19, 0x4e, 0x58,
	0x02, 0x43, 0xb8, 0x80, 0x0c, 0x00, 0x40, 0x66, 0x31, 0x14, 0x09, 0x80, 0x08, 0x44, 0x22, 0x03,
	0x08, 0xc8, 0x02, 0x01, 0x40, 0x20, 0xc9, 0x27, 0x11, 0x12, 0x25, 0x14, 0x11, 0x82, 0x06, 0x90,
	0xc0, 0xca, 0x02, 0x44, 0x02, 0x00, 0x09, 0x85, 0xa0, 0x5a, 0x42, 0x90, 0x05, 0x00, 0x53, 0x88,
	0x0c, 0x04, 0xd9, 0x90, 0xe1, 0x87, 0x03, 0x2c, 0x40, 0x21, 0x18, 0x44, 0x11, 0x28, 0x62, 0xb0,
	0x5a, 0x20, 0x44, 0x0e, 0x0e, 0x22, 0x10, 0x01, 0x24, 0xe1, 0x90, 0x08, 0x72, 0x12, 0x00, 0x00,
	0x8c, 0x40, 0x04, 0x06, 0x46, 0x10, 0x0b, 0x08, 0x04, 0x2a, 0x6a, 0x84, 0x00, 0x25, 0x84, 0x40,
	0x01, 0x06, 0x03, 0x19, 0xa4, 0x51, 0x10, 0x2d, 0x20, 0x93, 0x23, 0x82, 0xcc, 0x1a, 0xc2, 0x2a,
	0x40, 0x00, 0x10, 0x45, 0x95, 0x05, 0x52, 0x40, 0x08, 0x0c, 0x00, 0x48, 0x00, 0x10, 0x24, 0x2a,
	0x33, 0x41, 0xcb, 0x61, 0x08, 0x22, 0x40, 0x3e, 0x08, 0x4d, 0x23, 0x20, 0x40, 0xca, 0x88, 0x08,
	0x03, 0x4b, 0x14, 0xc2, 0x21, 0x8b, 0x91, 0x81, 0x48, 0x4a, 0x45, 0x82, 0x24, 0xa0, 0x0f, 0x0c,
	0x46, 0x10, 0x8b, 0x85, 0x11, 0x04, 0x0d, 0x28, 0x92, 0x62, 0x00, 0xb1, 0x06, 0x46, 0x06, 0x42,
	0x90, 0x00, 0x74, 0x1e, 0xec, 0x40, 0x82, 0x90, 0x9a, 0x4d, 0xc8, 0x2e, 0x04, 0xb7, 0x90, 0x08,
	0x7d, 0x82, 0x01, 0x20, 0xc7, 0x30, 0x03, 0x68, 0x90, 0x08, 0x28, 0x00, 0x28, 0x05, 0x14, 0x04,
	0xa9, 0x20, 0x04, 0x61, 0x04, 0xb1, 0x44, 0x01, 0x50, 0x91, 0x11, 0x90, 0xa0, 0x4a, 0xe4, 0x06,
	0x84, 0x08, 0x0d, 0x01, 0x18, 0x4b, 0x08, 0x25, 0x62, 0x80, 0x08, 0x05, 0x08, 0x10, 0x52, 0x2b,
	0x88, 0x90, 0x41, 0x00, 0x6a, 0x06, 0x12, 0x21, 0xc0, 0x00, 0xe2, 0x00, 0x03, 0xd0, 0x06, 0x40,
	0x0c, 0x61, 0x14, 0x44, 0x62, 0x30, 0x04, 0x18, 0x62, 0x08, 0x81, 0x10, 0x13, 0x3c, 0x1b, 0xc6,
	0x02, 0x06, 0x73, 0x95, 0xa5, 0x88, 0x2a, 0x48, 0x40, 0x0a, 0xb0, 0x79, 0x10, 0x49, 0x02, 0xa5,
	0xa0, 0x26, 0x70, 0x19, 0x43, 0x26, 0x80, 0x09, 0x23, 0x60, 0xc0, 0x84, 0x00, 0x21, 0x49, 0xac,
	0x10, 0x14, 0xc6, 0x68, 0x54, 0x43, 0xab, 0x30, 0x44, 0x41, 0x06, 0xa5, 0xf8, 0x08, 0x40, 0x4a,
	0x20, 0x14, 0x20, 0x20, 0x14, 0x89, 0x40, 0xa2, 0x46, 0x62, 0x08, 0x05, 0xb9, 0x10, 0xa1, 0x10,
	0x10, 0x81, 0x0c, 0xc1, 0x40, 0x86, 0x42, 0x06, 0x0a, 0x9a, 0x50, 0x45, 0x24, 0x30, 0x91, 0x20,
	0xa2, 0x08, 0x84, 0x20, 0x3e, 0x00, 0x61, 0x23, 0xf4, 0x42, 0x89, 0x70, 0x42, 0x22, 0xa0, 0x9c,
	0x58, 0x8d, 0x44, 0x25, 0x81, 0xb8, 0x14, 0xce, 0x20, 0x24, 0x21, 0x60, 0x81, 0x14, 0x18, 0x47,
	0x50, 0x33, 0xc0, 0x02, 0x04, 0x89, 0x05, 0x30, 0xc4, 0x8a, 0x02, 0x29, 0x8e, 0x09, 0x40, 0x80,
	0x19, 0x20, 0x11, 0xc3, 0xc4, 0x08, 0x61, 0x32, 0xa4, 0x5c, 0x8a, 0x81, 0x20, 0x45, 0x02, 0x0b,
	0x41, 0xc3, 0xa9, 0x00, 0x82, 0x02, 0x1b, 0x40, 0xd9, 0x64, 0x60, 0x00, 0x70, 0x89, 0xa4, 0x8c,
	0x20, 0x2c, 0x05, 0x90, 0x02, 0x40, 0x8c, 0x09, 0x02, 0x64, 0x20, 0x96, 0x05, 0x45, 0xc0, 0x10,
	0x93, 0x40, 0x30, 0x31, 0x08, 0x88, 0x14, 0x00, 0x20, 0x11, 0x94, 0x0a, 0x8f, 0x02, 0x82, 0x0a,
	0x15, 0x0c, 0x0e, 0x24, 0x4c, 0x34, 0xda, 0x8e, 0x80, 0x10, 0x43, 0x22, 0x00, 0xe0, 0x0c, 0x81,
	0x00, 0x28, 0x16, 0x22, 0xb9, 0x0c, 0x95, 0x90, 0xaa, 0x18, 0x84, 0x52, 0x29, 0x10, 0x09, 0x41,
	0x20, 0x26, 0x9a, 0xa0, 0x41, 0x58, 0x80, 0x74, 0x40, 0x93, 0x0d, 0xa0, 0x90, 0x06, 0x30, 0x10,
	0xa3, 0x82, 0x81, 0x12, 0x80, 0x56, 0xa7, 0x50, 0x00, 0x10, 0x41, 0xcd, 0x00, 0xa7, 0x00, 0xa2,
	0x08, 0xcc, 0x06, 0x0e, 0xb1, 0x41, 0x36, 0x91, 0x08, 0xc4, 0x20, 0x40, 0xc2, 0xb1, 0xa0, 0x1d,
	0x24, 0x6c, 0x62, 0x42, 0x87, 0xb0, 0x1a, 0x0b, 0x42, 0x94, 0xb8, 0x21, 0x34, 0x07, 0x0a, 0x42,
	0x80, 0xd8, 0x80, 0x20, 0x02, 0xc6, 0x04, 0xe3, 0x00, 0x32, 0xd0, 0x00, 0x80, 0x66, 0xc1, 0x22,
	0x0c, 0x29, 0xca, 0x40, 0x08, 0x84, 0x29, 0x06, 0x25, 0x00, 0x26, 0x3c, 0x11, 0x18, 0x01, 0x84,
	0x08, 0x80, 0x24, 0x20, 0x42, 0x98, 0x84, 0x41, 0x20, 0x20, 0x24, 0x10, 0x8b, 0x1c, 0x43, 0x60,
	0x02, 0x14, 0x00, 0x27, 0x30, 0x94, 0x8d, 0x52, 0x84, 0x08, 0x14, 0x18, 0x05, 0x20, 0x38, 0x22,
	0xa1, 0x90, 0xa1, 0x0a, 0xcc, 0x0c, 0x00, 0x61, 0x01, 0x64, 0x57, 0x2d, 0x4c, 0x40, 0x01, 0xac,
	0x08, 0x16, 0x81, 0x10, 0xb1, 0xc3, 0x10, 0x4d, 0x49, 0x25, 0x46, 0x56, 0x18, 0x24, 0x38, 0x01,
	0x04, 0x00, 0x53, 0x80, 0x25, 0x65, 0x18, 0xa2, 0x12, 0x10, 0x13, 0xa0, 0x05, 0x89, 0x62, 0x48,
	0x24, 0x29, 0xa8, 0x00, 0x00, 0x01, 0x60, 0x55, 0x42, 0x29, 0x84, 0xc5, 0x80, 0x1c, 0x01, 0x78,
	0xb8, 0x05, 0x55, 0x48, 0x32, 0xc0, 0x00, 0x00, 0x90, 0x90, 0x2c, 0x6a, 0xa1, 0x82, 0x0d, 0xe0,
	0x56, 0xc9, 0x18, 0x00, 0x10, 0x01, 0x50, 0x94, 0x44, 0x42, 0x90, 0x08, 0x04, 0xa4, 0x86, 0xa2,
	0x2c, 0x41, 0x51, 0x9a, 0x48, 0x1b, 0x87, 0x06, 0x21, 0x00, 0x1e, 0x40, 0xc8, 0x82, 0x42, 0x82,
	0x20, 0x09, 0x21, 0x94, 0x80, 0x28, 0x51, 0x39, 0x04, 0x8c, 0x10, 0x0b, 0x3a, 0x63, 0x20, 0x20,
	0x08, 0x10, 0xe6, 0x04, 0xc1, 0x51, 0x00, 0xa0, 0x48, 0x4e, 0x08, 0x81, 0x08, 0x26, 0xd1, 0x4b,
	0x06, 0x40, 0xf4, 0x22, 0x20, 0x18, 0x54, 0x61, 0x38, 0x36, 0xc9, 0x8a, 0x88, 0x12, 0x4c, 0x30,
	0x02, 0x13, 0x01, 0x10, 0x01, 0x05, 0x02, 0x22, 0x98, 0x80, 0xe5, 0x06, 0x24, 0x50, 0x12, 0x92,
	0x32, 0x48, 0x10, 0x65, 0x08, 0x55, 0x02, 0xa4, 0x28, 0x04, 0xa2, 0x0a, 0x57, 0x82, 0xb1, 0xc8,
	0x08, 0x82, 0x42, 0x04, 0x81, 0x12, 0x94, 0x04, 0x2b, 0x0c, 0x45, 0x83, 0x88, 0x40, 0x0c, 0x22,
	0x64, 0x10, 0x83, 0x32, 0x70, 0x00, 0x09, 0x10, 0x72, 0x61, 0x00, 0x44, 0x50, 0xc4, 0x12, 0x80,
	0x40, 0x02, 0x00, 0x02, 0x01, 0x22, 0x24, 0x69, 0x04, 0x49, 0x03, 0x07, 0x34, 0x00, 0x08, 0x00,
	0x01, 0x46, 0x29, 0x54, 0x12, 0x20, 0xa2, 0x40, 0x10, 0xca, 0x00, 0x12, 0x21, 0x91, 0x41, 0x10,
	0x80, 0x0e, 0x00, 0xa8, 0x08, 0x21, 0x42, 0x40, 0x4e, 0x05, 0x89, 0x28, 0x88, 0x49, 0x0c, 0x02,
	0x44, 0xca, 0x25, 0xb0, 0x13, 0xc8, 0x00, 0xc5, 0x91, 0xa0, 0x28, 0x11, 0x4e, 0x76, 0x42, 0x01,
	0x32, 0x11, 0xc6, 0x80, 0x00, 0xc2, 0x38, 0x14, 0x51, 0x81, 0xc3, 0x28, 0x41, 0x00, 0x1a, 0x2c,
	0x10, 0x41, 0x58, 0x80, 0x08, 0x17, 0x38, 0xd2, 0x27, 0x02, 0x00, 0x81, 0x1c, 0x90, 0x03, 0xc5,
	0x52, 0x91, 0x68, 0xa4, 0x09, 0x88, 0x03, 0x48, 0xc0, 0xd0, 0x05, 0x94, 0x00, 0x20, 0x68, 0x05,
	0x3a, 0xae, 0x0c, 0xc2, 0xe7, 0x0a, 0x84, 0x33, 0x84, 0x08, 0x48, 0x20, 0x50, 0x40, 0xd3, 0x05,
	0x30, 0xd5, 0xa8, 0x20, 0x03, 0x0b, 0x84, 0x98, 0x04, 0x22, 0x44, 0x25, 0x48, 0x01, 0x64, 0x0a,
	0x86, 0x14, 0x82, 0x51, 0x88, 0x80, 0x0a, 0x83, 0x30, 0xa6, 0x82, 0x0a, 0xa0, 0x1c, 0x01, 0x08,
	0xc1, 0xb8, 0x01, 0x48, 0x04, 0x6c, 0x4a, 0x86, 0x11, 0x0d, 0x8c, 0x91, 0x8c, 0x0c, 0x02, 0x50,
	0x07, 0xa4, 0x08, 0x43, 0x34, 0x56, 0x60, 0x20, 0x10, 0x08, 0x06, 0x68, 0xc4, 0x20, 0xae, 0x3c,
	0x81, 0x46, 0x0c, 0x10, 0x40, 0x81, 0x09, 0x80, 0xa2, 0x10, 0x41, 0x93, 0x33, 0x4c, 0x80, 0x21,
	0x0c, 0x80, 0xa2, 0x06, 0xc4, 0x00, 0xc5, 0x30, 0xf0, 0x19, 0x03, 0xd0, 0x00, 0xc1, 0x0e, 0x36,
	0x00, 0x0d, 0xd1, 0x8c, 0x02, 0x22, 0x13, 0xc8, 0x82, 0x18, 0x53, 0x20, 0x08, 0x84, 0x0a, 0x9a,
	0xd0, 0x53, 0x48, 0x00, 0x00, 0x00, 0x10, 0x1c, 0xc4, 0x20, 0x00, 0x90, 0x6a, 0x05, 0x18, 0x06,
	0x09, 0x44, 0x10, 0xb1, 0x95, 0x0c, 0x90, 0x05, 0x2e, 0x21, 0x80, 0x08, 0x49, 0x05, 0x00, 0x22,
	0x44, 0xc3, 0x10, 0x88, 0xdd, 0xa2, 0x42, 0x35, 0x29, 0x30, 0x45, 0x08, 0x21, 0x78, 0x40, 0x52,
	0x04, 0x40, 0x05, 0x6a, 0x10, 0x43, 0x02, 0x88, 0x65, 0x02, 0x42, 0x1e, 0xc0, 0x18, 0x82, 0x34,
	0xd0, 0x08, 0x28, 0x85, 0x4a, 0x83, 0x14, 0x02, 0x41, 0x10, 0x33, 0x03, 0x12, 0xa9, 0x1c, 0x0a,
	0x0c, 0x04, 0x82, 0x21, 0x1c, 0x86, 0x86, 0x06, 0xd3, 0x68, 0xab, 0xc1, 0x08, 0xc0, 0x40, 0x60,
	0xd2, 0x20, 0x10, 0x10, 0x0e, 0x06, 0xe0, 0xa8, 0x05, 0x21, 0x05, 0x81, 0x22, 0x43, 0x9a, 0x95,
	0x44, 0x99, 0x06, 0x04, 0x11, 0xf0, 0x31, 0xbc, 0x12, 0x22, 0x54, 0x80, 0x63, 0x36, 0x34, 0x04,
	0xc2, 0x40, 0xd1, 0x4a, 0x90, 0x20, 0xc9, 0x00, 0x18, 0x40, 0x50, 0x84, 0x81, 0xc0, 0x4b, 0x68,
	0x00, 0x21, 0x90, 0x28, 0xd7, 0x04, 0x20, 0x60, 0x20, 0x04, 0x20, 0x43, 0x41, 0x42, 0x20, 0x18,
	0x24, 0x94, 0xc7, 0x80, 0x08, 0x30, 0x03, 0x87, 0x40, 0x1c, 0x46, 0x14, 0x02, 0x20, 0xa6, 0xd5,
	0x88, 0x04, 0x20, 0x54, 0x20, 0x29, 0x35, 0x11, 0x87, 0x52, 0x40, 0x60, 0x23, 0xa5, 0x06, 0x0c,
	0x44, 0x24, 0x71, 0x20, 0x20, 0xd0, 0xe2, 0x08, 0x40, 0x89, 0x8c, 0x40, 0x18, 0x81, 0x10, 0x30,
	0x01, 0x01, 0x09, 0x03, 0x2f, 0x20, 0x34, 0x18, 0x1c, 0x14, 0x8a, 0x0c, 0x58, 0xa0, 0x01, 0xa8,
	0x94, 0x47, 0x40, 0x30, 0x04, 0x42, 0x13, 0x99, 0x00, 0x2b, 0x18, 0x20, 0xf2, 0x35, 0x2c, 0x84,
	0x40, 0x52, 0x20, 0x83, 0x0c, 0x54, 0x88, 0x20, 0x4c, 0x62, 0xa0, 0x04, 0x15, 0x99, 0xec, 0x0e,
	0xa3, 0x02, 0x14, 0x21, 0x40, 0x60, 0x0a, 0x42, 0xe2, 0x80, 0x20, 0x91, 0x08, 0x2e, 0x40, 0x3a,
	0x22, 0xbc, 0x02, 0x22, 0x46, 0xc1, 0x60, 0x18, 0x89, 0x4c, 0xa4, 0x02, 0x90, 0x42, 0x20, 0x00,
	0xc3, 0xe8, 0x04, 0x10, 0x31, 0x24, 0x44, 0x87, 0x4d, 0x1a, 0x02, 0xa9, 0x0b, 0x90, 0x08, 0x2d,
	0x42, 0xd6, 0x6a, 0x10, 0x91, 0x10, 0x0c, 0x42, 0xa4, 0x02, 0x09, 0x48, 0x01, 0x40, 0x42, 0x51,
	0x82, 0x00, 0x9d, 0x04, 0xa0, 0x40, 0x36, 0x41, 0x20, 0x04, 0x51, 0x06, 0x46, 0x60, 0x8a, 0x0b,
	0x44, 0x01, 0x05, 0x10, 0x92, 0x11, 0x04, 0x21, 0x00, 0x88, 0x24, 0x05, 0x90, 0x13, 0x68, 0x00,
	0x43, 0x4c, 0x21, 0x62, 0x22, 0x80, 0x15, 0xa2, 0x14, 0x41, 0x80, 0x11, 0x44, 0x40, 0x82, 0x08,
	0x02, 0x40, 0x93, 0x00, 0x52, 0x8e, 0x10, 0x52, 0x82, 0xa4, 0x2c, 0x4a, 0x45, 0x40, 0x04, 0xc0,
	0x09, 0xa8, 0x0d, 0x4a, 0x00, 0x12, 0x48, 0x34, 0x89, 0x03, 0xa3, 0x00, 0xb1, 0xe2, 0x28, 0x15,
	0x10, 0x09, 0x08, 0xe1, 0x03, 0x93, 0x04, 0x82, 0xa0, 0x46, 0xe2, 0xb0, 0x08, 0x00, 0xc6, 0x00,
	0x1c, 0x42, 0xc0, 0x32, 0x01, 0x57, 0x00, 0x02, 0x10, 0x32, 0x06, 0x1c, 0x43, 0x81, 0x5a, 0xc2,
	0x01, 0x8e, 0x55, 0x14, 0xab, 0x30, 0x14, 0x09, 0x03, 0x70, 0xc4, 0x23, 0x32, 0xa0, 0x21, 0xa2,
	0x44, 0x4d, 0x82, 0x08, 0xd4, 0x68, 0x2a, 0x8c, 0x11, 0x60, 0x06, 0x64, 0x00, 0x16, 0x90, 0x82,
	0xa4, 0x00, 0x00, 0x02, 0x09, 0x29, 0x11, 0x4c, 0x44, 0x00, 0x29, 0x05, 0xf0, 0x16, 0x44, 0x50,
	0xa0, 0x21, 0x15, 0x1c, 0x49, 0xac, 0x20, 0x42, 0xb8, 0x80, 0x09, 0x48, 0x23, 0x50, 0x44, 0x6a,
	0x21, 0x41, 0xc8, 0x80, 0x20, 0x34, 0xe8, 0xb0, 0x09, 0x18, 0x02, 0x00, 0x20, 0x99, 0xb0, 0x45,
	0x04, 0x05, 0x04, 0x74, 0x08, 0x10, 0xe1, 0x02, 0x05, 0x0a, 0xc3, 0x40, 0x82, 0xd1, 0x94, 0x00,
	0x08, 0x33, 0x42, 0x25, 0xa0, 0x19, 0x40, 0x56, 0x20, 0x20, 0x11, 0xc8, 0x8a, 0x0c, 0x06, 0x86,
	0x58, 0x08, 0xc0, 0x04, 0xa3, 0x18, 0xa1, 0x72, 0x80, 0x51, 0x04, 0x88, 0x10, 0x00, 0xa1, 0x14,
	0x14, 0x02, 0xec, 0x44, 0x67, 0x02, 0x18, 0x48, 0x11, 0x41, 0x32, 0x00, 0x08, 0x23, 0x08, 0x1b,
	0x22, 0x60, 0x60, 0x98, 0x24, 0x10, 0x40, 0xc1, 0x6a, 0x45, 0x48, 0x2a, 0xf4, 0x40, 0x07, 0x42,
	0x06, 0x58, 0x98, 0x80, 0x85, 0x62, 0x20, 0x02, 0x31, 0xbb, 0x04, 0x13, 0x82, 0x60, 0x52, 0x0b,
	0x91, 0x24, 0x01, 0x61, 0x0c, 0x80, 0x0b, 0x01, 0x20, 0x8c, 0x2c, 0x3c, 0x00, 0x50, 0x31, 0x00,
	0x51, 0x48, 0x1a, 0x02, 0x20, 0x22, 0x0d, 0x80, 0xc4, 0x28, 0x06, 0x82, 0x09, 0xcc, 0x43, 0x4d,
	0x0a, 0xa1, 0x02, 0x1a, 0x05, 0x86, 0x82, 0x5a, 0x12, 0x62, 0xa4, 0x68, 0x09, 0x0e, 0x20, 0x44,
	0x09, 0x10, 0x50, 0x05, 0x89, 0x0a, 0x23, 0x28, 0x0c, 0x94, 0x05, 0x22, 0x40, 0x01, 0x12, 0x02,
	0x10, 0x80, 0x21, 0x02, 0x45, 0x08, 0x20, 0xa0, 0x08, 0x02, 0x1a, 0x55, 0xe0, 0x9d, 0x04, 0x51,
	0x09, 0x1c, 0x14, 0x23, 0x8a, 0xc1, 0x00, 0x64, 0x36, 0x20, 0xa2, 0x19, 0xd5, 0x42, 0x2c, 0x14,
	0x72, 0x40, 0x80, 0x90, 0x0a, 0x2e, 0x54, 0x06, 0x92, 0x26, 0xc0, 0x81, 0x61, 0x00, 0x40, 0x00,
	0x25, 0x68, 0x83, 0xa6, 0x34, 0x01, 0xa0, 0x01, 0x85, 0x50, 0x0a, 0x10, 0x65, 0x81, 0x87, 0x49,
	0x1a, 0x02, 0x12, 0xa1, 0x38, 0x94, 0x60, 0x48, 0x86, 0x20, 0x62, 0x08, 0x80, 0xe8, 0x09, 0x05,
	0x00, 0xf1, 0x21, 0x01, 0xc1, 0x97, 0x48, 0x60, 0xd5, 0x8a, 0x08, 0xa8, 0x10, 0x00, 0x00, 0x01,
	0x30, 0x04, 0x68, 0x08, 0x01, 0x1e, 0x80, 0x08, 0x09, 0x94, 0x07, 0x81, 0x00, 0x10, 0x90, 0x20,
	0x88, 0x12, 0x85, 0x4c, 0x56, 0x01, 0x06, 0x5d, 0x48, 0x60, 0x46, 0x02, 0x2a, 0x85, 0x40, 0x50,
	0x60, 0x10, 0x01, 0x02, 0xaf, 0xa8, 0x58, 0x0e, 0x4a, 0x60, 0xa0, 0x15, 0x11, 0x9d, 0x22, 0x28,
	0x86, 0x41, 0xb0, 0x74, 0x48, 0xa8, 0x64, 0x03, 0x02, 0x08, 0xa5, 0x02, 0x0b, 0x06, 0xb1, 0x38,
	0x09, 0x40, 0xd4, 0x07, 0x20, 0x84, 0x2a, 0x96, 0x2c, 0x93, 0x08, 0x50, 0x50, 0x69, 0x03, 0xb0,
	0x95, 0x0f, 0x70, 0x94, 0xea, 0x30, 0x00, 0x00, 0xe7, 0x02, 0x00, 0x50, 0x01, 0x19, 0x84, 0x86,
	0x10, 0x25, 0x02, 0x32, 0x88, 0xc9, 0xc3, 0x24, 0x31, 0xb8, 0x04, 0x24, 0x11, 0x01, 0x30, 0x94,
	0x11, 0x0e, 0x20, 0x49, 0x80, 0x12, 0x05, 0x10, 0xa0, 0x90, 0x9e, 0x23, 0x7c, 0x04, 0x18, 0x08,
	0x41, 0x08, 0x2c, 0x04, 0x03, 0xc2, 0x93, 0x05, 0x05, 0x08, 0x32, 0x92, 0x18, 0x03, 0xe0, 0x4c,
	0x41, 0x0a, 0x02, 0xa2, 0x9c, 0x0c, 0x16, 0x24, 0x18, 0x42, 0x49, 0x18, 0x20, 0x5a, 0x02, 0x40,
	0x24, 0x72, 0x19, 0x84, 0x82, 0x45, 0x58, 0x40, 0x29, 0x04, 0x40, 0x08, 0x04, 0x20, 0x73, 0xa0,
	0xae, 0x89, 0x59, 0x06, 0x00, 0x80, 0x30, 0x86, 0x30, 0x8d, 0x80, 0x64, 0x06, 0x62, 0x10, 0x08,
	0x46, 0x82, 0x40, 0x41, 0x3a, 0x81, 0x8c, 0x47, 0x00, 0x22, 0x42, 0x3b, 0x04, 0x44, 0x11, 0xa1,
	0x22, 0xf0, 0xc2, 0x81, 0x10, 0x44, 0x8b, 0x3a, 0x13, 0x41, 0x00, 0xb1, 0x41, 0x42, 0x68, 0x92,
	0xe1, 0x04, 0xa4, 0x53, 0x28, 0x00, 0xc4, 0x10, 0x92, 0x69, 0x40, 0x02, 0x16, 0x24, 0xf1, 0x24,
	0x2c, 0x50, 0x02, 0x4c, 0x61, 0xa0, 0xa0, 0x10, 0x0e, 0x89, 0x00, 0x41, 0x82, 0xb9, 0x98, 0x90,
	0x0e, 0x02, 0xb1, 0x18, 0x1d, 0x20, 0x98, 0x81, 0x10, 0x85, 0x00, 0x98, 0x20, 0x80, 0x02, 0x2a,
	0x00, 0x01, 0x12, 0x88, 0x03, 0x42, 0x52, 0x60, 0x70, 0x24, 0x4c, 0x1c, 0x0a, 0x34, 0x72, 0x01,
	0x22, 0x61, 0x43, 0x81, 0x10, 0x12, 0x18, 0x92, 0x20, 0x12, 0x00, 0x5a, 0x95, 0x68, 0x20, 0x84,
	0x8e, 0x45, 0x26, 0x82, 0x00, 0x8f, 0x30, 0x8c, 0x40, 0x00, 0xb2, 0x01, 0x00, 0x2c, 0x40, 0x80,
	0x0c, 0x03, 0x09, 0x80, 0x0c, 0x06, 0x6a, 0x28, 0xc4, 0xf3, 0x8d, 0x99, 0x08, 0x00, 0x04, 0x52,
	0x1a, 0xac, 0x04, 0x1b, 0x80, 0x08, 0xc0, 0x39, 0x23, 0x2c, 0x16, 0x0c, 0x62, 0x01, 0xc9, 0x02,
	0x24, 0x14, 0xe2, 0x26, 0x60, 0x81, 0x82, 0x80, 0x51, 0x48, 0x72, 0x61, 0x00, 0x92, 0x79, 0x02,
	0x86, 0x30, 0xe0, 0x48, 0x21, 0xc4, 0x13, 0x00, 0x50, 0x17, 0x28, 0x29, 0x88, 0x05, 0x09, 0x4c,
	0x84, 0x9a, 0x93, 0xc1, 0xc2, 0x61, 0x06, 0x80, 0xc3, 0x04, 0x60, 0x54, 0x0b, 0x18, 0x00, 0xd8,
	0x85, 0xc0, 0x88, 0x29, 0x38, 0x10, 0x41, 0x05, 0x50, 0x0a, 0xc6, 0x26, 0x54, 0x80, 0x10, 0x08,
	0x0a, 0x65, 0x58, 0x00, 0x81, 0x81, 0x64, 0x80, 0xa8, 0x40, 0x21, 0x91, 0x24, 0x01, 0x5c, 0x09,
	0x4c, 0x00, 0x13, 0x16, 0x18, 0x14, 0x0a, 0x42, 0xa0, 0x98, 0x00, 0x48, 0xcd, 0x08, 0x20, 0x86,
	0x11, 0x20, 0x00, 0xc4, 0x81, 0x2a, 0xb1, 0x00, 0x88, 0x24, 0x4d, 0x40, 0x0e, 0x84, 0x10, 0x0e,
	0x19, 0xda, 0x01, 0x50, 0x36, 0x18, 0x0c, 0x88, 0x86, 0x0c, 0x54, 0xc2, 0x02, 0x09, 0x0d, 0x00,
	0x21, 0x38, 0x11, 0x30, 0x88, 0x90, 0x42, 0x8f, 0x08, 0x07, 0x00, 0xae, 0x10, 0x04, 0x00, 0x2a,
	0xc6, 0x23, 0x16, 0x45, 0x50, 0x80, 0x0a, 0x50, 0x2a, 0x92, 0x95, 0x58, 0xe0, 0x28, 0x60, 0x81,
	0x18, 0x00, 0x54, 0xae, 0x10, 0xf0, 0x00, 0xa9, 0x40, 0x0d, 0x4d, 0x0a, 0x12, 0x58, 0x27, 0x10,
	0x01, 0x82, 0x2a, 0x50, 0x08, 0x24, 0x0c, 0x49, 0x68, 0x50, 0xa0, 0x48, 0x94, 0x00, 0xdb, 0x40,
	0x00, 0x00, 0x02, 0x11, 0x04, 0x86, 0x25, 0x4c, 0x03, 0x8a, 0x04, 0x04, 0x00, 0x28, 0x5c, 0x53,
	0x03, 0x25, 0x00, 0xd0, 0x26, 0x0a, 0x24, 0x28, 0x00, 0x69, 0xd8, 0x00, 0x24, 0x53, 0x50, 0x8e,
	0xc0, 0x44, 0x86, 0x0e, 0x20, 0xc1, 0x13, 0x01, 0x9c, 0x29, 0x28, 0x86, 0xc0, 0x14, 0x2d, 0x45,
	0x60, 0x02, 0x90, 0x80, 0x01, 0xe9, 0x41, 0x4c, 0x22, 0x84, 0x20, 0x92, 0xc1, 0x92, 0x0e, 0x50,
	0x16, 0x48, 0xa1, 0x08, 0xd0, 0x00, 0x20, 0xc0, 0x40, 0x09, 0x40, 0x01, 0x41, 0x6c, 0x12, 0x10,
	0x24, 0x15, 0x82, 0x8a, 0x14, 0xe0, 0xe1, 0xaa, 0xb8, 0x18, 0x45, 0x10, 0x50, 0x60, 0x21, 0x09,
	0x08, 0xc7, 0x04, 0x06, 0x18, 0x24, 0x40, 0x04, 0x21, 0x08, 0x07, 0x93, 0xb3, 0x05, 0x12, 0x20,
	0x00, 0x20, 0x91, 0x15, 0x28, 0x48, 0x29, 0x04, 0xc0, 0x18, 0x04, 0x60, 0x48, 0xa4, 0x44, 0xa2,
	0x0a, 0x08, 0x88, 0x88, 0x05, 0x26, 0x00, 0x00, 0xb4, 0xc4, 0x10, 0x8c, 0x30, 0x12, 0xe8, 0x90,
	0x10, 0x15, 0x49, 0x04, 0xa0, 0x21, 0x80, 0x70, 0x52, 0x24, 0x0a, 0xb4, 0x90, 0x05, 0x34, 0x80,
	0xc9, 0x4a, 0xd0, 0x0b, 0x09, 0x18, 0x82, 0x42, 0x7c, 0x23, 0x61, 0x0c, 0x0c, 0x88, 0x44, 0x04,
	0x93, 0xb0, 0x28, 0x3c, 0x0d, 0xa0, 0x32, 0x84, 0x6a, 0x0a, 0xa1, 0xc7, 0x06, 0x26, 0x01, 0x4a,
	0x22, 0x15, 0x44, 0x81, 0x14, 0x02, 0x19, 0x28, 0x4c, 0x00, 0x00, 0x10, 0x10, 0x82, 0x98, 0x04,
	0x02, 0x29, 0x28, 0xe0, 0x60, 0x0b, 0x01, 0x0b, 0x8a, 0x28, 0x04, 0x00, 0x85, 0x80, 0xc1, 0x08,
	0x1e, 0x76, 0x80, 0x01, 0xb0, 0x42, 0x2e, 0x0c, 0x12, 0x30, 0x09, 0x10, 0x15, 0xec, 0x40, 0xa3,
	0x02, 0x81, 0x01, 0x90, 0x8d, 0x44, 0xa3, 0xd1, 0x04, 0x14, 0x00, 0x67, 0x00, 0xc2, 0x80, 0x02,
	0x60, 0x8f, 0xc4, 0x06, 0x90, 0x18, 0x34, 0xc8, 0x8d, 0x06, 0x20, 0x01, 0x30, 0x20, 0x3c, 0x08,
	0x40, 0x00, 0xe4, 0x49, 0x12, 0xcc, 0x08, 0x41, 0x02, 0xf0, 0x08, 0x31, 0x04, 0x02, 0x40, 0x00,
	0x04, 0x21, 0x84, 0xa1, 0xd2, 0x43, 0x42, 0x84, 0x09, 0x03, 0x38, 0xda, 0x01, 0x18, 0x24, 0x80,
	0x83, 0x31, 0x92, 0xac, 0x00, 0xb0, 0x4b, 0x00, 0xe1, 0xc1, 0x08, 0x44, 0x03, 0x03, 0x00, 0x81,
	0x14, 0x82, 0x04, 0x12, 0x60, 0x09, 0x44, 0x11, 0x23, 0x46, 0x24, 0xa0, 0x2c, 0x05, 0x82, 0x82,
	0x08, 0xc6, 0x3b, 0xb4, 0x40, 0x04, 0x26, 0x28, 0x15, 0x60, 0x87, 0x10, 0x83, 0x80, 0x04, 0x02,
	0x11, 0x2e, 0x1c, 0x48, 0x4d, 0x74, 0x65, 0xc2, 0x92, 0x10, 0x0d, 0x29, 0x08, 0x00, 0x01, 0x01,
	0x14, 0xc3, 0x82, 0x38, 0x93, 0x62, 0x00, 0x00, 0x58, 0x68, 0x10, 0x45, 0x03, 0x15, 0x40, 0x04,
	0xcd, 0x06, 0x94, 0xb0, 0x00, 0x01, 0x42, 0xe4, 0x0e, 0x42, 0x5a, 0x29, 0xb4, 0x0e, 0xe4, 0x60,
	0x10, 0x02, 0x08, 0xd0, 0x80, 0x0c, 0x40, 0x17, 0x82, 0xa5, 0x58, 0x00, 0x60, 0x1e, 0x11, 0x39,
	0x3b, 0x85, 0x00, 0x06, 0x76, 0x05, 0x00, 0xb0, 0xe8, 0x00, 0x8b, 0x40, 0x80, 0x38, 0x00, 0x00,
	0x50, 0x60, 0x20, 0x04, 0x13, 0x19, 0x28, 0x00, 0x44, 0x06, 0x11, 0x20, 0x1e, 0x30, 0x45, 0x0a,
	0x02, 0xf1, 0x8a, 0x12, 0x8c, 0x00, 0x68, 0x50, 0x70, 0x10, 0x0d, 0x81, 0x48, 0x24, 0x12, 0x04,
	0x20, 0x00, 0x11, 0x80, 0x2a, 0x58, 0x02, 0xcb, 0x81, 0xa0, 0x06, 0x47, 0x00, 0x44, 0xb1, 0x88,
	0x01, 0x42, 0x48, 0x28, 0xe3, 0x22, 0x00, 0x31, 0x51, 0x06, 0x2e, 0x90, 0xc1, 0x10, 0xc1, 0x49,
	0x28, 0x50, 0x64, 0x0b, 0x01, 0x00, 0x94, 0x80, 0x08, 0x82, 0x98, 0x38, 0x09, 0x55, 0xa1, 0x24,
	0x00, 0x01, 0x00, 0xe1, 0x44, 0x20, 0x1a, 0x13, 0x10, 0x11, 0x04, 0x5a, 0xc4, 0x00, 0x01, 0xc0,
	0x26, 0x0c, 0x05, 0x01, 0x6e, 0x32, 0x01, 0x09, 0xd9, 0x89, 0x82, 0x52, 0xa4, 0x62, 0x0c, 0x81,
	0x10, 0xcb, 0x10, 0x70, 0x61, 0x84, 0x24, 0x00, 0x03, 0x60, 0x80, 0xa0, 0xa8, 0x50, 0x48, 0x80,
	0x44, 0x63, 0x08, 0x84, 0x08, 0x46, 0xc1, 0x52, 0xc4, 0x81, 0x8c, 0x49, 0x92, 0x04, 0x28, 0x10,
	0x53, 0x22, 0x60, 0x45, 0x01, 0x5e, 0x25, 0xd8, 0x10, 0x58, 0x00, 0x6d, 0x02, 0x84, 0x03, 0xa1,
	0x3c, 0x48, 0x24, 0x32, 0x04, 0x68, 0x01, 0x20, 0x09, 0xc5, 0x00, 0xc1, 0x43, 0x8e, 0x30, 0x42,
	0x2c, 0x42, 0xd5, 0xb8, 0x11, 0x00, 0xc2, 0x40, 0x0a, 0x04, 0x2a, 0x30, 0x30, 0x82, 0x4c, 0x00,
	0xb3, 0xc0, 0x26, 0x0c, 0x04, 0xe2, 0x20, 0x10, 0xa1, 0x00, 0x90, 0x93, 0x21, 0x28, 0x45, 0x10,
	0x30, 0x15, 0x8f, 0x44, 0x46, 0x50, 0x20, 0x0d, 0x8d, 0x01, 0x28, 0x30, 0x65, 0x58, 0x92, 0xe0,
	0xc0, 0x8a, 0x08, 0x20, 0x92, 0x0c, 0x20, 0x11, 0x85, 0x42, 0x14, 0x42, 0x08, 0xc1, 0x88, 0xe0,
	0x22, 0x13, 0x00, 0x2c, 0x61, 0xd8, 0x49, 0x0a, 0x64, 0xc0, 0x04, 0x80, 0x53, 0x64, 0x04, 0x85,
	0xd1, 0x80, 0x59, 0x12, 0x80, 0x08, 0x82, 0x61, 0x09, 0x11, 0x44, 0x20, 0x0e, 0x00, 0x60, 0x0d,
	0x19, 0x08, 0x2c, 0x04, 0xa0, 0x10, 0x38, 0x84, 0x03, 0x8e, 0x44, 0x23, 0x98, 0x18, 0x00, 0x44,
	0x41, 0x50, 0x53, 0x78, 0x88, 0x09, 0x45, 0x86, 0x60, 0x30, 0x20, 0x00, 0x29, 0x00, 0xc3, 0x52,
	0x05, 0x9a, 0x28, 0xc9, 0x15, 0xc2, 0x10, 0x63, 0x50, 0xb2, 0x20, 0x04, 0x22, 0x0a, 0x86, 0x09,
	0x0c, 0x45, 0x03, 0x04, 0x32, 0xd0, 0x02, 0xa6, 0x30, 0x18, 0x28, 0x68, 0x02, 0x02, 0x0a, 0x30,
	0x51, 0x02, 0x08, 0x06, 0x60, 0x10, 0xd1, 0xc1, 0x82, 0x06, 0xd1, 0xa2, 0x0b, 0xf9, 0x00, 0x2e,
	0x18, 0xa4, 0x02, 0x80, 0x4c, 0x58, 0xa3, 0x40, 0x10, 0x80, 0x04, 0x1c, 0x52, 0x03, 0x22, 0x41,
	0x81, 0x21, 0x04, 0x06, 0x0c, 0x10, 0x60, 0x00, 0x02, 0x55, 0x13, 0x60, 0x14, 0x00, 0x38, 0x18,
	0x01, 0x00, 0x45, 0x02, 0x10, 0x53, 0x31, 0x48, 0x08, 0x20, 0x06, 0xa4, 0x48, 0xaf, 0x40, 0x47,
	0x41, 0x60, 0x00, 0x21, 0x1a, 0x28, 0x82, 0x68, 0x74, 0x22, 0x81, 0x9e, 0x18, 0xc9, 0xe4, 0x74,
	0xa4, 0x43, 0x01, 0x64, 0x12, 0x23, 0x56, 0xb1, 0x50, 0x00, 0xc0, 0x00, 0x06, 0x00, 0x64, 0xc1,
	0xa4, 0x80, 0x03, 0x40, 0x28, 0xc1, 0x80, 0x19, 0x51, 0x04, 0xc1, 0x40, 0x41, 0x90, 0x9d, 0xa1,
	0x0c, 0x04, 0x0e, 0x64, 0x10, 0x00, 0x14, 0x92, 0x40, 0x42, 0x61, 0xa1, 0x90, 0xd0, 0x08, 0x0d,
	0x30, 0x22, 0x08, 0x00, 0x44, 0x07, 0xe2, 0x34, 0xe4, 0x41, 0x98, 0x84, 0x08, 0x41, 0x08, 0x05,
	0x22, 0x0f, 0x30, 0xc0, 0x44, 0x08, 0x13, 0x28, 0x06, 0x80, 0x11, 0x29, 0x48, 0x10, 0x0a, 0x3d,
	0x89, 0x10, 0xc2, 0x40, 0x32, 0x10, 0x04, 0xa4, 0x08, 0xcc, 0x34, 0x55, 0x03, 0xac, 0xd0, 0xc2,
	0x80, 0x02, 0x86, 0x0a, 0xa5, 0x50, 0x54, 0x20, 0x50, 0x04, 0xaa, 0x02, 0x6c, 0x5a, 0xa2, 0x2c,
	0x51, 0xfb, 0x00, 0x39, 0x03, 0x09, 0x40, 0x04, 0x92, 0xb4, 0x0d, 0x44, 0x05, 0x10, 0x86, 0x89,
	0x28, 0x21, 0x42, 0x47, 0x0a, 0x12, 0x68, 0x1e, 0xa0, 0x81, 0x02, 0x20, 0xd0, 0x61, 0x2f, 0x00,
	0x44, 0x25, 0x20, 0x04, 0x02, 0x0a, 0x98, 0x9a, 0xa0, 0x20, 0x04, 0x00, 0x24, 0x2c, 0x80, 0x02,
	0x00, 0x02, 0x48, 0x08, 0x14, 0x92, 0xa0, 0x00, 0x00, 0x52, 0x19, 0x49, 0x92, 0x41, 0x00, 0x44,
	0x20, 0x82, 0x44, 0x01, 0x81, 0x4c, 0x57, 0x98, 0x07, 0xe0, 0x96, 0x20, 0x42, 0x31, 0x02, 0xb0,
	0x51, 0x0d, 0x69, 0x6c, 0x85, 0x68, 0x24, 0x08, 0x05, 0x04, 0x26, 0xc4, 0xd8, 0x92, 0x85, 0x07,
	0x86, 0x38, 0x61, 0x40, 0x82, 0x80, 0x08, 0x02, 0x10, 0x34, 0x02, 0x3d, 0x30, 0x82, 0x0c, 0x40,
	0x05, 0x2a, 0x08, 0x50, 0x00, 0x40, 0x18, 0x10, 0x60, 0x15, 0x21, 0x13, 0x60, 0x02, 0x91, 0x18,
	0xa4, 0x81, 0x0a, 0x8a, 0x48, 0x14, 0x70, 0x94, 0x81, 0x4b, 0xc0, 0x0e, 0xb5, 0x90, 0x1a, 0x14,
	0x48, 0x00, 0x40, 0x12, 0x48, 0x26, 0x6c, 0x00, 0x84, 0x18, 0x10, 0x00, 0x85, 0x70, 0x82, 0xe8,
	0x42, 0xa0, 0x22, 0x80, 0x11, 0x40, 0x2b, 0x02, 0x96, 0xc9, 0x88, 0x58, 0x84, 0x0f, 0x30, 0xc5,
	0x28, 0x36, 0xe0, 0xc6, 0x4a, 0x10, 0x15, 0x00, 0x18, 0x80, 0x51, 0x08, 0x20, 0x82, 0xb1, 0x11,
	0xc0, 0x10, 0x09, 0x12, 0x22, 0x03, 0x21, 0x69, 0x44, 0x0e, 0x40, 0x00, 0x09, 0x02, 0x50, 0x98,
	0x4f, 0x00, 0x00, 0xf1, 0x1b, 0x00, 0xc2, 0x28, 0x46, 0x73, 0x82, 0x02, 0x01, 0x09, 0x83, 0x00,
	0x81, 0x42, 0x93, 0x0c, 0xc1, 0x02, 0x16, 0x41, 0x80, 0x83, 0x00, 0x50, 0x02, 0x60, 0x00, 0xd2,
	0x14, 0xa0, 0x11, 0x2a, 0x70, 0x80, 0x00, 0x03, 0xe1, 0x8b, 0x80, 0x10, 0x95, 0x08, 0x21, 0x51,
	0x8f, 0x00, 0x3a, 0x21, 0x83, 0x8a, 0x20, 0x94, 0x40, 0x4c, 0x73, 0x03, 0x07, 0xa8, 0x08, 0x80,
	0x18, 0x06, 0xc0, 0x09, 0x45, 0x40, 0x24, 0x10, 0xa0, 0x42, 0xa2, 0xa4, 0x08, 0x04, 0x18, 0x42,
	0x41, 0x10, 0x59, 0x01, 0x88, 0x66, 0x05, 0x1a, 0x0f, 0x14, 0x86, 0x81, 0x66, 0xd0, 0x39, 0x00,
	0x0c, 0x80, 0x00, 0x70, 0x11, 0x21, 0x12, 0x08, 0xc0, 0xe1, 0x0a, 0x60, 0x09, 0x9c, 0x91, 0x0c,
	0xe2, 0x64, 0x25, 0x5a, 0x90, 0x10, 0x08, 0x89, 0x3c, 0x74, 0x71, 0x24, 0x44, 0x05, 0x46, 0x08,
	0x00, 0x80, 0x80, 0xa8, 0xd9, 0x01, 0x1c, 0xb6, 0xb1, 0x8a, 0x10, 0x09, 0xaa, 0x56, 0x82, 0x58,
	0x00, 0x01, 0x94, 0x2d, 0x14, 0x21, 0xa2, 0x03, 0x85, 0x94, 0x20, 0x28, 0x41, 0x52, 0x80, 0xc5,
	0x01, 0x23, 0x08, 0x25, 0x20, 0xb1, 0x40, 0x08, 0x03, 0x00, 0x05, 0xe0, 0x0c, 0x08, 0xc2, 0xa0,
	0x06, 0x25, 0xaa, 0x05, 0x54, 0x1f, 0x08, 0x28, 0x60, 0x00, 0x18, 0xed, 0x50, 0x02, 0x02, 0x71,
	0x10, 0x28, 0x21, 0x41, 0x6d, 0x10, 0x90, 0x70, 0x22, 0x25, 0x83, 0x81, 0x50, 0xb3, 0xe0, 0x3c,
	0x98, 0x06, 0x20, 0x10, 0x55, 0x4b, 0x90, 0xc1, 0xd0, 0xc1, 0x40, 0x02, 0x11, 0x00, 0xb4, 0x40,
	0x8c, 0x10, 0x00, 0x4a, 0xa2, 0x90, 0x0c, 0x64, 0x20, 0x10, 0xc3, 0x9b, 0x19, 0x90, 0x01, 0x00,
	0x12, 0x28, 0x81, 0x30, 0x0e, 0x86, 0x22, 0x46, 0x99, 0x81, 0x44, 0x1a, 0x08, 0x0c, 0x05, 0xa1,
	0x10, 0xf0, 0x0c, 0x21, 0x00, 0xe1, 0x32, 0x00, 0x58, 0x08, 0xe8, 0x00, 0x62, 0x83, 0x18, 0x00,
	0x82, 0x47, 0x12, 0x62, 0x59, 0x01, 0xa0, 0x19, 0x80, 0x00, 0x50, 0xa8, 0x38, 0x80, 0x50, 0x48,
	0x12, 0xe0, 0x43, 0x18, 0xc8, 0xc4, 0x07, 0x40, 0x02, 0xa0, 0x01, 0x91, 0x05, 0xa8, 0x48, 0x00,
	0xc3, 0x0d, 0x09, 0x86, 0xe2, 0x54, 0x50, 0x10, 0x01, 0x91, 0x1a, 0x48, 0x0c, 0x71, 0x10, 0xa9,
	0x25, 0x80, 0x84, 0x04, 0x95, 0x48, 0x1f, 0x00, 0xd0, 0x28, 0x04, 0x30, 0x01, 0x27, 0x64, 0x88,
	0xe8, 0x0c, 0x06, 0x18, 0xb2, 0xd4, 0x0c, 0xe7, 0x22, 0x06, 0x00, 0x00, 0xac, 0x00, 0x28, 0x12,
	0x30, 0x48, 0x38, 0x11, 0x09, 0x8e, 0x42, 0x20, 0xa0, 0x24, 0x10, 0x8e, 0x09, 0x32, 0x10, 0x58,
	0x09, 0xd8, 0x93, 0x88, 0x44, 0x32, 0x10, 0x00, 0x84, 0x14, 0x84, 0x12, 0x21, 0x2a, 0xa6, 0x34,
	0x82, 0x01, 0x24, 0x43, 0x42, 0x10, 0x01, 0x42, 0x69, 0x00, 0x11, 0xb0, 0x13, 0x48, 0x81, 0x23,
	0x44, 0xd2, 0x2a, 0x18, 0x48, 0xd3, 0x04, 0x22, 0x70, 0xa1, 0x04, 0xb0, 0x88, 0x63, 0x06, 0x01,
	0x0a, 0x30, 0x81, 0x54, 0x40, 0x60, 0xd0, 0x08, 0x80, 0x30, 0x80, 0x64, 0x1e, 0x04, 0x41, 0x20,
	0x30, 0x0a, 0x41, 0x70, 0xe6, 0xc1, 0x19, 0x8c, 0x4e, 0x08, 0x54, 0x11, 0xca, 0x80, 0x20, 0x84,
	0x4e, 0x02, 0x30, 0x4b, 0x04, 0x0c, 0x80, 0x09, 0x40, 0xc0, 0x18, 0x29, 0x81, 0x00, 0x0d, 0x64,
	0xc6, 0x81, 0x02, 0x00, 0x13, 0x88, 0x60, 0x11, 0x10, 0x2e, 0x48, 0x0e, 0x43, 0x72, 0x50, 0x0a,
	0x8a, 0x84, 0xc5, 0x22, 0x20, 0x11, 0x20, 0x83, 0xa1, 0x01, 0x2a, 0x5c, 0x06, 0x32, 0x0c, 0x48,
	0x49, 0x8d, 0x42, 0x86, 0x98, 0x08, 0x01, 0x83, 0x43, 0x30, 0x82, 0x50, 0x98, 0x81, 0x49, 0x0a,
	0x22, 0x41, 0x6a, 0x84, 0x04, 0x0b, 0x0c, 0x1c, 0x03, 0x42, 0x80, 0x30, 0x56, 0xc1, 0x1c, 0x06,
	0x80, 0x30, 0x00, 0x04, 0xa1, 0x0a, 0x23, 0xa0, 0x01, 0x45, 0x10, 0x41, 0x28, 0x90, 0x81, 0x17,
	0x5c, 0x89, 0x04, 0x2c, 0x90, 0x00, 0x23, 0x4d, 0xd0, 0x05, 0x20, 0x02, 0x90, 0x8a, 0x25, 0x8a,
	0x22, 0x2a, 0x05, 0x30, 0x11, 0xd4, 0x06, 0xa2, 0x44, 0x65, 0x0a, 0x10, 0xc8, 0x05, 0x63, 0x10,
	0x50, 0x88, 0x2a, 0x01, 0xc4, 0x29, 0x28, 0x10, 0x20, 0x15, 0x51, 0x58, 0x84, 0x00, 0x44, 0x42,
	0x08, 0x80, 0x84, 0x41, 0x00, 0x32, 0xd2, 0x94, 0x10, 0x0c, 0x20, 0x04, 0x06, 0xea, 0x14, 0x60,
	0xc2, 0x6a, 0x02, 0x92, 0x21, 0x80, 0xe8, 0x10, 0x0b, 0x44, 0xc5, 0x60, 0x2f, 0x1d, 0xd0, 0x84,
	0x08, 0x20, 0x02, 0x84, 0x29, 0x18, 0x06, 0x42, 0x41, 0x50, 0x14, 0x48, 0x02, 0x86, 0x56, 0x21,
	0x48, 0x14, 0x01, 0xdb, 0x82, 0x24, 0x80, 0x5b, 0xae, 0x3c, 0x40, 0x80, 0x22, 0x10, 0x53, 0x02,
	0xad, 0x0a, 0x20, 0x08, 0x02, 0x18, 0xa5, 0x80, 0x1c, 0x42, 0x60, 0x70, 0xc1, 0x01, 0x0c, 0x96,
	0x2c, 0x0e, 0x90, 0x08, 0x09, 0x81, 0x50, 0x2d, 0x26, 0x04, 0xaa, 0x29, 0xc5, 0x01, 0x46, 0x5c,
	0x10, 0x53, 0x2b, 0xa8, 0x84, 0x00, 0x38, 0x53, 0x00, 0x01, 0x15, 0x98, 0x86, 0x0a, 0xc2, 0xaa,
	0x89, 0x60, 0x4e, 0xa0, 0x52, 0x04, 0x73, 0x8c, 0x40, 0x8a, 0x08, 0x16, 0x50, 0xa2, 0xb1, 0x24,
	0x0a, 0x69, 0x42, 0x05, 0x71, 0x24, 0xa0, 0x11, 0x42, 0x04, 0x90, 0xc1, 0x0a, 0x18, 0x08, 0xc9,
	0x26, 0x80, 0x01, 0x20, 0x35, 0x4a, 0x08, 0x58, 0xd0, 0x00, 0x91, 0x00, 0x47, 0x04, 0x76, 0x10,
	0x00, 0x83, 0x08, 0x80, 0x89, 0x60, 0x34, 0x2a, 0x0d, 0x88, 0x03, 0x86, 0x02, 0x82, 0x80, 0x0c,
	0x45, 0x82, 0x01, 0x60, 0x35, 0x60, 0x00, 0x01, 0x03, 0x6c, 0x48, 0x86, 0x90, 0x19, 0x08, 0x09,
	0x00, 0x18, 0x54, 0x01, 0x83, 0xe0, 0x47, 0x04, 0x14, 0x40, 0xb9, 0x80, 0x1c, 0x50, 0xaa, 0x02,
	0x03, 0x80, 0x00, 0x50, 0x50, 0xa8, 0x10, 0xb3, 0x00, 0x38, 0x44, 0x04, 0xe2, 0x28, 0x10, 0x20,
	0x2c, 0x60, 0x18, 0x40, 0x40, 0xa3, 0x41, 0x16, 0x0c, 0x09, 0x40, 0x20, 0x31, 0x31, 0x04, 0x10,
	0x19, 0x45, 0x22, 0xa0, 0x08, 0x28, 0x84, 0x45, 0xe0, 0x08, 0x21, 0xe0, 0x83, 0x04, 0x10, 0x43,
	0x54, 0x01, 0x82, 0x08, 0x80, 0x82, 0x46, 0x22, 0xe4, 0x10, 0x80, 0x70, 0x02, 0x81, 0x38, 0x51,
	0x33, 0x0e, 0x20, 0xcc, 0x28, 0x12, 0x11, 0x91, 0x26, 0xe1, 0x0c, 0x09, 0x18, 0x63, 0x40, 0x00,
	0x68, 0x19, 0x8a, 0x24, 0x50, 0x4a, 0x20, 0x25, 0x0d, 0x24, 0x2c, 0x64, 0x70, 0x0e, 0x45, 0x1c,
	0x0c, 0x08, 0x94, 0x83, 0x30, 0x28, 0xd7, 0x24, 0x40, 0x52, 0x70, 0x07, 0x20, 0x06, 0x00, 0x52,
	0x00, 0x20, 0x29, 0x60, 0x40, 0x84, 0x52, 0x40, 0x12, 0x00, 0x99, 0x10, 0x8f, 0x48, 0x11, 0x01,
	0x9d, 0x8c, 0x80, 0x22, 0x0e, 0x83, 0x32, 0x20, 0x4d, 0xc4, 0x03, 0x10, 0x46, 0x22, 0x22, 0xa1,
	0x41, 0x08, 0x3c, 0x10, 0xa0, 0x94, 0xc1, 0x91, 0x08, 0x70, 0x62, 0x1b, 0xaa, 0x31, 0x0c, 0x83,
	0x50, 0x17, 0xc9, 0x28, 0xc9, 0x01, 0x01, 0x0c, 0x04, 0x68, 0x30, 0x34, 0x40, 0x02, 0x00, 0xc2,
	0x43, 0x01, 0x00, 0x13, 0x08, 0x18, 0x00, 0xe2, 0x1f, 0x81, 0x14, 0xaa, 0x4c, 0x30, 0x98, 0x20,
	0x20, 0x46, 0xc7, 0x0c, 0x02, 0xc1, 0x80, 0x60, 0x00, 0x81, 0x18, 0x10, 0xe0, 0x2a, 0x49, 0xc8,
	0x41, 0x02, 0x26, 0xa2, 0x08, 0x25, 0x8f, 0x20, 0x72, 0x41, 0x1a, 0x00, 0xe1, 0x08, 0x00, 0x1e,
	0x20, 0x80, 0x84, 0xc8, 0x01, 0x03, 0x60, 0x43, 0xb9, 0x18, 0x41, 0x10, 0x8c, 0x24, 0xe6, 0x08,
	0x8a, 0xbc, 0x02, 0x00, 0x36, 0x41, 0x58, 0xa8, 0xa0, 0xc1, 0x00, 0x02, 0x12, 0x80, 0x83, 0x28,
	0x18, 0x65, 0x70, 0xa0, 0x00, 0x01, 0x18, 0x08, 0xa8, 0x22, 0x94, 0x52, 0x30, 0x01, 0x06, 0x04,
	0x40, 0xc0, 0x23, 0x07, 0x35, 0x00, 0xaa, 0x10, 0x44, 0x52, 0x09, 0x0c, 0x58, 0xe3, 0x0c, 0x80,
	0x10, 0x10, 0x78, 0x00, 0x41, 0x44, 0x41, 0xc1, 0x04, 0x29, 0x99, 0xaa, 0x00, 0x30, 0xc1, 0x10,
	0x05, 0x54, 0x29, 0x50, 0xc4, 0x29, 0x84, 0x60, 0x08, 0x44, 0x22, 0x24, 0xd0, 0x21, 0xc8, 0x04,
	0x48, 0x0c, 0x25, 0x00, 0x30, 0x40, 0xc6, 0x83, 0x68, 0x36, 0x82, 0x07, 0x1c, 0x89, 0x29, 0x02,
	0x22, 0x90, 0x02, 0x41, 0x85, 0x88, 0x0e, 0x02, 0x01, 0x04, 0x58, 0x11, 0x6e, 0x00, 0xb1, 0x69,
	0x00, 0x75, 0x94, 0x08, 0x10, 0x02, 0x70, 0x23, 0x95, 0x42, 0xe3, 0x28, 0x82, 0x10, 0x89, 0x14,
	0x10, 0x02, 0x0c, 0x80, 0x11, 0x04, 0x21, 0x12, 0x02, 0x66, 0x01, 0x6b, 0x21, 0xc8, 0x0a, 0xc0,
	0x24, 0x01, 0xe0, 0x12, 0xdc, 0x04, 0x4e, 0x20, 0x06, 0x0b, 0xb2, 0x90, 0x47, 0x05, 0x0a, 0x05,
	0x41, 0xb1, 0x44, 0x41, 0x01, 0x58, 0x94, 0x02, 0x32, 0x9c, 0xc8, 0x28, 0x04, 0x25, 0x50, 0x86,
	0x20, 0x06, 0x8b, 0x4c, 0x10, 0x10, 0x0d, 0x8c, 0x40, 0xc4, 0x12, 0x10, 0x08, 0x24, 0x20, 0x80,
	0x05, 0x44, 0x12, 0x42, 0x2c, 0xd9, 0x00, 0x4a, 0x04, 0xa2, 0x80, 0xa4, 0x24, 0x0d, 0x46, 0x2e,
	0xc4, 0x7b, 0x33, 0x80, 0x54, 0x04, 0x6a, 0x00, 0x00, 0x00, 0x21, 0x40, 0xaa, 0x10, 0xe1, 0xe0,
	0xa4, 0x10, 0x51, 0x01, 0x06, 0x90, 0x90, 0xa0, 0xe8, 0x80, 0x05, 0x30, 0x71, 0x28, 0x14, 0xe5,
	0x82, 0x43, 0x10, 0xa4, 0x42, 0x90, 0x8c, 0x84, 0x01, 0x58, 0x10, 0x1a, 0x01, 0xb9, 0x06, 0x80,
	0x68, 0x82, 0x40, 0x0d, 0x08, 0x14, 0x2a, 0x4c, 0x41, 0xf8, 0xa2, 0x01, 0x10, 0x22, 0x00, 0xa0,
	0x20, 0x90, 0xd4, 0x8a, 0x44, 0x0a, 0x52, 0x90, 0xa7, 0x70, 0x41, 0x05, 0x40, 0x01, 0x08, 0x07,
	0x80, 0x4d, 0x04, 0x60, 0x40, 0xc8, 0x00, 0x58, 0x94, 0xa0, 0x34, 0x02, 0xe8, 0x08, 0x00, 0x5d,
	0x02, 0x04, 0xe1, 0x52, 0x80, 0x28, 0x88, 0x07, 0x1e, 0xd0, 0x18, 0x9d, 0x30, 0x5e, 0x84, 0x28,
	0x60, 0x01, 0xbc, 0xb4, 0x0a, 0x00, 0x0e, 0x00, 0x41, 0x10, 0x21, 0x48, 0x80, 0x02, 0x30, 0x51,
	0x1c, 0x81, 0x00, 0x68, 0x0a, 0xc1, 0x20, 0x2d, 0x15, 0x0c, 0x61, 0x00, 0xb0, 0xd0, 0x11, 0x11,
	0x51, 0x00, 0x60, 0x70, 0x80, 0xb2, 0x69, 0xc5, 0x81, 0x44, 0x82, 0x03, 0x00, 0x8d, 0x41, 0xa8,
	0x42, 0x01, 0xaa, 0x82, 0x80, 0x44, 0x4b, 0x72, 0x00, 0x0b, 0xb2, 0x00, 0x18, 0x26, 0x54, 0xc0,
	0xc8, 0xa0, 0x54, 0x89, 0x2a, 0x0a, 0x82, 0x29, 0x1b, 0x51, 0x04, 0xc2, 0x52, 0x01, 0xe1, 0x3b,
	0x04, 0x48, 0x24, 0x40, 0x83, 0xc1, 0x04, 0x20, 0x12, 0xce, 0x00, 0xa0, 0x03, 0x04, 0x08, 0x01,
	0x08, 0x50, 0xd0, 0xb0, 0x08, 0xa4, 0x04, 0x26, 0x00, 0x84, 0xa2, 0x92, 0x00, 0x41, 0xcc, 0x62,
	0x80, 0x28, 0x15, 0x61, 0xc4, 0x41, 0x1c, 0xd1, 0x3a, 0x32, 0xc1, 0x09, 0x22, 0x08, 0x51, 0x90,
	0x05, 0x0d, 0x13, 0x80, 0x50, 0x00, 0x8a, 0x18, 0x48, 0x00, 0x60, 0x24, 0xe4, 0x42, 0x02, 0xc4,
	0x07, 0x42, 0x04, 0x11, 0x41, 0x82, 0x40, 0x00, 0x0d, 0x3a, 0xc0, 0x42, 0x94, 0x9c, 0xc2, 0x44,
	0x2e, 0x47, 0x70, 0x98, 0x60, 0x09, 0x2a, 0x60, 0x00, 0x33, 0x25, 0x5c, 0x14, 0x20, 0x00, 0x11,
	0x01, 0x08, 0x28, 0x0c, 0x2a, 0x24, 0xc0, 0x12, 0x09, 0x94, 0x0b, 0x8b, 0x22, 0xc0, 0x10, 0x16,
	0x70, 0x80, 0x04, 0x30, 0x00, 0xc9, 0xa6, 0xa8, 0x59, 0xa2, 0x02, 0x10, 0x1a, 0x01, 0x94, 0xc7,
	0xc1, 0x42, 0xa0, 0x18, 0x32, 0x18, 0x55, 0x20, 0x40, 0x63, 0x00, 0x9a, 0x24, 0xc1, 0x08, 0x00,
	0x32, 0x29, 0x00, 0x45, 0x41, 0x89, 0x58, 0x72, 0x80, 0x00, 0x90, 0x1c, 0x44, 0x00, 0x12, 0x18,
	0x02, 0x50, 0x08, 0x46, 0x2c, 0x80, 0xcb, 0x30, 0x59, 0x91, 0xec, 0x08, 0x00, 0xc2, 0x09, 0x89,
	0x12, 0x40, 0x0c, 0x20, 0x51, 0x02, 0x1c, 0x10, 0x05, 0x02, 0x60, 0xa8, 0xa0, 0x08, 0x0e, 0xa2,
	0x4c, 0x96, 0xc0, 0x9c, 0x84, 0x10, 0x82, 0x52, 0x00, 0x31, 0x20, 0xe5, 0x55, 0x28, 0x74, 0x80,
	0x18, 0x22, 0x24, 0x04, 0x0a, 0x02, 0xe1, 0x40, 0xa3, 0x99, 0x05, 0xe6, 0x08, 0x91, 0x50, 0x03,
	0x14, 0x8e, 0xc1, 0x60, 0x24, 0xeb, 0x12, 0xa0, 0x02, 0x60, 0x60, 0x40, 0x2a, 0x91, 0xf8, 0x80,
	0x2c, 0x14, 0x22, 0x48, 0x05, 0x34, 0x91, 0x2c, 0x12, 0xa2, 0x51, 0x81, 0x01, 0x9c, 0x62, 0x08,
	0xd1, 0xe1, 0x10, 0x85, 0x50, 0x2e, 0x20, 0x15, 0x80, 0x02, 0x00, 0x16, 0xe6, 0x72, 0x81, 0x13,
	0xa5, 0x40, 0xc2, 0x02, 0x02, 0x64, 0x09, 0x80, 0x65, 0x40, 0x80, 0x04, 0x44, 0x20, 0x30, 0x98,
	0x11, 0xc5, 0x32, 0x11, 0x50, 0x31, 0x08, 0x80, 0x04, 0x22, 0x77, 0x20, 0x0a, 0x24, 0x98, 0x00,
	0x00, 0x20, 0x48, 0xb3, 0x08, 0x88, 0x04, 0x24, 0xb3, 0x0a, 0x96, 0x00, 0x83, 0xa0, 0x34, 0x02,
	0x50, 0x39, 0x68, 0x04, 0x48, 0x14, 0x00, 0x00, 0x03, 0x04, 0x0a, 0xc9, 0x58, 0x01, 0x22, 0x0b,
	0x81, 0x49, 0x41, 0x2e, 0x25, 0x02, 0x22, 0x01, 0x90, 0x63, 0x60, 0x04, 0x80, 0x14, 0x80, 0x45,
	0x06, 0x3a, 0x05, 0x50, 0x23, 0x64, 0x4a, 0x89, 0x06, 0x03, 0x91, 0x84, 0xb4, 0x40, 0x88, 0x52,
	0x61, 0x10, 0x89, 0x1c, 0x40, 0x87, 0x20, 0x82, 0x61, 0x12, 0x84, 0x9a, 0x89, 0x40, 0x42, 0x68,
	0x00, 0x00, 0x9a, 0x21, 0x20, 0x11, 0xda, 0x84, 0x80, 0x46, 0x45, 0x00, 0x14, 0x01, 0x08, 0xb4,
	0x05, 0x24, 0x0e, 0xa1, 0xa2, 0x20, 0x44, 0x80, 0x49, 0x48, 0x06, 0x40, 0xa2, 0xc0, 0x51, 0xc4,
	0x68, 0x34, 0x32, 0x00, 0x3c, 0x10, 0x44, 0x1c, 0x11, 0x30, 0x08, 0x00, 0x4e, 0xa8, 0x30, 0x15,
	0xd8, 0x31, 0x28, 0x80, 0x28, 0x04, 0x46, 0x08, 0x20, 0xa1, 0x04, 0x42, 0x12, 0xd2, 0x0a, 0x31,
	0x01, 0x02, 0x00, 0x04, 0x20, 0x01, 0xac, 0x65, 0x14, 0x0d, 0x58, 0x51, 0x03, 0x10, 0x00, 0x15,
	0x08, 0x04, 0x46, 0x89, 0x8f, 0xf0, 0x88, 0x06, 0x08, 0x20, 0xb1, 0x09, 0x68, 0x52, 0x2a, 0x02,
	0x33, 0x82, 0x82, 0xa8, 0x08, 0xe4, 0x60, 0x25, 0x03, 0xac, 0x11, 0x00, 0x85, 0x40, 0xb1, 0x92,
	0x86, 0x01, 0xc2, 0x03, 0x28, 0x42, 0x30, 0x17, 0x28, 0xd6, 0xa0, 0x26, 0x00, 0x10, 0x00, 0x59,
	0x12, 0x08, 0x0a, 0xc2, 0x61, 0x80, 0x95, 0x08, 0x43, 0x62, 0x90, 0x40, 0x31, 0x90, 0x00, 0x04,
	0x2a, 0x92, 0x08, 0x80, 0x21, 0x57, 0x0e, 0x30, 0x00, 0x40, 0x22, 0x24, 0x03, 0x45, 0x60, 0x62,
	0xa0, 0x90, 0x41, 0x44, 0x00, 0x18, 0x90, 0x98, 0x15, 0x20, 0x05, 0x00, 0x4a, 0x43, 0x62, 0x00,
	0x08, 0x90, 0x80, 0x28, 0x00, 0xb1, 0x3f, 0x40, 0x58, 0x4d, 0x24, 0x45, 0x22, 0x18, 0x04, 0x02,
	0x26, 0x6a, 0x10, 0x10, 0x84, 0xcc, 0x85, 0x24, 0x28, 0x14, 0x12, 0x94, 0x41, 0x81, 0x60, 0x70,
	0x01, 0x88, 0x80, 0x05, 0x01, 0x23, 0x40, 0x34, 0x8a, 0x0b, 0x20, 0x89, 0x40, 0x36, 0x01, 0x21,
	0x00, 0x25, 0x58, 0x09, 0x40, 0x91, 0x0b, 0x18, 0x90, 0x00, 0x69, 0x3a, 0x01, 0x83, 0x84, 0x00,
	0xca, 0x24, 0x18, 0x02, 0xd2, 0x00, 0x10, 0x41, 0x01, 0x00, 0xc1, 0xa3, 0x04, 0x38, 0x9c, 0x25,
	0x04, 0x11, 0x60, 0x31, 0x40, 0xd8, 0x29, 0x20, 0x04, 0x08, 0x0a, 0x14, 0x08, 0xc0, 0x08, 0x01,
	0x81, 0x05, 0x0d, 0x95, 0x20, 0x42, 0x61, 0x10, 0x30, 0x8c, 0x0b, 0x08, 0x38, 0x45, 0x1a, 0x2a,
	0x98, 0x40, 0x46, 0x74, 0xb4, 0x40, 0x32, 0xd0, 0x08, 0x02, 0x14, 0x02, 0x39, 0x19, 0x20, 0x42,
	0x84, 0x22, 0x60, 0x00, 0x01, 0x08, 0x51, 0x09, 0x2c, 0x20, 0x7a, 0x0a, 0x80, 0x90, 0xe4, 0x18,
	0x00, 0x50, 0x18, 0x41, 0xc1, 0x27, 0x04, 0x02, 0x4a, 0x20, 0x04, 0x00, 0xc4, 0x1c, 0x15, 0xc0,
	0xb0, 0x0c, 0x11, 0x20, 0x06, 0xa6, 0x20, 0xb8, 0x19, 0x8a, 0xa3, 0x58, 0xc2, 0x40, 0x90, 0x01,
	0xcb, 0x0c, 0x20, 0x30, 0xd8, 0x83, 0x4c, 0x16, 0x80, 0x0e, 0x20, 0x09, 0x3c, 0x51, 0x54, 0x80,
	0x20, 0x07, 0x53, 0x30, 0x3c, 0x4e, 0x65, 0x00, 0x90, 0x48, 0x0e, 0x01, 0x06, 0x84, 0x38, 0x82,
	0x8a, 0x18, 0x18, 0x18, 0x40, 0x60, 0x03, 0x11, 0x89, 0x01, 0x53, 0x09, 0x42, 0x22, 0x01, 0x08,
	0x05, 0x92, 0x04, 0x58, 0x11, 0x12, 0x82, 0x70, 0x14, 0x82, 0x7c, 0x12, 0x00, 0xa0, 0x89, 0xc8,
	0x60, 0x02, 0x00, 0x20, 0x13, 0x7d, 0x48, 0x00, 0x0a, 0x40, 0x4b, 0x03, 0x49, 0x04, 0x84, 0x54,
	0x00, 0xb1, 0x96, 0x44, 0x16, 0x2b, 0x54, 0x46, 0x50, 0x10, 0x04, 0x01, 0xc9, 0x64, 0x05, 0x1a,
	0x2a, 0xc0, 0x81, 0xc3, 0x12, 0x33, 0x40, 0x12, 0xc4, 0x05, 0x0d, 0x72, 0xa0, 0x40, 0x84, 0x14,
	0x10, 0x20, 0x30, 0x00, 0x1a, 0x80, 0x99, 0x8e, 0xc2, 0x6e, 0x30, 0x48, 0x25, 0x45, 0x90, 0x0a,
	0x18, 0x60, 0x82, 0x28, 0x20, 0x86, 0x03, 0x10, 0x64, 0x91, 0x90, 0x8c, 0x8a, 0xa0, 0x2e, 0x50,
	0x88, 0x0d, 0x30, 0x98, 0x00, 0x08, 0x03, 0x53, 0x1a, 0x29, 0xc9, 0x00, 0x38, 0x01, 0x60, 0x20,
	0xc0, 0xcb, 0x00, 0x28, 0x41, 0x08, 0x82, 0xa5, 0x4c, 0x81, 0x26, 0x44, 0x02, 0x30, 0x51, 0x42,
	0x0e, 0x00, 0xd0, 0x10, 0xa0, 0x50, 0x81, 0x81, 0x08, 0x54, 0xa3, 0xb3, 0xa0, 0x80, 0x0c, 0x62,
	0xa1, 0xa8, 0x09, 0x08, 0x1e, 0x02, 0x22, 0xa4, 0x3b, 0x04, 0x1d, 0x90, 0x05, 0x48, 0x20, 0x38,
	0x00, 0x00, 0x08, 0x66, 0x04, 0x50, 0x90, 0x35, 0x04, 0x08, 0x28, 0x40, 0x12, 0x22, 0x01, 0x40,
	0xc3, 0x82, 0x04, 0x02, 0x10, 0x03, 0x64, 0xc0, 0xa0, 0x30, 0x00, 0x02, 0x84, 0x20, 0x06, 0x08,
	0x48, 0x20, 0xc3, 0x06, 0x68, 0x40, 0x01, 0x50, 0x33, 0x01, 0x01, 0xf8, 0x80, 0x86, 0x02, 0x05,
	0x01, 0x28, 0x01, 0x0e, 0x43, 0x42, 0xa3, 0x01, 0x13, 0xb4, 0xce, 0x24, 0x02, 0x00, 0xc0, 0x1c,
	0x00, 0x06, 0xa6, 0x40, 0xb2, 0xe2, 0x24, 0xa0, 0x94, 0x4c, 0x40, 0xe0, 0xb1, 0x23, 0x18, 0x08,
	0x28, 0x00, 0x05, 0x50, 0x8a, 0x80, 0x01, 0xe0, 0x40, 0x85, 0x38, 0x06, 0x50, 0x01, 0x43, 0x00,
	0xd2, 0x08, 0x95, 0xc8, 0x5a, 0x08, 0x00, 0x14, 0x6a, 0x81, 0x09, 0x02, 0x4a, 0x22, 0x84, 0x22,
	0x12, 0x00, 0x11, 0x2c, 0x46, 0x26, 0xc1, 0x81, 0x81, 0x01, 0xc8, 0x00, 0x04, 0x09, 0x30, 0x81,
	0xc9, 0x4b, 0x58, 0x95, 0x28, 0xa0, 0x04, 0x0c, 0x20, 0x34, 0x40, 0x89, 0x0b, 0x30, 0x4b, 0x0b,
	0x04, 0x00, 0x80, 0x09, 0x98, 0x02, 0x02, 0x02, 0x72, 0x52, 0x04, 0x48, 0x00, 0x48, 0x04, 0x30,
	0xa0, 0xb4, 0x40, 0x88, 0x87, 0x02, 0x53, 0x18, 0x22, 0x65, 0x45, 0x23, 0x24, 0x00, 0x98, 0x18,
	0x09, 0x08, 0x0c, 0x00, 0x60, 0x40, 0xa4, 0xd8, 0x40, 0x42, 0x50, 0xc2, 0x80, 0xa8, 0x05, 0x5d,
	0x28, 0x04, 0x04, 0x00, 0xb0, 0xe8, 0x80, 0x82, 0x26, 0x84, 0x20, 0x1e, 0x20, 0xd3, 0xcd, 0x08,
	0x07, 0x22, 0x06, 0x14, 0x10, 0x69, 0x0e, 0x05, 0x19, 0x80, 0xd1, 0x84, 0x2a, 0x38, 0x22, 0x8a,
	0x11, 0x91, 0x51, 0xc2, 0x06, 0x50, 0x3a, 0xa1, 0x49, 0x04, 0x82, 0x6c, 0x26, 0x52, 0x03, 0x94,
	0x80, 0x02, 0x40, 0x10, 0x00, 0x88, 0x50, 0x42, 0xa2, 0x4c, 0x10, 0x79, 0x34, 0x00, 0x8f, 0x84,
	0x34, 0x41, 0x49, 0x32, 0x09, 0x42, 0xc0, 0x14, 0x62, 0x03, 0x06, 0x28, 0x48, 0x22, 0x50, 0x60,
	0x00, 0xb2, 0xec, 0x00, 0xc2, 0x0e, 0x35, 0x20, 0x80, 0x50, 0x4d, 0x08, 0x00, 0x60, 0x2a, 0x1c,
	0xa0, 0x54, 0x20, 0x02, 0x04, 0x10, 0x05, 0x40, 0x8b, 0xe8, 0x62, 0x12, 0x82, 0x0d, 0xc1, 0x92,
	0x84, 0x1c, 0x11, 0x81, 0xa4, 0x00, 0x0c, 0x48, 0x54, 0x71, 0x81, 0x91, 0x44, 0x90, 0x28, 0x06,
	0x83, 0x8a, 0x12, 0x65, 0x50, 0x60, 0x5c, 0xc4, 0x20, 0x31, 0x05, 0x8c, 0x02, 0x4c, 0x10, 0xa2,
	0x94, 0x29, 0x96, 0x08, 0x40, 0x25, 0xa3, 0xb0, 0x68, 0x10, 0x0d, 0x74, 0x01, 0x98, 0x00, 0x81,
	0x00, 0x40, 0x00, 0xb2, 0x61, 0x94, 0xa0, 0x04, 0x88, 0x40, 0x00, 0x61, 0x31, 0x14, 0x43, 0x05,
	0x16, 0x20, 0xe8, 0x0b, 0x08, 0x17, 0x08, 0x10, 0x00, 0x79, 0x11, 0x01, 0x84, 0x41, 0x1c, 0x90,
	0x99, 0x87, 0x84, 0x1a, 0xc0, 0x6c, 0x07, 0x90, 0x82, 0x84, 0xdb, 0x89, 0x28, 0x36, 0x12, 0x11,
	0x20, 0x05, 0x03, 0x20, 0x85, 0x02, 0x00, 0xe0, 0x40, 0x02, 0x24, 0x05, 0x32, 0x22, 0x10, 0x09,
	0x20, 0x70, 0x80, 0xd0, 0x18, 0xa5, 0x18, 0x80, 0x00, 0x01, 0x80, 0x21, 0x40, 0x0b, 0x6d, 0x00,
	0x53, 0x08, 0x11, 0xb0, 0x08, 0x06, 0x2a, 0x62, 0xa0, 0x0d, 0x8c, 0x16, 0x65, 0x40, 0x11, 0x01,
	0x06, 0x30, 0x44, 0xc3, 0x02, 0x00, 0x10, 0x10, 0x34, 0xc2, 0x02, 0x0e, 0x30, 0xc8, 0x85, 0xd9,
	0x06, 0x09, 0x08, 0x04, 0x82, 0x28, 0x88, 0x03, 0x06, 0x08, 0x20, 0x92, 0x0e, 0x61, 0x81, 0x65,
	0x66, 0x01, 0x23, 0x0e, 0xc0, 0x02, 0xac, 0x1a, 0x35, 0x03, 0x13, 0x20, 0xd8, 0x2b, 0x10, 0x04,
	0x63, 0x04, 0x68, 0x5d, 0x22, 0x34, 0x40, 0x10, 0x08, 0x1c, 0x48, 0x00, 0x3a, 0x40, 0x50, 0x01,
	0x85, 0x50, 0x00, 0x00, 0x01, 0x09, 0x9d, 0x30, 0x8c, 0x08, 0x0e, 0x27, 0x50, 0x0e, 0x28, 0x91,
	0x86, 0x04, 0x80, 0x69, 0x30, 0xc5, 0x42, 0xe5, 0x50, 0x82, 0x10, 0x82, 0x3c, 0x86, 0x6c, 0x10,
	0x40, 0x43, 0x28, 0x04, 0x42, 0x23, 0x00, 0xa4, 0xb0, 0x09, 0x51, 0xcd, 0x00, 0x24, 0x06, 0xe2,
	0x90, 0x61, 0x96, 0x08, 0x22, 0x50, 0x09, 0x02, 0x4d, 0x59, 0x20, 0x42, 0x05, 0x10, 0x04, 0xe4,
	0x0c, 0xcb, 0x04, 0x14, 0xc3, 0x22, 0x80, 0x08, 0x21, 0x16, 0x95, 0x61, 0x06, 0xd0, 0x03, 0x0a,
	0x1a, 0x10, 0x00, 0x02, 0xb0, 0x40, 0x64, 0x42, 0x04, 0x02, 0x88, 0x40, 0x02, 0xa8, 0x54, 0x04,
	0x00, 0x11, 0x58, 0x54, 0x6d, 0x02, 0x23, 0xc2, 0x80, 0x64, 0x80, 0x02, 0x04, 0xf2, 0x00, 0x14,
	0x1d, 0x03, 0x66, 0x44, 0xa1, 0x28, 0x00, 0x04, 0x8a, 0x24, 0x62, 0x90, 0x30, 0xa5, 0x41, 0x07,
	0x00, 0x1a, 0x40, 0x90, 0xa4, 0x4c, 0x10, 0x41, 0x48, 0x24, 0x61, 0x3c, 0xa8, 0x10, 0x2c, 0x12,
	0xe0, 0x01, 0x98, 0x40, 0x80, 0x88, 0x10, 0x41, 0x40, 0x84, 0x91, 0x48, 0x04, 0x40, 0xa3, 0x00,
	0x9b, 0xa8, 0x90, 0x24, 0x50, 0x31, 0xa8, 0x83, 0x91, 0x44, 0x42, 0x0a, 0x12, 0x02, 0x30, 0xa8,
	0x55, 0xe8, 0x08, 0x41, 0x00, 0x0c, 0x08, 0x12, 0x07, 0x4c, 0xd2, 0x40, 0xac, 0x14, 0x09, 0xe2,
	0x02, 0x01, 0x82, 0x9c, 0x00, 0x15, 0x81, 0x20, 0x52, 0x08, 0x01, 0x60, 0x42, 0x0a, 0x46, 0x25,
	0x28, 0x94, 0xa5, 0x98, 0x61, 0x48, 0x84, 0x41, 0x80, 0xc4, 0x48, 0x21, 0x02, 0x47, 0x88, 0x89,
	0x50, 0x0f, 0x8a, 0x14, 0x12, 0x31, 0x12, 0x05, 0x87, 0x80, 0x32, 0x32, 0x29, 0x80, 0x00, 0xc8,
	0x24, 0x72, 0xe0, 0x33, 0x88, 0x41, 0x80, 0x0e, 0x22, 0xa6, 0x43, 0x0c, 0xd0, 0x51, 0x24, 0x48,
	0xa0, 0x82, 0x04, 0xfc, 0x86, 0x00, 0x38, 0x11, 0xc3, 0x02, 0x00, 0x43, 0x44, 0x08, 0x86, 0x20,
	0x0e, 0x18, 0x54, 0x00, 0x58, 0x01, 0xc8, 0x10, 0x04, 0x81, 0x02, 0x40, 0x20, 0xf8, 0x22, 0xa4,
	0x05, 0x4a, 0x24, 0x62, 0x08, 0x06, 0x2c, 0x10, 0x08, 0x06, 0x15, 0x83, 0x98, 0x9c, 0x00, 0x03,
	0x28, 0x50, 0x41, 0x36, 0x11, 0x04, 0x82, 0x12, 0x65, 0x28, 0x08, 0x8c, 0x82, 0x68, 0x10, 0x30,
	0x82, 0x1f, 0x80, 0x03, 0x63, 0x04, 0x02, 0x82, 0x1c, 0xc4, 0x80, 0xc1, 0x0c, 0x21, 0xa1, 0x04,
	0x30, 0x10, 0x08, 0x00, 0xa2, 0xd3, 0xbd, 0x01, 0xc1, 0x41, 0x60, 0x10, 0x08, 0x12, 0x1c, 0x98,
	0x44, 0x02, 0x82, 0x7b, 0xa3, 0x40, 0x14, 0xa4, 0x4e, 0x05, 0xc2, 0x04, 0x48, 0x8c, 0x80, 0x04,
	0xa2, 0x41, 0x14, 0x21, 0x01, 0x86, 0x22, 0xe5, 0xca, 0x09, 0xbc, 0x0e, 0x81, 0x10, 0xe3, 0x00,
	0x38, 0x20, 0x01, 0x88, 0x50, 0x10, 0xa3, 0x8e, 0x20, 0x5c, 0x45, 0x30, 0x54, 0x29, 0x91, 0x68,
	0x01, 0xa4, 0x14, 0xa0, 0x10, 0x20, 0x1c, 0x82, 0x6a, 0x44, 0x62, 0x20, 0x02, 0x68, 0x10, 0xc9,
	0x4c, 0x40, 0x02, 0x10, 0x0c, 0x0a, 0x4c, 0x68, 0x05, 0xaa, 0xa1, 0x60, 0x50, 0x46, 0x60, 0x80,
	0x02, 0x81, 0x40, 0x00, 0x08, 0x3e, 0x14, 0x08, 0x31, 0x31, 0x80, 0x02, 0x3a, 0x46, 0x12, 0x9a,
	0x61, 0x00, 0x2d, 0x10, 0x21, 0x81, 0x82, 0xe0, 0xc0, 0x01, 0x00, 0x45, 0x38, 0x01, 0x05, 0x84,
	0x0c, 0x48, 0x20, 0x09, 0x9d, 0x00, 0x1e, 0x41, 0x12, 0x23, 0xf1, 0x11, 0x00, 0xc4, 0x42, 0x36,
	0x10, 0x89, 0x04, 0xb1, 0x82, 0x43, 0x08, 0x81, 0x08, 0xa6, 0x60, 0x86, 0x60, 0x04, 0xc4, 0x20,
	0x2c, 0x41, 0x40, 0x40, 0x00, 0x02, 0x00, 0x12, 0x00, 0x04, 0x60, 0x6e, 0x90, 0xa1, 0x0b, 0x00,
	0x93, 0x28, 0x16, 0x01, 0x49, 0x01, 0xf1, 0x40, 0xa0, 0x62, 0xa0, 0xc0, 0x20, 0x90, 0x51, 0x0b,
	0x50, 0x01, 0x00, 0x03, 0x39, 0x85, 0x80, 0x34, 0xc5, 0x18, 0x89, 0x01, 0x10, 0x04, 0x02, 0x06,
	0xa2, 0xbb, 0x14, 0x08, 0x08, 0x06, 0x80, 0xfa, 0x10, 0x49, 0x1b, 0x84, 0x04, 0xb4, 0x01, 0x30,
	0xa0, 0x02, 0xc2, 0x54, 0x23, 0x21, 0x0a, 0xc9, 0x88, 0xa0, 0x00, 0x12, 0x71, 0x16, 0x8c, 0x50,
	0xc2, 0x22, 0x60, 0x08, 0xb1, 0x40, 0x06, 0x44, 0x60, 0x12, 0x8a, 0x04, 0x09, 0x46, 0xa2, 0x60,
	0x54, 0x40, 0x85, 0x81, 0x84, 0x88, 0x26, 0x62, 0x88, 0x98, 0x08, 0x11, 0x09, 0x16, 0x84, 0x18,
	0x02, 0xc5, 0x40, 0x06, 0x04, 0x91, 0x48, 0x30, 0x01, 0x14, 0x01, 0x4a, 0xc7, 0x83, 0x85, 0xa0,
	0x8c, 0x28, 0x1a, 0x56, 0x31, 0x89, 0x38, 0x04, 0x69, 0x54, 0x24, 0x30, 0x00, 0x60, 0x40, 0x28,
	0x04, 0x10, 0x52, 0x80, 0x4c, 0x02, 0x08, 0x40, 0x92, 0x63, 0x1b, 0xc0, 0x09, 0x09, 0x6a, 0x52,
	0x80, 0x20, 0x68, 0x8c, 0x27, 0x10, 0xc0, 0x43, 0x14, 0x0c, 0x43, 0xa2, 0x00, 0x75, 0x31, 0xb7,
	0x90, 0x88, 0x60, 0x2a, 0xe0, 0xf0, 0x30, 0x00, 0x00, 0x82, 0x54, 0x15, 0x02, 0x20, 0x01, 0x41,
	0x85, 0x04, 0x02, 0x30, 0x89, 0x50, 0x81, 0xc9, 0x00, 0xa6, 0x68, 0x20, 0x04, 0x52, 0x41, 0x20,
	0x21, 0x41, 0x04, 0x18, 0x09, 0x40, 0x00, 0x82, 0xca, 0x35, 0xa5, 0x87, 0x40, 0x1e, 0x93, 0x40,
	0x20, 0x19, 0x12, 0x80, 0x00, 0x66, 0x62, 0x0c, 0xcd, 0x12, 0x84, 0x4e, 0x81, 0x28, 0x14, 0x00,
	0xd1, 0x80, 0x0a, 0x01, 0x81, 0xad, 0x44, 0x97, 0x08, 0x00, 0x40, 0x60, 0x01, 0x11, 0x9a, 0x61,
	0x26, 0xc0, 0x81, 0x38, 0x30, 0x54, 0x27, 0x60, 0x21, 0x02, 0x01, 0x34, 0xc4, 0xa0, 0x02, 0xb0,
	0x48, 0x86, 0x15, 0xc0, 0x40, 0x40, 0x00, 0x88, 0x06, 0x80, 0x84, 0x40, 0x56, 0x46, 0x01, 0x82,
	0x80, 0x41, 0xa9, 0x56, 0x94, 0x0b, 0x25, 0x11, 0x06, 0x2e, 0x10, 0x12, 0x40, 0x2e, 0x00, 0x80,
	0x4d, 0x10, 0x11, 0x40, 0x94, 0x0d, 0x00, 0x6c, 0x0c, 0x02, 0x90, 0x12, 0x1c, 0x10, 0x02, 0x62,
	0x41, 0x02, 0x02, 0x04, 0x10, 0x2a, 0x08, 0x40, 0x12, 0x11, 0x0c, 0x08, 0xa2, 0x64, 0x27, 0x08,
	0xa0, 0x80, 0x0c, 0x46, 0x74, 0x11, 0x89, 0x29, 0x40, 0x49, 0xc6, 0x06, 0xf7, 0x00, 0x80, 0xa5,
	0xde, 0x80, 0x68, 0x20, 0x09, 0x03, 0xac, 0x84, 0x00, 0x38, 0x11, 0x02, 0x19, 0x98, 0x09, 0xe2,
	0x20, 0x30, 0xa2, 0x08, 0x6c, 0xc1, 0x4a, 0x44, 0x10, 0x31, 0x03, 0x01, 0x1a, 0x28, 0x20, 0xa5,
	0x40, 0x0f, 0x44, 0xc2, 0x8a, 0x62, 0xb3, 0x08, 0x27, 0x69, 0x90, 0x20, 0x0e, 0xc2, 0x80, 0x91,
	0x68, 0x90, 0x08, 0x3c, 0x14, 0x0b, 0x92, 0x15, 0x4d, 0x80, 0x38, 0x24, 0x30, 0x12, 0x49, 0x14,
	0x04, 0x02, 0xa0, 0x42, 0x28, 0x04, 0xc6, 0x01, 0x22, 0x20, 0x71, 0x25, 0x34, 0x83, 0x41, 0x12,
	0x91, 0x00, 0x24, 0x3c, 0x41, 0x04, 0x06, 0x07, 0xb8, 0x82, 0x01, 0x1b, 0x8c, 0x50, 0x20, 0x4a,
	0x11, 0x98, 0x80, 0x06, 0x48, 0x83, 0xc8, 0x01, 0x88, 0x08, 0x29, 0x74, 0x26, 0x90, 0xa8, 0x00,
	0x00, 0x0f, 0x08, 0xa2, 0x92, 0x90, 0x19, 0x1b, 0x44, 0x00, 0x97, 0x42, 0x29, 0x24, 0x11, 0x00,
	0x12, 0x10, 0xc2, 0x30, 0xe8, 0x44, 0x22, 0x24, 0x00, 0x00, 0x04, 0x48, 0x11, 0x40, 0x04, 0x03,
	0x19, 0xa0, 0xa8, 0x00, 0x04, 0x04, 0x43, 0x60, 0x0a, 0x15, 0x06, 0x03, 0x52, 0xf5, 0x48, 0x00,
	0x10, 0x54, 0x00, 0x0c, 0xd4, 0x29, 0x93, 0xd0, 0x90, 0x4d, 0x4e, 0xa2, 0xc9, 0x10, 0x08, 0x12,
	0x42, 0x00, 0xe1, 0x03, 0x0a, 0x04, 0x86, 0x80, 0x70, 0x84, 0x32, 0x28, 0x90, 0x4a, 0x61, 0x08,
	0x10, 0xb2, 0x94, 0x10, 0x1a, 0x40, 0x40, 0x06, 0x73, 0xac, 0x20, 0x91, 0x20, 0x40, 0x20, 0x99,
	0x84, 0x24, 0x4b, 0xe9, 0x08, 0x81, 0x82, 0x00, 0x31, 0x04, 0x86, 0x30, 0x34, 0x09, 0x32, 0x28,
	0x41, 0x2c, 0x16, 0x00, 0x61, 0x95, 0xa0, 0x10, 0xca, 0x2a, 0x44, 0x20, 0xa0, 0x00, 0x10, 0x20,
	0x68, 0xc6, 0xb1, 0x15, 0xb0, 0x10, 0x2d, 0x18, 0x26, 0x00, 0x30, 0x08, 0xc6, 0x68, 0x00, 0x50,
	0x62, 0x28, 0xc8, 0x00, 0x09, 0x08, 0x81, 0x80, 0x11, 0xc0, 0x03, 0x89, 0x06, 0x50, 0x88, 0x1c,
	0x01, 0xc2, 0x63, 0x7a, 0x05, 0x49, 0x34, 0x25, 0x8d, 0x00, 0x48, 0x00, 0xa2, 0x16, 0x8d, 0xc0,
	0x4a, 0x5c, 0x46, 0x21, 0x0e, 0x25, 0x44, 0x40, 0x16, 0x60, 0x11, 0x91, 0xd0, 0x82, 0xa8, 0x02,
	0x40, 0x51, 0x21, 0x11, 0x48, 0x06, 0x58, 0x12, 0xc1, 0x22, 0x10, 0x51, 0x60, 0x10, 0x22, 0x28,
	0x82, 0x58, 0x49, 0x0e, 0x02, 0x04, 0x09, 0x09, 0x01, 0x17, 0xa6, 0x10, 0x62, 0x53, 0x8a, 0x08,
	0x00, 0x87, 0x10, 0x20, 0xa3, 0x20, 0x44, 0x41, 0x67, 0x0a, 0x05, 0x02, 0x84, 0x18, 0x96, 0x84,
	0x06, 0x91, 0x68, 0x04, 0x85, 0x03, 0x20, 0x00, 0x05, 0x73, 0x24, 0x01, 0xc0, 0x48, 0x00, 0xa5,
	0x1a, 0x06, 0xf1, 0x4c, 0x26, 0x74, 0x14, 0x00, 0x82, 0x88, 0x88, 0x62, 0x08, 0x02, 0x40, 0xa0,
	0x45, 0x40, 0x04, 0x12, 0x92, 0xaa, 0x10, 0x8c, 0x41, 0x09, 0x28, 0x86, 0x00, 0x89, 0x80, 0x01,
	0x02, 0x0a, 0x82, 0x81, 0x31, 0xa0, 0x40, 0x00, 0x06, 0x10, 0x89, 0x80, 0x25, 0x0e, 0xec, 0x00,
	0x05, 0x82, 0x22, 0xd8, 0x08, 0xa1, 0x40, 0x44, 0xa0, 0x00, 0x41, 0xc8, 0x01, 0x14, 0x05, 0x9b,
	0xbc, 0x60, 0x90, 0x88, 0x0e, 0x14, 0x9b, 0x85, 0xc0, 0x94, 0x02, 0x64, 0xa1, 0x10, 0x34, 0x01,
	0x01, 0xa1, 0x60, 0xb3, 0x50, 0x0a, 0xb1, 0x4b, 0xc5, 0x02, 0x40, 0x71, 0x10, 0x05, 0x04, 0x47,
	0x20, 0x96, 0x48, 0xa5, 0x00, 0x01, 0x09, 0x08, 0x72, 0xd0, 0x08, 0x20, 0x06, 0xa1, 0x0a, 0x92,
	0xb2, 0x00, 0x08, 0x10, 0x08, 0x50, 0xc2, 0x08, 0x2e, 0x15, 0x00, 0x4a, 0x78, 0x80, 0x01, 0xa3,
	0x80, 0xd1, 0x07, 0x6e, 0x82, 0xa8, 0x30, 0x24, 0x07, 0x00, 0x08, 0x83, 0x80, 0x28, 0xa4, 0x07,
	0x08, 0x00, 0x60, 0x43, 0x80, 0x1d, 0xc0, 0xaa, 0x56, 0x87, 0x10, 0x10, 0x1d, 0x4c, 0xa4, 0x06,
	0xa3, 0x88, 0x01, 0x00, 0x06, 0x42, 0x2c, 0x80, 0x01, 0x2e, 0x84, 0x00, 0x83, 0x72, 0x06, 0xa2,
	0xb4, 0x14, 0x05, 0x04, 0x10, 0x60, 0x2a, 0x99, 0x38, 0x82, 0x23, 0x06, 0x14, 0x12, 0x29, 0x00,
	0x54, 0x69, 0x14, 0x61, 0x32, 0x21, 0x28, 0x14, 0x25, 0x60, 0xd0, 0x21, 0x92, 0x00, 0x80, 0x20,
	0x40, 0x73, 0xb0, 0x80, 0x00, 0x01, 0x80, 0x58, 0x05, 0x58, 0xa8, 0x29, 0x48, 0x0a, 0x02, 0x55,
	0x4a, 0x82, 0x00, 0xd6, 0xc0, 0x20, 0x81, 0x51, 0x0a, 0x88, 0x59, 0x0c, 0x70, 0x82, 0x02, 0x81,
	0xe1, 0x88, 0xa8, 0x0a, 0x55, 0x11, 0x04, 0x15, 0x80, 0x07, 0x08, 0x02, 0xc1, 0xb3, 0x08, 0x1b,
	0x04, 0x6e, 0x40, 0x60, 0x0d, 0x50, 0x40, 0x4f, 0x70, 0x30, 0x10, 0x20, 0x01, 0x10, 0x63, 0x10,
	0xd1, 0x28, 0x86, 0x0c, 0x0a, 0x04, 0x00, 0x44, 0x00, 0xa0, 0x4d, 0x1a, 0xa1, 0x20, 0x51, 0x38,
	0x12, 0x44, 0x11, 0x67, 0x12, 0x04, 0x00, 0x89, 0x00, 0xc9, 0x00, 0x28, 0x00, 0x83, 0x00, 0x64,
	0x88, 0x00, 0x0a, 0x03, 0xc2, 0x98, 0xc0, 0x08, 0x23, 0x16, 0x07, 0x09, 0xb1, 0x60, 0x08, 0x6a,
	0x04, 0xd2, 0x28, 0x08, 0xb5, 0x11, 0x05, 0x10, 0x25, 0x00, 0xa2, 0x08, 0x16, 0x0c, 0x0a, 0x40,
	0x00, 0x00, 0x59, 0x03, 0x04, 0x02, 0x92, 0xe3, 0x30, 0x00, 0x81, 0xc5, 0x06, 0x30, 0x98, 0x24,
	0x1c, 0x0e, 0x68, 0x20, 0x81, 0x40, 0x02, 0x1d, 0x03, 0x6a, 0x40, 0x41, 0x02, 0x24, 0x24, 0x05,
	0x23, 0x54, 0x92, 0x98, 0x08, 0x88, 0x49, 0x02, 0x20, 0x10, 0x90, 0x30, 0xa0, 0x03, 0x82, 0x24,
	0x20, 0x31, 0x8a, 0x39, 0x58, 0xa8, 0x32, 0x86, 0x80, 0x89, 0x35, 0x88, 0x85, 0x24, 0x05, 0x41,
	0x81, 0x10, 0x08, 0x41, 0x00, 0xd4, 0x00, 0x16, 0x00, 0x12, 0x44, 0x36, 0xc0, 0x68, 0x81, 0x90,
	0x0c, 0xcc, 0x76, 0x20, 0x51, 0x2c, 0x21, 0x50, 0xab, 0x14, 0xb0, 0x4a, 0x00, 0x64, 0x14, 0x4d,
	0x40, 0xc2, 0x12, 0x8e, 0x54, 0x08, 0x02, 0x40, 0x41, 0x28, 0x00, 0x01, 0x9e, 0x81, 0x40, 0xd4,
	0x29, 0x12, 0x24, 0x87, 0x82, 0x08, 0x01, 0x63, 0xa1, 0x00, 0x83, 0x01, 0x00, 0x44, 0xca, 0xa4,
	0x45, 0x48, 0x20, 0x20, 0x22, 0x49, 0x1a, 0xc0, 0x02, 0xc5, 0x3a, 0xa0, 0x10, 0x15, 0x45, 0xd1,
	0xc0, 0x48, 0xa1, 0xe3, 0x04, 0x10, 0xc0, 0x20, 0x74, 0x11, 0x10, 0x83, 0xb1, 0x49, 0x02, 0x52,
	0x14, 0x28, 0x11, 0x75, 0x96, 0xa8, 0x1e, 0x10, 0x60, 0x02, 0xe8, 0x1c, 0x49, 0x28, 0xc2, 0x12,
	0x3c, 0x95, 0x00, 0xa2, 0x42, 0x12, 0x00, 0x80, 0x28, 0x13, 0x04, 0x2c, 0x44, 0x20, 0x00, 0x08,
	0x09, 0x80, 0x40, 0x10, 0xa9, 0x03, 0x74, 0x41, 0x89, 0x20, 0x22, 0x8a, 0xac, 0xa8, 0x11, 0xa0,
	0x42, 0x35, 0x48, 0x8b, 0x40, 0x42, 0x67, 0x10, 0x62, 0x40, 0x12, 0x40, 0x94, 0x42, 0x10, 0x01,
	0x4a, 0x0d, 0xa4, 0xc0, 0x60, 0x5e, 0x50, 0x81, 0x16, 0x40, 0xd2, 0x41, 0x44, 0x22, 0x31, 0x08,
	0x81, 0x14, 0xc9, 0x10, 0x83, 0x80, 0x84, 0x28, 0x04, 0x21, 0x08, 0x82, 0x83, 0x02, 0x04, 0x42,
	0x4c, 0x00, 0x50, 0x8a, 0x2b, 0x54, 0x50, 0x82, 0x46, 0x86, 0x62, 0x02, 0xe9, 0x9c, 0x0c, 0x56,
	0x75, 0x01, 0xa1, 0x34, 0x8c, 0x20, 0x2c, 0x84, 0x58, 0x84, 0xd8, 0x05, 0x08, 0x10, 0xc2, 0x19,
	0x12, 0xe0, 0xc8, 0x01, 0x14, 0x82, 0x08, 0x08, 0x01, 0x40, 0x88, 0x30, 0xc0, 0x4a, 0x8a, 0xa0,
	0x03, 0x40, 0x02, 0x10, 0x8a, 0x1d, 0x00, 0x15, 0x03, 0x6c, 0x10, 0x91, 0x01, 0x81, 0x46, 0x8e,
	0x00, 0x22, 0x13, 0x81, 0x38, 0x80, 0xc9, 0x4c, 0xa0, 0x81, 0xa2, 0x50, 0x59, 0x25, 0x64, 0x03,
	0x1a, 0x85, 0x18, 0x0c, 0x24, 0x20, 0x00, 0x80, 0x90, 0x01, 0x46, 0x28, 0x1c, 0x05, 0xab, 0x00,
	0xac, 0x48, 0xc9, 0x06, 0x66, 0xe0, 0x1c, 0x41, 0x48, 0x08, 0x00, 0x30, 0x41, 0xb0, 0x59, 0xc2,
	0x63, 0x24, 0x33, 0x71, 0x24, 0x80, 0x02, 0x4d, 0x48, 0x83, 0x08, 0x20, 0x90, 0x92, 0x40, 0x04,
	0x22, 0x12, 0x88, 0xb0, 0x8a, 0x40, 0x2a, 0x90, 0x60, 0x20, 0x75, 0x86, 0x03, 0x08, 0xa3, 0x22,
	0xa2, 0xc8, 0x92, 0x4a, 0x10, 0x01, 0x11, 0x88, 0x44, 0x90, 0x89, 0x20, 0x02, 0xa0, 0x8c, 0x25,
	0x02, 0x80, 0x72, 0x02, 0xea, 0x06, 0x01, 0x0d, 0x0c, 0x04, 0x21, 0x1b, 0x83, 0x00, 0x04, 0x22,
	0x1e, 0xc0, 0x61, 0x22, 0x55, 0x01, 0x84, 0x20, 0x02, 0x92, 0xb0, 0xd4, 0x01, 0x6a, 0x14, 0x83,
	0x21, 0x1e, 0xa4, 0x16, 0x00, 0x12, 0x27, 0x08, 0x28, 0x30, 0x11, 0x48, 0x04, 0x00, 0x5a, 0x04,
	0x10, 0x0e, 0x4c, 0x44, 0x92, 0x11, 0x14, 0x80, 0x02, 0x09, 0x50, 0x60, 0x29, 0x20, 0x18, 0x12,
	0x08, 0x00, 0x24, 0x11, 0x00, 0x40, 0x88, 0xc3, 0x26, 0xd4, 0x02, 0x21, 0x00, 0x18, 0x81, 0x20,
	0x41, 0x93, 0x90, 0x8d, 0x54, 0x26, 0x38, 0x04, 0x81, 0x84, 0x68, 0x82, 0x08, 0x3c, 0x61, 0xc0,
	0xaa, 0x80, 0x4c, 0x20, 0x46, 0x10, 0x8a, 0x19, 0xa0, 0x40, 0x22, 0x36, 0x40, 0x61, 0x83, 0x21,
	0x90, 0x08, 0x22, 0x55, 0xa8, 0xba, 0xa8, 0x42, 0x6c, 0x64, 0x40, 0x40, 0x10, 0x50, 0x0a, 0x0d,
	0x00, 0x80, 0x82, 0x24, 0x04, 0x04, 0xa8, 0x0c, 0xf0, 0x70, 0x05, 0xc4, 0x08, 0x05, 0x64, 0x60,
	0x51, 0x0a, 0x1d, 0xc0, 0xa0, 0x02, 0x26, 0x30, 0x22, 0x2c, 0x14, 0x05, 0x10, 0x80, 0x69, 0xaa,
	0x05, 0x04, 0x20, 0x20, 0x61, 0x50, 0x05, 0x00, 0xc2, 0xc2, 0x08, 0x04, 0xf1, 0x24, 0x64, 0x00,
	0xc1, 0x62, 0x82, 0x00, 0x01, 0x50, 0x03, 0x05, 0x18, 0x70, 0x00, 0xa2, 0x10, 0xc0, 0x01, 0x0a,
	0x84, 0xc0, 0x19, 0x00, 0x84, 0x04, 0x08, 0xd5, 0x82, 0x07, 0x59, 0x58, 0x40, 0x0c, 0x94, 0x20,
	0x08, 0x09, 0x06, 0x44, 0x0a, 0x23, 0x00, 0x0b, 0x14, 0x04, 0x86, 0x54, 0x40, 0xa0, 0x11, 0x51,
	0x01, 0x21, 0x68, 0xf2, 0x0a, 0x32, 0x10, 0x11, 0x40, 0x0c, 0x42, 0x28, 0x14, 0x40, 0x08, 0x08,
	0x72, 0x30, 0xc0, 0x20, 0xad, 0x58, 0x61, 0x28, 0x45, 0x29, 0x00, 0x48, 0x01, 0x27, 0x24, 0x62,
	0xc0, 0x80, 0x05, 0x0a, 0xc1, 0x18, 0x25, 0x19, 0xac, 0x25, 0x80, 0x0b, 0x5a, 0x20, 0x00, 0x15,
	0x10, 0x82, 0x01, 0x36, 0x40, 0x51, 0x0a, 0x40, 0xd8, 0x0c, 0x20, 0x92, 0x92, 0x09, 0x19, 0x45,
	0x4b, 0x50, 0x83, 0x82, 0xa3, 0xe1, 0x86, 0xea, 0x08, 0x43, 0x91, 0x19, 0x89, 0x13, 0x80, 0x04,
	0x84, 0x80, 0x10, 0x15, 0xc2, 0x05, 0x0c, 0xd2, 0xc0, 0x00, 0x48, 0x00, 0xa8, 0x40, 0x21, 0x01,
	0x85, 0x14, 0x19, 0xc2, 0x42, 0x60, 0xf0, 0x0a, 0x40, 0x50, 0x42, 0x26, 0x47, 0xc9, 0x22, 0x14,
	0x81, 0xa2, 0x20, 0x31, 0x08, 0x21, 0xc1, 0xc0, 0x00, 0x48, 0x22, 0x40, 0x93, 0x88, 0x00, 0x61,
	0x44, 0x40, 0x0b, 0x80, 0x69, 0x86, 0xec, 0x0a, 0x36, 0x89, 0x14, 0xf0, 0x51, 0x20, 0x42, 0x21,
	0x71, 0x21, 0xd1, 0x0c, 0x20, 0x6c, 0x30, 0x53, 0x00, 0x01, 0x00, 0x23, 0x04, 0x74, 0xa8, 0x34,
	0x40, 0x5b, 0xa0, 0x1a, 0x16, 0x01, 0x80, 0x28, 0x0f, 0x08, 0x10, 0x24, 0x80, 0x12, 0x69, 0x04,
	0x00, 0x48, 0x40, 0xab, 0x94, 0x38, 0x51, 0x4a, 0x26, 0x67, 0x01, 0x00, 0x6c, 0x86, 0x0e, 0x0a,
	0x52, 0x20, 0x0c, 0xa0, 0x14, 0x8c, 0x00, 0x11, 0x83, 0x2c, 0xb0, 0x81, 0x24, 0x00, 0x40, 0x48,
	0x1e, 0x31, 0xc5, 0x40, 0x32, 0x20, 0x90, 0x14, 0x80, 0x52, 0x80, 0x48, 0x11, 0x40, 0x23, 0x80,
	0x14, 0x02, 0x1c, 0x94, 0xa0, 0x08, 0xcc, 0x4a, 0x08, 0x04, 0x37, 0xb0, 0x2a, 0x05, 0x04, 0x00,
	0x30, 0x03, 0x03, 0x36, 0xc1, 0x04, 0x2a, 0x12, 0x54, 0x20, 0xa0, 0xe5, 0x94, 0xa0, 0x16, 0x00,
	0x81, 0x92, 0x81, 0x09, 0xc0, 0x54, 0x44, 0xc0, 0xa9, 0x81, 0x8c, 0x00, 0x0c, 0xb0, 0x28, 0x05,
	0x34, 0x8f, 0x42, 0x42, 0x21, 0xa0, 0x10, 0x24, 0x99, 0x01, 0x04, 0x91, 0x40, 0x09, 0x50, 0x44,
	0x63, 0x62, 0x86, 0x52, 0x18, 0x30, 0x01, 0xa8, 0x00, 0x51, 0x12, 0x82, 0x10, 0x88, 0x01, 0x08,
	0x31, 0x00, 0x34, 0x49, 0x42, 0x80, 0x0c, 0x50, 0x02, 0x94, 0x00, 0x13, 0xc7, 0x00, 0x90, 0x22,
	0x94, 0xc9, 0x19, 0x04, 0x60, 0x74, 0x71, 0x25, 0x35, 0x8b, 0x41, 0x60, 0x86, 0x08, 0x00, 0x60,
	0x40, 0x66, 0x02, 0x90, 0x41, 0x81, 0x81, 0x8e, 0x48, 0x00, 0x43, 0x58, 0x11, 0xb4, 0x13, 0x0b,
	0x2a, 0xa5, 0xc0, 0x90, 0x84, 0x06, 0x04, 0x04, 0x03, 0x2a, 0x1d, 0x30, 0x09, 0x89, 0x20, 0x04,
	0x81, 0x10, 0x90, 0x10, 0x42, 0x50, 0x02, 0x21, 0x08, 0x30, 0x04, 0xa6, 0x10, 0x00, 0xc2, 0x8e,
	0x89, 0x18, 0xa8, 0x00, 0x10, 0x1a, 0xae, 0x64, 0x4a, 0xa2, 0x64, 0x40, 0x60, 0x28, 0x40, 0x1b,
	0x00, 0x18, 0x04, 0xc8, 0xa5, 0xc8, 0x17, 0x22, 0x50, 0xe0, 0x20, 0x08, 0x10, 0x40, 0x2c, 0x24,
	0x10, 0x5a, 0x89, 0x04, 0x0c, 0xac, 0x06, 0x23, 0x18, 0x27, 0x21, 0x89, 0x4d, 0x10, 0x84, 0xc0,
	0x04, 0x38, 0x06, 0x08, 0x22, 0x97, 0xe3, 0x10, 0xd9, 0x44, 0x01, 0x08, 0x00, 0xa0, 0x14, 0x9c,
	0x44, 0x01, 0x00, 0x11, 0x41, 0x88, 0x68, 0x84, 0x24, 0x20, 0x72, 0x41, 0x22, 0x44, 0x88, 0x06,
	0x2a, 0x00, 0x10, 0x30, 0x59, 0x93, 0x22, 0x00, 0x06, 0xd8, 0x14, 0x2d, 0x41, 0xae, 0x50, 0x20,
	0x22, 0x34, 0x80, 0x01, 0x81, 0x20, 0x01, 0x12, 0x00, 0x1d, 0x40, 0xca, 0x34, 0x66, 0x80, 0xb1,
	0x48, 0x45, 0x06, 0x12, 0x10, 0x08, 0x89, 0x94, 0x1a, 0x09, 0x58, 0x44, 0x20, 0x02, 0x24, 0xc5,
	0x09, 0x3c, 0x02, 0xd0, 0x12, 0x00, 0x10, 0x6a, 0x50, 0x92, 0x8a, 0x04, 0xd8, 0x90, 0xa0, 0x12,
	0x41, 0x39, 0x01, 0x21, 0x08, 0x4f, 0x70, 0x60, 0x62, 0x8b, 0x81, 0x42, 0x09, 0x06, 0x87, 0x8a,
	0x81, 0x65, 0xd5, 0x04, 0x60, 0x10, 0x01, 0x10, 0xe0, 0x88, 0x82, 0x2c, 0x01, 0x81, 0x24, 0x08,
	0x0b, 0x21, 0x62, 0x60, 0x52, 0x32, 0x0d, 0x1c, 0x05, 0x22, 0x14, 0x00, 0xb8, 0x38, 0x00, 0x00,
	0x3e, 0xe3, 0x60, 0x83, 0x01, 0x04, 0x00, 0x10, 0x10, 0x20, 0x82, 0x24, 0x06, 0x01, 0x50, 0x50,
	0x18, 0x86, 0x08, 0x13, 0x45, 0x0a, 0x82, 0x2b, 0x28, 0xa4, 0x91, 0x60, 0x18, 0x00, 0x89, 0x8f,
	0x90, 0x0c, 0x00, 0x4c, 0x01, 0x60, 0x36, 0x1c, 0x58, 0x0c, 0x22, 0x51, 0x18, 0xb0, 0x50, 0x0a,
	0xa6, 0x40, 0x46, 0x12, 0x34, 0x80, 0x84, 0x84, 0x48, 0x44, 0x30, 0x01, 0x49, 0x10, 0x0b, 0x42,
	0x00, 0x39, 0x24, 0x68, 0x0d, 0x41, 0x22, 0xa2, 0x88, 0x02, 0x2c, 0x40, 0x08, 0x10, 0x50, 0x41,
	0x02, 0x15, 0x91, 0x46, 0x22, 0x70, 0x42, 0x91, 0x08, 0x07, 0x25, 0x62, 0x07, 0x48, 0x99, 0x11,
	0x44, 0x48, 0x1c, 0x16, 0xe9, 0x3c, 0xc0, 0xc0, 0x40, 0x4e, 0x60, 0x20, 0x0c, 0x9d, 0x00, 0x28,
	0x08, 0x24, 0x93, 0x88, 0xd0, 0x01, 0x20, 0x44, 0x33, 0x00, 0x0d, 0x29, 0x90, 0x40, 0x4c, 0x85,
	0x01, 0x12, 0x80, 0x42, 0x2c, 0x24, 0x11, 0xc0, 0x00, 0x59, 0x40, 0x8b, 0x04, 0x65, 0x00, 0x2c,
	0x55, 0x1c, 0x22, 0x00, 0x04, 0x80, 0x00, 0x5c, 0x88, 0x84, 0x20, 0x07, 0x49, 0x35, 0x84, 0x17,
	0x08, 0x0a, 0x70, 0x08, 0x08, 0x00, 0x1e, 0x08, 0x64, 0x04, 0xf2, 0x8c, 0x00, 0xc3, 0xc0, 0x00,
	0x04, 0x73, 0x21, 0x01, 0x00, 0xc3, 0x50, 0xb2, 0x0a, 0x88, 0x04, 0x0a, 0x21, 0x14, 0x62, 0x00,
	0x13, 0x0c, 0xc0, 0xad, 0x4c, 0xd0, 0x10, 0x0a, 0x21, 0x89, 0x80, 0x10, 0x80, 0x2a, 0xa7, 0x20,
	0x91, 0x00, 0x4a, 0x31, 0x0b, 0x30, 0x28, 0x11, 0x00, 0x00, 0x63, 0xc0, 0x10, 0x2d, 0x10, 0xa2,
	0x20, 0xc4, 0x8b, 0x3b, 0x28, 0x01, 0xe9, 0x10, 0xc4, 0x40, 0x02, 0x41, 0x10, 0x00, 0x10, 0x80,
	0xc1, 0x84, 0x00, 0x90, 0x61, 0x04, 0x11, 0x30, 0x89, 0x98, 0x01, 0x05, 0x4e, 0x02, 0x30, 0x20,
	0x08, 0x54, 0x40, 0x12, 0x80, 0x62, 0x84, 0x0c, 0x08, 0x09, 0x40, 0x94, 0x33, 0x04, 0x01, 0x48,
	0x81, 0x22, 0x80, 0x02, 0xa6, 0x11, 0x14, 0x23, 0x14, 0x04, 0x6b, 0x20, 0x40, 0x14, 0xa4, 0x10,
	0x44, 0x1a, 0x11, 0x40, 0x42, 0x01, 0x34, 0x01, 0x39, 0x08, 0x14, 0x50, 0xa2, 0x14, 0x00, 0x00,
	0x03, 0x90, 0x09, 0x0a, 0x24, 0x85, 0x40, 0xb0, 0x51, 0x1f, 0x00, 0x10, 0x82, 0x08, 0x08, 0x2c,
	0x41, 0x29, 0x4a, 0x83, 0x80, 0x93, 0x40, 0x56, 0x2b, 0x4e, 0x10, 0xc2, 0x20, 0x48, 0x01, 0x8c,
	0x00, 0x70, 0x3a, 0x0c, 0x0c, 0x10, 0x00, 0x28, 0x03, 0x40, 0x3a, 0x18, 0x53, 0x60, 0x60, 0x64,
	0x80, 0xb4, 0x24, 0x00, 0x62, 0x08, 0x50, 0x20, 0x06, 0x60, 0x88, 0x20, 0x1a, 0x60, 0x08, 0x11,
	0x15, 0x83, 0x0b, 0x38, 0x24, 0x52, 0x98, 0x24, 0x10, 0x28, 0x40, 0x54, 0x08, 0x01, 0xf1, 0xc9,
	0x21, 0x30, 0x44, 0x30, 0x89, 0x21, 0x02, 0x0c, 0x40, 0x80, 0x88, 0x36, 0x20, 0x4c, 0x04, 0x74,
	0x84, 0x02, 0x00, 0x49, 0x95, 0x48, 0x04, 0x82, 0x6a, 0x38, 0x20, 0x46, 0x2b, 0x40, 0x22, 0xf3,
	0x21, 0xd5, 0x00, 0x09, 0x48, 0x03, 0x11, 0x84, 0x4c, 0x80, 0x6a, 0x46, 0xa2, 0x90, 0x92, 0x30,
	0x88, 0x06, 0x48, 0xc1, 0x69, 0x07, 0x00, 0x40, 0x02, 0x02, 0x00, 0x29, 0x02, 0x85, 0x14, 0x29,
	0x38, 0x40, 0x09, 0xac, 0x98, 0x01, 0x41, 0x20, 0x82, 0x50, 0x82, 0x1d, 0xcf, 0xc0, 0x28, 0x00,
	0x10, 0x0c, 0x40, 0x40, 0xc4, 0x10, 0x42, 0x82, 0x06, 0x08, 0x81, 0x08, 0x44, 0x23, 0xd1, 0x81,
	0x20, 0xd6, 0x22, 0x58, 0x00, 0x90, 0x08, 0x51, 0x14, 0x03, 0x06, 0x42, 0xc8, 0x07, 0xec, 0x12,
	0x82, 0x18, 0x97, 0x43, 0x9b, 0x00, 0x09, 0x4a, 0x04, 0xc0, 0x20, 0x22, 0x60, 0x5e, 0x24, 0x04,
	0x93, 0x6a, 0x90, 0x48, 0x44, 0x26, 0x64, 0x41, 0x22, 0x92, 0x48, 0x08, 0x48, 0x16, 0x22, 0xd0,
	0x1e, 0x00, 0x49, 0x6f, 0x00, 0x24, 0x08, 0x08, 0xb1, 0xc0, 0xc8, 0x02, 0x20, 0x49, 0x90, 0x04,
	0x8c, 0x03, 0x10, 0x84, 0xaa, 0x0d, 0x14, 0x45, 0x61, 0x00, 0x42, 0x63, 0x01, 0x20, 0x01, 0x6a,
	0x04, 0x16, 0x20, 0x10, 0x00, 0x82, 0x44, 0x4a, 0x20, 0x33, 0x0c, 0x30, 0x10, 0x82, 0x38, 0x34,
	0x91, 0xb0, 0x91, 0x51, 0x41, 0x68, 0x85, 0x22, 0x00, 0x00, 0x97, 0x60, 0x08, 0x52, 0x52, 0xa9,
	0x4d, 0x18, 0x0c, 0x56, 0x24, 0xe2, 0x06, 0x58, 0xd0, 0x21, 0x16, 0x44, 0x28, 0x08, 0x21, 0x58,
	0x02, 0x22, 0x01, 0xc0, 0x8b, 0x04, 0x05, 0x84, 0x00, 0x11, 0x19, 0x35, 0xf0, 0x8d, 0x40, 0x10,
	0x17, 0x02, 0x1a, 0x0c, 0x48, 0x28, 0x52, 0x80, 0x63, 0x14, 0xd1, 0x09, 0x26, 0x48, 0x20, 0x20,
	0x35, 0x18, 0x12, 0x40, 0x04, 0x32, 0x82, 0x86, 0x61, 0x00, 0xac, 0x60, 0x00, 0xa0, 0x02, 0x44,
	0x48, 0x00, 0x06, 0xa6, 0x90, 0x24, 0x10, 0x0e, 0x65, 0x60, 0x01, 0x08, 0x88, 0x49, 0x18, 0x00,
	0x50, 0x41, 0x41, 0x83, 0x24, 0xca, 0xa8, 0x22, 0x40, 0x20, 0xa0, 0x88, 0x14, 0x60, 0x00, 0x07,
	0x80, 0x20, 0x18, 0x80, 0x01, 0x14, 0x05, 0x01, 0x9c, 0x04, 0x93, 0x46, 0x08, 0xc3, 0x62, 0x10,
	0x14, 0x1a, 0x48, 0x70, 0x10, 0x08, 0x05, 0xa8, 0x11, 0x4c, 0x40, 0xa6, 0x00, 0x20, 0x28, 0x52,
	0x01, 0x58, 0x20, 0x09, 0x89, 0x05, 0x86, 0xa0, 0x0c, 0x56, 0xf0, 0x20, 0x88, 0x51, 0x48, 0x04,
	0x65, 0x00, 0x8d, 0x01, 0xc5, 0x20, 0x62, 0x00, 0x91, 0x8e, 0x4d, 0xc2, 0x80, 0x12, 0x11, 0x2a,
	0x20, 0xd1, 0x11, 0xa1, 0x52, 0x80, 0x48, 0x84, 0xd0, 0x09, 0x28, 0x40, 0x50, 0x89, 0x80, 0x14,
	0x06, 0x0c, 0x1a, 0x44, 0x08, 0xba, 0xa5, 0x50, 0xc7, 0x08, 0xa7, 0x22, 0x0a, 0x30, 0x44, 0x00,
	0x44, 0xc4, 0xd0, 0x09, 0x10, 0x40, 0x88, 0x48, 0x22, 0xcb, 0x18, 0x84, 0x07, 0x8b, 0x06, 0x41,
	0x50, 0x00, 0x49, 0x1a, 0xa2, 0x24, 0x63, 0x23, 0x90, 0x91, 0x02, 0x08, 0x28, 0xa0, 0x08, 0x0a,
	0x1c, 0x04, 0x47, 0x2a, 0x05, 0xd8, 0x08, 0x84, 0x10, 0xa8, 0x40, 0x71, 0x58, 0x82, 0x0d, 0x14,
	0x00, 0x56, 0xe6, 0x92, 0x8e, 0xd4, 0x00, 0x4d, 0x14, 0x54, 0x49, 0x80, 0x25, 0x0c, 0x01, 0x38,
	0x30, 0x08, 0xac, 0x10, 0x90, 0x40, 0x4a, 0x13, 0x63, 0x01, 0x04, 0x01, 0x00, 0x02, 0xd1, 0x19,
	0x02, 0xa0, 0x00, 0xae, 0x36, 0x00, 0x80, 0x24, 0xa0, 0x52, 0x8b, 0x00, 0xd0, 0x02, 0x00, 0x51,
	0x84, 0x84, 0x0c, 0x02, 0xc3, 0x39, 0x80, 0x8a, 0x44, 0x66, 0x50, 0x08, 0x09, 0x31, 0x08, 0x41,
	0x02, 0x07, 0x18, 0xa0, 0x20, 0xc5, 0x2a, 0x12, 0x45, 0x48, 0xa0, 0x68, 0xc6, 0x83, 0x48, 0x20,
	0x80, 0x1c, 0x11, 0x45, 0x88, 0x60, 0x56, 0x53, 0x12, 0x60, 0x07, 0x2d, 0x24, 0x04, 0x29, 0x84,
	0x20, 0x88, 0x45, 0x08, 0x96, 0x81, 0x84, 0x00, 0x91, 0x04, 0x04, 0x21, 0x09, 0x0e, 0x39, 0x10,
	0x28, 0x70, 0x00, 0xc0, 0x20, 0xb0, 0x02, 0x6e, 0x00, 0x82, 0xe3, 0x02, 0x9d, 0x00, 0x2b, 0x74,
	0x21, 0xe1, 0x31, 0x15, 0x58, 0xaa, 0x40, 0xe0, 0x20, 0x30, 0x14, 0x52, 0xc4, 0x00, 0x16, 0x68,
	0xa0, 0x08, 0x10, 0x82, 0x58, 0x01, 0x18, 0x36, 0xe4, 0x0a, 0x61, 0x44, 0x01, 0xa1, 0x90, 0x94,
	0x11, 0xc0, 0x12, 0xc4, 0x13, 0x19, 0x44, 0x09, 0x66, 0x2a, 0x84, 0x18, 0x13, 0x05, 0xcd, 0x40,
	0x20, 0x30, 0x81, 0x02, 0x84, 0xd0, 0x40, 0x1e, 0x00, 0x1b, 0x15, 0x70, 0x12, 0x20, 0x22, 0x04,
	0x61, 0x05, 0x51, 0x03, 0x08, 0x40, 0x80, 0x8a, 0x8e, 0x30, 0x84, 0x40, 0x08, 0x84, 0x60, 0xb3,
	0x01, 0x52, 0x4a, 0x02, 0x06, 0x82, 0xb0, 0x49, 0x40, 0x46, 0x3a, 0x81, 0x00, 0x14, 0x00, 0x88,
	0xa4, 0x08, 0x14, 0x2a, 0x34, 0x7c, 0x04, 0x61, 0x00, 0xe1, 0xd8, 0x9c, 0xa4, 0x14, 0x86, 0x44,
	0x00, 0x41, 0x21, 0xc8, 0x4d, 0x04, 0x00, 0x02, 0x51, 0x2a, 0x01, 0x9d, 0x04, 0x40, 0x45, 0x60,
	0x98, 0x28, 0x42, 0x05, 0x0a, 0xa6, 0x00, 0x02, 0x99, 0x89, 0xa2, 0x20, 0x02, 0x02, 0x2d, 0xe8,
	0x10, 0x00, 0x5c, 0xb0, 0x83, 0x02, 0x4c, 0x02, 0xe8, 0x04, 0xa4, 0x02, 0x02, 0x04, 0x88, 0x42,
	0x4c, 0x32, 0x90, 0xbb, 0x00, 0xcd, 0x00, 0x72, 0x11, 0x2a, 0x04, 0x85, 0x01, 0x28, 0x10, 0x11,
	0xb2, 0x00, 0x88, 0x1a, 0xc3, 0x16, 0x81, 0xd3, 0x00, 0x00, 0x04, 0x87, 0x44, 0x70, 0x58, 0x91,
	0x30, 0x04, 0x45, 0x2c, 0x33, 0x28, 0x21, 0x40, 0x88, 0x81, 0x52, 0x80, 0xe8, 0x01, 0xa0, 0x80,
	0x0c, 0x36, 0x44, 0xe1, 0x12, 0x88, 0x18, 0xe2, 0x4a, 0x20, 0x0b, 0x20, 0x28, 0x81, 0x00, 0x48,
	0xb2, 0x10, 0x06, 0x0d, 0x8e, 0x00, 0x44, 0x15, 0x90, 0xa2, 0x04, 0x82, 0x01, 0x28, 0x42, 0xa8,
	0x90, 0x4d, 0x80, 0x25, 0x42, 0x10, 0x02, 0x0e, 0x81, 0x84, 0x2e, 0x54, 0x04, 0x10, 0x21, 0x00,
	0xd8, 0x02, 0x78, 0x81, 0x08, 0x06, 0xbc, 0x50, 0x4c, 0x10, 0xd0, 0x00, 0x30, 0xa4, 0x41, 0x22,
	0x02, 0x41, 0x51, 0x12, 0x50, 0x08, 0x07, 0x18, 0xb4, 0xe0, 0x01, 0x30, 0x4c, 0x68, 0x00, 0x32,
	0x08, 0x95, 0xb8, 0x90, 0xc0, 0x0c, 0xb0, 0x51, 0x10, 0x4c, 0x90, 0xce, 0x44, 0x21, 0xea, 0x00,
	0x28, 0x18, 0x0a, 0x10, 0x00, 0x22, 0x18, 0x04, 0x09, 0x05, 0x6a, 0x81, 0x08, 0x27, 0x09, 0x08,
	0xc2, 0x74, 0x45, 0x58, 0x00, 0xa1, 0x81, 0x00, 0x12, 0x50, 0x8a, 0x16, 0x04, 0x5a, 0x60, 0x00,
	0x86, 0x0a, 0x20, 0xc0, 0x1c, 0x08, 0x02, 0x05, 0x02, 0x1a, 0x41, 0x08, 0x4c, 0x2a, 0x24, 0x01,
	0xa1, 0x11, 0x12, 0x46, 0x00, 0x85, 0x80, 0xb8, 0x08, 0x42, 0x20, 0x32, 0x82, 0x30, 0x8a, 0x78,
	0x84, 0x89, 0x12, 0x02, 0x12, 0x10, 0x20, 0x81, 0x60, 0x46, 0x52, 0x01, 0x2f, 0x20, 0x10, 0x04,
	0x48, 0xa0, 0xe0, 0x00, 0xc4, 0x10, 0xa5, 0x22, 0x04, 0x82, 0x1a, 0x0d, 0x05, 0x04, 0x2a, 0x50,
	0x71, 0xa7, 0x04, 0x87, 0x02, 0x18, 0x60, 0xc8, 0x81, 0x1d, 0xc9, 0x00, 0x00, 0x05, 0x29, 0x30,
	0x0c, 0x55, 0x00, 0x54, 0x24, 0x03, 0x82, 0x70, 0x03, 0x25, 0x00, 0xb0, 0x40, 0x82, 0x85, 0x95,
	0x0d, 0x48, 0x10, 0x49, 0x16, 0x00, 0xd4, 0x08, 0x0e, 0xd4, 0x82, 0x80, 0x00, 0xce, 0xc6, 0x22,
	0x02, 0x48, 0x18, 0xb9, 0x52, 0xe0, 0x42, 0x20, 0x11, 0x2c, 0x58, 0x0a, 0x89, 0x48, 0x85, 0xc0,
	0x04, 0x05, 0x83, 0x88, 0x00, 0xa5, 0x10, 0x00, 0x30, 0x1f, 0x02, 0x08, 0x00, 0x90, 0x10, 0x45,
	0xcc, 0xac, 0x06, 0x70, 0xc0, 0x02, 0x31, 0x07, 0x03, 0x12, 0xe0, 0x22, 0x90, 0x00, 0x4d, 0x80,
	0x36, 0x01, 0x80, 0x02, 0xc8, 0x0c, 0x49, 0x18, 0x10, 0x40, 0x18, 0xe4, 0x06, 0x09, 0x22, 0x20,
	0x08, 0x08, 0x18, 0x09, 0x21, 0x66, 0x92, 0x09, 0x00, 0xe0, 0x4a, 0x8d, 0x32, 0x10, 0x41, 0x0d,
	0x19, 0x41, 0x29, 0x02, 0xb3, 0x40, 0x81, 0x94, 0x18, 0x44, 0x34, 0x03, 0x33, 0xb2, 0x58, 0x08,
	0x4c, 0x6c, 0x81, 0x82, 0x94, 0x0c, 0x44, 0x01, 0x44, 0x01, 0x81, 0x31, 0x40, 0xc3, 0x82, 0x00,
	0x75, 0xf2, 0x01, 0x18, 0x19, 0xca, 0x36, 0xc0, 0xa1, 0x04, 0x19, 0x10, 0xa1, 0x00, 0x75, 0x80,
	0x08, 0x89, 0x86, 0x20, 0x00, 0x01, 0x01, 0xa1, 0x31, 0x46, 0x00, 0x50, 0x01, 0xa0, 0x04, 0x04,
	0x05, 0x41, 0x4c, 0x66, 0x08, 0x90, 0x80, 0xd5, 0x01, 0x1c, 0x10, 0xb2, 0x08, 0xd0, 0x82, 0x29,
	0x50, 0x01, 0x40, 0x02, 0x70, 0x98, 0x81, 0x28, 0x14, 0x91, 0x9d, 0xd9, 0x98, 0x6f, 0x20, 0x80,
	0x02, 0x00, 0x41, 0x00, 0x20, 0x20, 0x04, 0xd3, 0x22, 0x08, 0x0d, 0xac, 0x28, 0x40, 0x70, 0x83,
	0xd1, 0x82, 0x09, 0x56, 0x25, 0x09, 0x1c, 0x59, 0x08, 0x82, 0x04, 0x12, 0x1a, 0x08, 0xa0, 0x00,
	0x87, 0x02, 0xc2, 0x20, 0x06, 0x41, 0x42, 0x08, 0x40, 0xe3, 0xc8, 0x02, 0x98, 0x83, 0x20, 0x6a,
	0x65, 0x5b, 0x02, 0x48, 0x81, 0x41, 0x0c, 0x92, 0xe0, 0x14, 0x18, 0x05, 0x41, 0x10, 0x43, 0x38,
	0x2c, 0x20, 0x86, 0x40, 0x0c, 0x46, 0x00, 0x19, 0xd1, 0x11, 0x20, 0x02, 0xd1, 0x10, 0x09, 0x05,
	0x01, 0xa6, 0x22, 0x04, 0x90, 0x8c, 0x20, 0x04, 0x00, 0x0e, 0x30, 0x88, 0x80, 0x09, 0x9f, 0x20,
	0x08, 0x40, 0x68, 0x20, 0x34, 0x01, 0xa7, 0x64, 0x00, 0x41, 0xb3, 0x24, 0x88, 0x08, 0x14, 0x12,
	0x08, 0x91, 0x20, 0x1c, 0x06, 0x00, 0xf0, 0x82, 0x2a, 0xb8, 0x16, 0x08, 0x24, 0xa1, 0x10, 0x10,
	0x01, 0x96, 0x4b, 0x56, 0x02, 0x60, 0x18, 0x20, 0x40, 0xa9, 0x58, 0x01, 0x73, 0x82, 0xc0, 0x8a,
	0x60, 0x04, 0x14, 0x82, 0x06, 0xdc, 0xda, 0xa8, 0x20, 0x22, 0x02, 0x97, 0x2c, 0x40, 0x04, 0x24,
	0x12, 0x21, 0x00, 0xa1, 0xc0, 0x80, 0x78, 0x54, 0x50, 0x23, 0xc1, 0x91, 0x01, 0x12, 0x27, 0x99,
	0x20, 0x01, 0x14, 0x04, 0x00, 0x44, 0x09, 0x92, 0x91, 0x40, 0x40, 0x0c, 0x51, 0x10, 0x33, 0x44,
	0x41, 0xcc, 0x60, 0xa5, 0x00, 0x08, 0x28, 0x16, 0x08, 0x00, 0x84, 0x33, 0x08, 0x49, 0x0b, 0x2a,
	0x04, 0x80, 0xa1, 0x21, 0x04, 0x95, 0x01, 0x0e, 0xa1, 0x50, 0x26, 0x24, 0x04, 0xe9, 0x10, 0xc2,
	0x50, 0x98, 0x01, 0xd8, 0xc0, 0x0a, 0x44, 0x30, 0x09, 0x65, 0x02, 0x02, 0x36, 0xd4, 0x22, 0x0a,
	0xc4, 0x14, 0x82, 0x00, 0x25, 0x0a, 0x07, 0x84, 0x07, 0x42, 0x12, 0x24, 0x19, 0x88, 0x80, 0x40,
	0x6c, 0x20, 0x30, 0x50, 0x82, 0x00, 0x00, 0xc8, 0x0e, 0x82, 0x01, 0xb6, 0x70, 0x84, 0x41, 0x40,
	0x27, 0x0b, 0xb4, 0x04, 0x89, 0x00, 0x38, 0x53, 0xca, 0x13, 0x80, 0x54, 0x42, 0x40, 0x84, 0x03,
	0x24, 0x6c, 0x44, 0x89, 0x1e, 0xa2, 0x6b, 0x04, 0x21, 0x18, 0x04, 0x00, 0x45, 0xa2, 0x84, 0xc8,
	0x01, 0x40, 0x20, 0x60, 0xa8, 0x20, 0x34, 0x82, 0x40, 0x20, 0x06, 0x5a, 0x80, 0x20, 0x80, 0x0a,
	0x70, 0x34, 0x52, 0x01, 0x15, 0x0b, 0x60, 0x24, 0xc7, 0x90, 0x32, 0x08, 0x4c, 0x82, 0x04, 0x26,
	0x92, 0xa1, 0xc9, 0x41, 0x23, 0x2c, 0x66, 0x29, 0x1d, 0xa5, 0x00, 0x00, 0x10, 0x41, 0xa8, 0x0a,
	0x20, 0x5c, 0x21, 0x54, 0xa3, 0x0a, 0x00, 0xb9, 0x40, 0x04, 0x2c, 0x20, 0x00, 0x04, 0x80, 0x03,
	0x0c, 0x18, 0xc0, 0x43, 0x00, 0x74, 0x0e, 0xa3, 0x04, 0x42, 0xc3, 0x0c, 0x80, 0xc2, 0x2a, 0x00,
	0x00, 0x82, 0x94, 0x48, 0xd2, 0x04, 0x24, 0x01, 0xf8, 0x23, 0x68, 0x12, 0x0c, 0x7c, 0x30, 0x00,
	0x03, 0x18, 0x1f, 0x60, 0x26, 0x00, 0x18, 0x04, 0x84, 0x05, 0x67, 0x10, 0x90, 0x02, 0x19, 0x30,
	0xcc, 0x86, 0x00, 0x26, 0x50, 0x10, 0x45, 0x47, 0x81, 0x08, 0x42, 0xaa, 0x01, 0xa4, 0x86, 0x29,
	0x70, 0x96, 0x00, 0x1d, 0x31, 0xc1, 0xe4, 0x5c, 0x02, 0x20, 0x09, 0x05, 0x14, 0x00, 0x40, 0x83,
	0x41, 0x28, 0xa8, 0x90, 0x00, 0x08, 0x32, 0xa0, 0xb7, 0x0d, 0x51, 0x02, 0x24, 0x85, 0x18, 0x0a,
	0x40, 0x08, 0x21, 0x12, 0x43, 0x12, 0x12, 0x40, 0x88, 0xa6, 0x06, 0x65, 0xc1, 0x34, 0x1c, 0x54,
	0x41, 0x20, 0x01, 0x48, 0x2a, 0x40, 0x05, 0x43, 0x56, 0x80, 0x80, 0x08, 0x34, 0x02, 0x47, 0x12,
	0x43, 0x21, 0x08, 0x00, 0x11, 0xc4, 0x0a, 0x01, 0x08, 0x05, 0x0c, 0x4c, 0x00, 0x06, 0x01, 0x70,
	0x80, 0x00, 0x5d, 0x48, 0x2c, 0x00, 0x12, 0x09, 0x2d, 0xc2, 0x48, 0x08, 0xe0, 0x19, 0x87, 0x00,
	0x98, 0x29, 0x20, 0x13, 0xc2, 0x08, 0x01, 0x81, 0x84, 0x2c, 0x21, 0x18, 0x2a, 0x78, 0x89, 0x05,
	0x60, 0x55, 0x00, 0x07, 0x61, 0x14, 0x80, 0x32, 0x14, 0x58, 0xa2, 0xb9, 0x42, 0x2a, 0x0c, 0xe6,
	0x52, 0x86, 0x8d, 0x48, 0x04, 0x00, 0xc4, 0xd0, 0x08, 0x28, 0xc3, 0x27, 0x02, 0x31, 0x18, 0x12,
	0x80, 0x12, 0x02, 0x02, 0x62, 0x01, 0x27, 0x24, 0x98, 0x01, 0x24, 0x42, 0x82, 0x94, 0x01, 0x16,
	0x05, 0x50, 0x34, 0x08, 0x04, 0x84, 0x43, 0x21, 0x08, 0x00, 0xb0, 0x88, 0x90, 0x16, 0x40, 0x40,
	0x74, 0x63, 0x2e, 0x00, 0x41, 0x60, 0x02, 0x24, 0x02, 0x02, 0x04, 0x49, 0x81, 0x44, 0x01, 0xc8,
	0x2a, 0xc0, 0x15, 0x80, 0x64, 0x24, 0xda, 0x84, 0x01, 0x08, 0x60, 0x08, 0xa1, 0x02, 0x10, 0x39,
	0x50, 0x20, 0x04, 0x84, 0x10, 0x12, 0x61, 0x02, 0x49, 0x22, 0xe0, 0x10, 0x3c, 0x61, 0xc6, 0x80,
	0x00, 0x86, 0x29, 0xa4, 0x10, 0x90, 0x25, 0x00, 0xc0, 0x80, 0x12, 0x18, 0x95, 0x40, 0x32, 0x12,
	0x89, 0x18, 0xd1, 0x02, 0x86, 0x54, 0x81, 0x20, 0x0c, 0x98, 0x06, 0x46, 0x20, 0xc0, 0x40, 0x18,
	0x0d, 0x09, 0x27, 0x2e, 0x40, 0x80, 0x2a, 0x40, 0x17, 0x40, 0x68, 0x12, 0x62, 0xa9, 0x0c, 0xc0,
	0x82, 0x1e, 0x54, 0xb1, 0x05, 0x28, 0x12, 0x68, 0x24, 0xa6, 0x62, 0x2e, 0x20, 0x51, 0x04, 0x44,
	0x91, 0x53, 0x83, 0xc8, 0x00, 0x28, 0x00, 0x42, 0x29, 0x11, 0xa0, 0x06, 0xc8, 0x1a, 0x45, 0x08,
	0x10, 0x84, 0x5b, 0x25, 0x10, 0x86, 0xc2, 0x10, 0x48, 0x08, 0x2e, 0x14, 0x22, 0xc0, 0x2d, 0x00,
	0x50, 0x42, 0x48, 0xc1, 0x80, 0x81, 0x80, 0x10, 0x01, 0x20, 0x02, 0xc1, 0x15, 0x01, 0x02, 0x48,
	0x22, 0x07, 0x82, 0x14, 0x00, 0xd4, 0xc2, 0x02, 0x01, 0x5b, 0x06, 0x25, 0x84, 0x00, 0x02, 0x64,
	0xb8, 0x02, 0xc8, 0x8e, 0x89, 0x20, 0x01, 0x8a, 0x12, 0x80, 0x10, 0x8c, 0x02, 0x92, 0x02, 0x80,
	0x01, 0x43, 0x2d, 0x08, 0x82, 0x21, 0x00, 0x11, 0x01, 0x8a, 0x12, 0x94, 0x02, 0x03, 0x30, 0xc0,
	0x6d, 0x24, 0x86, 0x21, 0x04, 0x50, 0xc0, 0xa0, 0x0e, 0x12, 0x81, 0x29, 0xc4, 0x06, 0x63, 0x00,
	0x42, 0x02, 0x80, 0x10, 0x06, 0x0d, 0x08, 0x45, 0x50, 0x01, 0x1c, 0x98, 0x4c, 0x64, 0x64, 0x90,
	0x88, 0x30, 0x8a, 0x43, 0x5a, 0x01, 0x11, 0x03, 0xa8, 0x43, 0x20, 0x00, 0x00, 0xa3, 0xb6, 0x24,
	0x0e, 0x6b, 0x20, 0xc4, 0x22, 0x00, 0x4c, 0x48, 0xe0, 0x24, 0x21, 0x90, 0x01, 0x8c, 0x88, 0xa8,
	0x04, 0x22, 0x68, 0xb3, 0xa5, 0x52, 0x42, 0x4a, 0xc2, 0x0b, 0x1d, 0x00, 0xc8, 0x04, 0x02, 0x10,
	0x2a, 0x84, 0x80, 0x08, 0x45, 0x4a, 0x24, 0x8b, 0x00, 0x30, 0x10, 0x21, 0x18, 0x52, 0xc9, 0x02,
	0xc5, 0x98, 0x02, 0x10, 0x81, 0x02, 0x21, 0x91, 0x40, 0x65, 0x40, 0x41, 0x90, 0x92, 0x11, 0xd4,
	0x20, 0x5a, 0x83, 0x20, 0x00, 0x25, 0x82, 0x0c, 0x06, 0x15, 0x41, 0x16, 0x04, 0x81, 0x02, 0x6a,
	0x41, 0x89, 0x12, 0xb1, 0x00, 0x6f, 0x54, 0x01, 0x09, 0x1a, 0x8d, 0x02, 0x00, 0x32, 0xd0, 0x01,
	0xa5, 0x15, 0x81, 0x01, 0x10, 0x80, 0x22, 0x00, 0x84, 0xc0, 0x00, 0x58, 0x24, 0xc1, 0x13, 0x09,
	0x12, 0xe0, 0x16, 0x84, 0x9a, 0x05, 0x08, 0x53, 0x84, 0x42, 0x00, 0x68, 0x21, 0x21, 0x0a, 0x49,
	0x10, 0x10, 0x12, 0x81, 0x10, 0x10, 0x63, 0x42, 0x41, 0x02, 0x01, 0x05, 0xdb, 0xc4, 0x3c, 0x11,
	0xb0, 0x00, 0xc9, 0x0c, 0x0e, 0x20, 0x20, 0x12, 0x90, 0x69, 0x08, 0x82, 0x42, 0x01, 0x8b, 0x30,
	0x2c, 0x4c, 0x00, 0x22, 0x02, 0x43, 0xa9, 0xc0, 0x84, 0xc8, 0x0c, 0xf2, 0x00, 0x04, 0x60, 0x4a,
	0x00, 0x40, 0xa4, 0x08, 0x22, 0x04, 0xc7, 0x04, 0x42, 0x05, 0x20, 0x1d, 0xc1, 0x04, 0x85, 0x6a,
	0x20, 0x22, 0x1c, 0xc8, 0x55, 0x88, 0x00, 0x00, 0xa0, 0x05, 0x21, 0x14, 0xc8, 0x48, 0x22, 0x20,
	0x1a, 0x10, 0x49, 0x0d, 0x2e, 0xc4, 0x9a, 0x04, 0x01, 0x90, 0x44, 0x12, 0x04, 0x42, 0x21, 0x20,
	0x84, 0x22, 0x74, 0x00, 0x60, 0x82, 0x29, 0x40, 0xa8, 0x00, 0x87, 0xb3, 0x98, 0xb4, 0x14, 0x05,
	0x40, 0xa2, 0x41, 0x0b, 0x19, 0xce, 0x08, 0x04, 0x02, 0x38, 0x89, 0x15, 0x82, 0xc0, 0x40, 0x50,
	0x08, 0xa0, 0x20, 0x03, 0x08, 0x10, 0xa1, 0x90, 0x1d, 0x90, 0x5a, 0x40, 0x06, 0x96, 0xd3, 0x08,
	0x81, 0x02, 0x07, 0x00, 0xc0, 0x49, 0x08, 0x8c, 0x80, 0xa4, 0x00, 0xc7, 0x90, 0x2b, 0xc9, 0x88,
	0x40, 0x08, 0x20, 0x00, 0xa4, 0x41, 0x04, 0x20, 0x38, 0x52, 0x00, 0x00, 0xc0, 0x13, 0x00, 0x12,
	0x00, 0xe2, 0x82, 0x91, 0x03, 0x49, 0x04, 0x84, 0x52, 0x02, 0xa0, 0x4d, 0x21, 0x36, 0x00, 0x8a,
	0xb1, 0xd4, 0x4a, 0xee, 0x08, 0x40, 0x48, 0xb2, 0xd0, 0x08, 0x85, 0x30, 0x64, 0x23, 0x39, 0x08,
	0x80, 0x0c, 0x0a, 0x90, 0x01, 0x10, 0x51, 0x88, 0x62, 0x40, 0x12, 0x49, 0x1d, 0x2c, 0x80, 0xcd,
	0x14, 0xa0, 0x31, 0x21, 0x24, 0x00, 0x86, 0x10, 0xa2, 0xd2, 0x82, 0x05, 0x49, 0x6a, 0x60, 0x00,
	0x02, 0x08, 0x35, 0x03, 0x22, 0x54, 0x90, 0xd8, 0xa4, 0x09, 0x59, 0x0a, 0x30, 0x00, 0x08, 0x15,
	0x61, 0x00, 0x0a, 0x04, 0xa4, 0xb3, 0x92, 0x00, 0x41, 0x40, 0x00, 0x47, 0x02, 0x00, 0xd4, 0x04,
	0xac, 0x0a, 0xa7, 0x10, 0x81, 0x01, 0xc1, 0x0e, 0x22, 0x22, 0x40, 0x24, 0x3c, 0x14, 0x6c, 0x28,
	0xc2, 0xd8, 0x18, 0xe1, 0x11, 0x0c, 0x00, 0x00, 0x82, 0x1d, 0xb5, 0x06, 0x01, 0x4e, 0x43, 0x63,
	0x0c, 0x4d, 0x8c, 0x08, 0x6c, 0x03, 0x20, 0x8c, 0x4c, 0x08, 0x01, 0x00, 0x32, 0x88, 0x0a, 0x21,
	0x0f, 0x45, 0x20, 0x90, 0x43, 0x20, 0x04, 0x16, 0xac, 0x20, 0x54, 0x08, 0x85, 0x51, 0x89, 0xc0,
	0x3e, 0x23, 0xb2, 0xa0, 0x0d, 0x10, 0x48, 0x00, 0x34, 0x19, 0xa2, 0xc0, 0x86, 0x40, 0x12, 0xe0,
	0x01, 0x0e, 0x20, 0x03, 0x80, 0x00, 0xc0, 0x29, 0x8a, 0x90, 0x46, 0x24, 0x08, 0x84, 0x00, 0x87,
	0x89, 0x41, 0x64, 0x38, 0x04, 0x49, 0x04, 0x28, 0xc5, 0x06, 0x0e, 0x93, 0xb0, 0x0a, 0x34, 0x04,
	0x04, 0x6c, 0x20, 0x23, 0x1e, 0x81, 0x5a, 0x81, 0x4a, 0x61, 0x90, 0x84, 0x50, 0x0e, 0x42, 0x0c,
	0x12, 0xa1, 0x26, 0xa8, 0x88, 0x04, 0x34, 0x10, 0x28, 0x20, 0x90, 0x45, 0x83, 0x30, 0x25, 0x1a,
	0x20, 0x8c, 0x41, 0x05, 0x00, 0xc4, 0x48, 0xa8, 0x89, 0x4d, 0x01, 0x08, 0xc2, 0x00, 0xa4, 0x45,
	0x58, 0x01, 0x78, 0x31, 0x2b, 0x28, 0x88, 0x06, 0x21, 0x62, 0x10, 0x23, 0x9a, 0x90, 0x88, 0x41,
	0x10, 0x80, 0x19, 0x01, 0x40, 0x06, 0x21, 0x48, 0x11, 0x82, 0x20, 0xd0, 0x18, 0x09, 0x04, 0xb1,
	0x00, 0x25, 0xc0, 0x00, 0xc6, 0x20, 0x06, 0x22, 0x29, 0x59, 0x53, 0x60, 0x38, 0x81, 0x30, 0x8e,
	0x88, 0x06, 0xa4, 0x0e, 0x20, 0xa0, 0x84, 0x00, 0x02, 0x00, 0x20, 0x65, 0x32, 0x84, 0x00, 0x00,
	0x0a, 0x12, 0x12, 0x01, 0x2a, 0x18, 0x0f, 0x45, 0x02, 0x92, 0x69, 0x20, 0xa5, 0x51, 0x44, 0x12,
	0x44, 0x00, 0x2a, 0x18, 0x0d, 0x24, 0x52, 0xc4, 0x22, 0x8a, 0x80, 0x80, 0x00, 0x20, 0x00, 0xd8,
	0x00, 0x25, 0x12, 0x01, 0x44, 0x72, 0x02, 0x8a, 0x7c, 0x10, 0x20, 0x2c, 0x35, 0x52, 0x00, 0x80,
	0x90, 0x4a, 0x02, 0x05, 0x80, 0x14, 0x49, 0x18, 0x01, 0x18, 0x92, 0x88, 0xb1, 0x69, 0x18, 0x2c,
	0x34, 0x50, 0x4a, 0x80, 0x95, 0x16, 0x82, 0x10, 0x42, 0xa0, 0x0c, 0x90, 0x04, 0xaa, 0x44, 0x26,
	0x89, 0x10, 0x24, 0x40, 0x0b, 0x0e, 0x21, 0x20, 0x1c, 0x04, 0x86, 0x08, 0x10, 0xc1, 0x00, 0x0f,
	0x30, 0x5c, 0x04, 0x40, 0xa0, 0x2b, 0x98, 0x30, 0xd9, 0x61, 0x72, 0x24, 0x23, 0x21, 0x25, 0x46,
	0xe4, 0x1c, 0x00, 0x42, 0x08, 0x00, 0x88, 0xc1, 0x2c, 0x35, 0x20, 0x14, 0x40, 0x00, 0x23, 0x26,
	0xf4, 0x20, 0x15, 0x14, 0xda, 0x20, 0x00, 0x40, 0x53, 0x2f, 0xc0, 0xcb, 0x26, 0x52, 0x11, 0x01,
	0x06, 0xe1, 0xc0, 0x4a, 0x6a, 0x82, 0x48, 0x12, 0x80, 0x11, 0x00, 0x34, 0x87, 0x00, 0x21, 0xa9,
	0x00, 0x4a, 0x22, 0x80, 0x60, 0x33, 0x21, 0x10, 0x0a, 0x18, 0x42, 0xa0, 0x20, 0x3c, 0x92, 0x40,
	0x04, 0x61, 0x42, 0x1a, 0x58, 0x02, 0x82, 0x4a, 0x80, 0x00, 0x10, 0xdc, 0x01, 0x21, 0x04, 0xb0,
	0x9a, 0x04, 0x04, 0x02, 0x00, 0x28, 0x20, 0x52, 0x19, 0x80, 0xc1, 0xc6, 0x4c, 0x04, 0x28, 0x01,
	0x44, 0x93, 0x21, 0x30, 0x40, 0x08, 0xb4, 0x41, 0xc5, 0x28, 0x00, 0x30, 0x8a, 0x01, 0x00, 0x1a,
	0x60, 0x04, 0xc0, 0x03, 0xa0, 0x24, 0x14, 0xad, 0x20, 0x04, 0xc1, 0x00, 0x39, 0x48, 0x4c, 0x02,
	0xc6, 0x20, 0x20, 0x74, 0x95, 0x00, 0x22, 0x16, 0xc3, 0x1a, 0x00, 0x42, 0x01, 0x12, 0xe3, 0xc1,
	0x89, 0x68, 0x10, 0x24, 0x12, 0x86, 0x12, 0x08, 0x20, 0x14, 0x27, 0x00, 0x70, 0x80, 0x09, 0x50,
	0x8a, 0x8b, 0x70, 0x81, 0x10, 0x29, 0xcc, 0x01, 0x86, 0x04, 0x01, 0x10, 0x98, 0x3c, 0x50, 0xa0,
	0x02, 0x44, 0x00, 0x10, 0x49, 0x0a, 0x80, 0x3a, 0x60, 0x20, 0x15, 0xd0, 0x44, 0x00, 0x08, 0x25,
	0x81, 0x3a, 0x00, 0x15, 0xc0, 0x10, 0x42, 0x0a, 0x10, 0xc0, 0x0b, 0x20, 0x08, 0x71, 0x28, 0x2c,
	0xc0, 0x99, 0x41, 0x02, 0x30, 0x00, 0x00, 0x80, 0x99, 0x6c, 0x24, 0x63, 0x09, 0x10, 0x50, 0xc5,
	0x66, 0x0c, 0x12, 0x0a, 0x01, 0x94, 0x07, 0x0a, 0x42, 0x80, 0xe2, 0xa6, 0x29, 0x8c, 0x20, 0x04,
	0x11, 0x10, 0x01, 0x01, 0x18, 0x03, 0x24, 0x40, 0x18, 0x8c, 0x04, 0x88, 0xc6, 0x4c, 0x83, 0x20,
	0x09, 0xe0, 0x47, 0x8c, 0x40, 0x40, 0x01, 0x06, 0x25, 0x14, 0xc0, 0x72, 0x05, 0x70, 0x84, 0x40,
	0x40, 0x43, 0x20, 0xb0, 0x88, 0x13, 0x88, 0x89, 0xa2, 0x20, 0x55, 0x00, 0x00, 0x20, 0xd3, 0x04,
	0x08, 0x12, 0x21, 0x05, 0x08, 0x41, 0x68, 0x4a, 0x16, 0x00, 0x8e, 0x61, 0x00, 0xc6, 0x0c, 0xa2,
	0xc9, 0x01, 0xa8, 0x55, 0x00, 0x12, 0x60, 0x6a, 0x87, 0xd8, 0x12, 0x8a, 0x20, 0x14, 0x11, 0x32,
	0x00, 0x00, 0xa4, 0x08, 0x06, 0x10, 0x0d, 0x14, 0x10, 0x05, 0x40, 0x01, 0xa0, 0xa8, 0x40, 0x0f,
	0x80, 0x42, 0x04, 0x43, 0x37, 0x0c, 0x94, 0x4b, 0x28, 0x80, 0x90, 0x0c, 0x8d, 0x0d, 0xc9, 0x72,
	0x30, 0xd8, 0x92, 0x05, 0x01, 0x23, 0x1a, 0x10, 0x00, 0x81, 0x90, 0x06, 0xc5, 0x00, 0xa2, 0x02,
	0x88, 0x94, 0x56, 0x00, 0x08, 0x22, 0x49, 0x16, 0xc0, 0x81, 0x21, 0x18, 0xa0, 0x03, 0x11, 0x75,
	0x02, 0xc0, 0x46, 0x52, 0x90, 0x28, 0x30, 0x8a, 0x28, 0x30, 0x04, 0x10, 0x8a, 0x19, 0x49, 0x01,
	0x2c, 0x07, 0x00, 0x00, 0x44, 0x40, 0x24, 0x0a, 0x97, 0x59, 0x98, 0x01, 0x58, 0xa0, 0x22, 0x01,
	0x21, 0x00, 0xf4, 0xda, 0xa8, 0x5e, 0x60, 0x00, 0xac, 0x65, 0x01, 0x80, 0x42, 0xd0, 0x03, 0x00,
	0x14, 0x03, 0x4b, 0x02, 0x02, 0x60, 0x16, 0x81, 0x88, 0x84, 0x02, 0x62, 0xaa, 0x14, 0x84, 0xc2,
	0x40, 0x52, 0x15, 0x00, 0x95, 0x80, 0x01, 0x04, 0x10, 0x20, 0x08, 0x38, 0x08, 0xd0, 0x60, 0x14,
	0x20, 0x88, 0x88, 0x89, 0x14, 0x85, 0x3c, 0x00, 0x82, 0x03, 0x40, 0x90, 0x84, 0x02, 0x62, 0x22,
	0x01, 0x40, 0xcc, 0xc1, 0x1e, 0x13, 0x02, 0x2a, 0x20, 0x40, 0x22, 0x00, 0x10, 0x8b, 0x00, 0xa9,
	0x83, 0x00, 0x08, 0x85, 0x03, 0x38, 0x69, 0x59, 0x22, 0x54, 0xe1, 0xd0, 0xa0, 0xd4, 0x40, 0x29,
	0x12, 0x74, 0x20, 0x21, 0x54, 0x08, 0x08, 0x08, 0xc2, 0x22, 0x2b, 0x90, 0x14, 0x01, 0x4a, 0x53,
	0x30, 0x01, 0x30, 0x18, 0x40, 0x44, 0x84, 0x98, 0x30, 0x1c, 0x02, 0x04, 0x10, 0x61, 0x29, 0x08,
	0x4c, 0x02, 0x61, 0x10, 0x04, 0x60, 0x98, 0xd0, 0x08, 0x8b, 0x00, 0x14, 0x92, 0x2c, 0x61, 0x06,
	0x85, 0x0a, 0x12, 0xa1, 0x1b, 0xe0, 0x83, 0x88, 0x5c, 0x21, 0x48, 0x20, 0x30, 0x41, 0x8a, 0x46,
	0x84, 0x50, 0x0e, 0x09, 0x01, 0x05, 0x70, 0x40, 0x81, 0x02, 0x14, 0x01, 0xc8, 0x00, 0xd5, 0x01,
	0x20, 0x00, 0x0d, 0x4f, 0x58, 0x00, 0xe0, 0x90, 0x08, 0x50, 0x09, 0x0a, 0x23, 0xcb, 0x0c, 0x09,
	0xc4, 0x02, 0x26, 0x86, 0x58, 0x0d, 0x0c, 0x94, 0x41, 0x5a, 0xd2, 0x18, 0x04, 0x80, 0x02, 0x06,
	0x40, 0x14, 0xc0, 0x04, 0x11, 0x08, 0x04, 0x42, 0x60, 0x20, 0x03, 0x48, 0xc1, 0x80, 0x42, 0x15,
	0x50, 0x18, 0xc1, 0x14, 0x2a, 0x24, 0x55, 0x58, 0x14, 0x64, 0x54, 0x69, 0x20, 0x04, 0x91, 0x90,
	0x60, 0x50, 0xc8, 0x22, 0x23, 0x40, 0x21, 0x01, 0x4e, 0x65, 0x08, 0x25, 0x00, 0x18, 0x01, 0x0c,
	0x03, 0x20, 0x04, 0xe1, 0x91, 0x34, 0xc1, 0x68, 0x30, 0x41, 0x10, 0x02, 0x21, 0x12, 0x0a, 0x00,
	0x04, 0xf0, 0x2c, 0x58, 0x04, 0x8d, 0x00, 0x80, 0x2b, 0xa8, 0x70, 0x16, 0x65, 0x08, 0xc2, 0x80,
	0x12, 0x8c, 0x82, 0x44, 0x48, 0x20, 0x30, 0x14, 0x04, 0xc0, 0x81, 0x20, 0x02, 0x42, 0xb0, 0x00,
	0x46, 0x80, 0x18, 0x14, 0x02, 0x10, 0x38, 0x4a, 0xc2, 0x60, 0xc0, 0x6a, 0x0a, 0x88, 0x4c, 0x88,
	0x20, 0x21, 0xc2, 0x0b, 0xe8, 0xc4, 0xa4, 0x04, 0x40, 0x00, 0x0a, 0x60, 0x58, 0x4c, 0x18, 0x72,
	0x89, 0x23, 0x00, 0x0a, 0x20, 0x2a, 0x26, 0x02, 0x08, 0x88, 0x10, 0x04, 0x14, 0x80, 0x20, 0x31,
	0x3c, 0x54, 0xc0, 0x00, 0x81, 0xa3, 0x80, 0x44, 0x00, 0x66, 0x24, 0x81, 0x43, 0x16, 0x81, 0x18,
	0x87, 0x06, 0x14, 0x20, 0x15, 0x00, 0x04, 0x23, 0x04, 0x46, 0xe2, 0x3f, 0x00, 0x03, 0x04, 0x54,
	0x30, 0x61, 0x12, 0xc0, 0x02, 0x41, 0x46, 0x21, 0xc3, 0x8a, 0x14, 0x08, 0x26, 0x30, 0x24, 0x48,
	0x21, 0x0d, 0x07, 0xa1, 0x18, 0xa2, 0x70, 0x06, 0x00, 0x92, 0x00, 0x78, 0x47, 0x20, 0xb0, 0x0c,
	0x55, 0x00, 0x2e, 0xe2, 0x20, 0x88, 0xb1, 0x8a, 0x8c, 0x02, 0x02, 0x03, 0x0c, 0x81, 0x50, 0x83,
	0x44, 0x93, 0x00, 0x26, 0xc1, 0x04, 0x40, 0x20, 0xf1, 0x01, 0x20, 0xc8, 0x11, 0x4e, 0x2e, 0x00,
	0x20, 0x23, 0x51, 0x50, 0x20, 0x7c, 0x06, 0x81, 0x08, 0x48, 0x4c, 0x82, 0x14, 0x00, 0xca, 0xa5,
	0x44, 0x17, 0x00, 0x08, 0x02, 0x0a, 0x9a, 0x50, 0x10, 0x40, 0x22, 0x40, 0x01, 0x08, 0x75, 0x80,
	0x2a, 0x02, 0x46, 0x21, 0x8a, 0x24, 0x49, 0x81, 0x0a, 0x94, 0x03, 0x8d, 0x04, 0x00, 0x24, 0x48,
	0xa0, 0x3a, 0x02, 0x01, 0x4c, 0x09, 0x48, 0x22, 0x42, 0x08, 0xc0, 0x13, 0xac, 0x08, 0xb2, 0x91,
	0x00, 0x04, 0x98, 0x41, 0x64, 0x01, 0xc1, 0x2a, 0x40, 0x83, 0x21, 0x64, 0x33, 0x1a, 0x08, 0x00,
	0x00, 0x20, 0x12, 0x94, 0x4b, 0x95, 0xcc, 0x58, 0x04, 0x20, 0x54, 0x48, 0xa0, 0xa9, 0x08, 0x43,
	0x20, 0x01, 0xd0, 0xa0, 0xe1, 0x40, 0x04, 0x06, 0xb7, 0x91, 0x01, 0x29, 0xcb, 0xe0, 0x20, 0x25,
	0x18, 0x24, 0x84, 0x00, 0xc0, 0x10, 0x50, 0x01, 0x01, 0x38, 0x00, 0x2c, 0x00, 0x67, 0xa1, 0x18,
	0x90, 0x00, 0x8d, 0x68, 0x04, 0x70, 0x28, 0x18, 0x87, 0x83, 0x16, 0x30, 0x62, 0x0c, 0x14, 0x10,
	0xaa, 0x30, 0x17, 0xa2, 0xa5, 0xc9, 0x92, 0x44, 0x20, 0xb2, 0x0a, 0x03, 0x5d, 0x01, 0xc4, 0x4e,
	0x10, 0x8a, 0x21, 0x40, 0x90, 0x80, 0x2a, 0x24, 0x00, 0x21, 0xf0, 0x41, 0x42, 0x56, 0x02, 0x02,
	0x14, 0x60, 0x49, 0x20, 0x02, 0x04, 0x81, 0xa2, 0x08, 0x40, 0x42, 0x00, 0x00, 0x20, 0x23, 0x31,
	0x04, 0x88, 0x40, 0x01, 0x61, 0x84, 0x28, 0x46, 0x0c, 0x48, 0xb1, 0xb2, 0x06, 0x20, 0x08, 0x8e,
	0x72, 0x30, 0x99, 0x15, 0x31, 0x44, 0x0d, 0x04, 0xa2, 0x63, 0x84, 0x21, 0x90, 0xa2, 0x00, 0xc4,
	0x41, 0x05, 0x1c, 0xc8, 0x05, 0x28, 0x11, 0x18, 0x02, 0x49, 0x03, 0x00, 0x18, 0x15, 0xd0, 0xa2,
	0x0c, 0xc0, 0x26, 0x46, 0x04, 0x28, 0x02, 0x1d, 0xd1, 0x01, 0x54, 0xe3, 0x8a, 0x80, 0x00, 0x10,
	0xce, 0x04, 0x02, 0x10, 0xa3, 0x70, 0x04, 0x03, 0x26, 0x97, 0x09, 0xb3, 0x90, 0x96, 0x00, 0x18,
	0x82, 0xc8, 0x21, 0x1c, 0x5c, 0x61, 0x00, 0x41, 0x42, 0x05, 0x80, 0x88, 0x21, 0x08, 0xa0, 0x10,
	0x09, 0x15, 0xc0, 0x00, 0x40, 0x01, 0xa8, 0x09, 0x68, 0x1a, 0x8a, 0x50, 0x80, 0xc0, 0x23, 0x80,
	0x52, 0xc9, 0x00, 0x47, 0x82, 0x22, 0x01, 0x45, 0x45, 0x40, 0x51, 0x78, 0x05, 0x21, 0x0a, 0x84,
	0x66, 0x44, 0x1a, 0x82, 0x01, 0x4b, 0x23, 0x70, 0x45, 0xb0, 0x84, 0x48, 0x40, 0x49, 0x32, 0x10,
	0x11, 0x08, 0x98, 0x00, 0x81, 0x28, 0x61, 0x41, 0x0d, 0x14, 0x83, 0x84, 0x20, 0x16, 0x41, 0x96,
	0xa0, 0x91, 0x08, 0x2c, 0x70, 0x80, 0x83, 0x51, 0x48, 0x20, 0x28, 0x14, 0x90, 0x01, 0xd9, 0x11,
	0x24, 0x5a, 0x21, 0x70, 0x03, 0x38, 0x86, 0xa8, 0x04, 0x20, 0x43, 0x28, 0x08, 0x88, 0x43, 0x6c,
	0x86, 0x80, 0x85, 0x28, 0x0a, 0x87, 0x04, 0x41, 0x89, 0x32, 0x00, 0x50, 0x28, 0x12, 0x50, 0x38,
	0xb0, 0x80, 0x88, 0xaa, 0x10, 0x24, 0x28, 0x0a, 0xb1, 0x40, 0x22, 0x16, 0x65, 0x10, 0xa9, 0x09,
	0x09, 0xc1, 0x04, 0x02, 0x59, 0x1c, 0x84, 0x14, 0x42, 0x02, 0x62, 0x89, 0x0c, 0x20, 0x09, 0x64,
	0x40, 0x82, 0x49, 0x0a, 0x21, 0x97, 0x41, 0x52, 0x06, 0x13, 0x04, 0x40, 0x00, 0x62, 0x06, 0x80,
	0xa1, 0x84, 0x08, 0x90, 0x00, 0x08, 0x80, 0x60, 0x02, 0x18, 0x41, 0x84, 0x42, 0xe1, 0x82, 0x03,
	0x10, 0x40, 0x21, 0x50, 0x02, 0x42, 0x84, 0x04, 0x4a, 0x0a, 0x4a, 0x11, 0xa3, 0x82, 0xac, 0x99,
	0x08, 0x56, 0x23, 0x01, 0x0a, 0x8c, 0x5c, 0xa4, 0x46, 0x34, 0x00, 0x88, 0x1c, 0x82, 0x0d, 0x32,
	0x11, 0x00, 0x33, 0x81, 0x01, 0x40, 0x08, 0x00, 0x08, 0x2e, 0x80, 0x10, 0x28, 0x68, 0x61, 0x29,
	0x02, 0x80, 0x00, 0x46, 0x40, 0x30, 0xa9, 0x00, 0x98, 0x13, 0x2c, 0x10, 0xb0, 0x89, 0x01, 0x30,
	0x8c, 0x86, 0x30, 0x23, 0x50, 0x03, 0x00, 0x40, 0x02, 0x08, 0x80, 0xa0, 0x25, 0x10, 0x08, 0xe6,
	0x16, 0x04, 0x40, 0x98, 0x20, 0x16, 0x0c, 0x54, 0x41, 0xd2, 0x10, 0x30, 0x12, 0x00, 0x1a, 0x42,
	0x82, 0x30, 0x45, 0x19, 0xad, 0x40, 0x83, 0x4a, 0x91, 0xa8, 0x81, 0x00, 0x12, 0x02, 0x51, 0x00,
	0x20, 0x9a, 0x04, 0x40, 0x64, 0x68, 0x3c, 0x30, 0xda, 0x41, 0x62, 0xb0, 0x18, 0x10, 0xe9, 0x41,
	0x80, 0x0c, 0x36, 0x62, 0x2c, 0x15, 0x85, 0x69, 0x08, 0x11, 0x01, 0x08, 0x68, 0x82, 0x0c, 0x7c,
	0x04, 0x32, 0x1a, 0x95, 0x00, 0x6d, 0x60, 0x50, 0x10, 0x87, 0x25, 0x0c, 0x00, 0x4c, 0x80, 0x11,
	0x28, 0x80, 0x44, 0x26, 0x58, 0x11, 0x80, 0x85, 0x25, 0x16, 0x4a, 0x0c, 0x41, 0x88, 0x10, 0x04,
	0x08, 0x42, 0x42, 0x84, 0x43, 0x80, 0x94, 0x48, 0x02, 0x02, 0x47, 0x49, 0x95, 0x70, 0x09, 0xc0,
	0x00, 0x00, 0x09, 0x1a, 0xa4, 0x01, 0x05, 0x20, 0x80, 0x00, 0x09, 0x50, 0x4a, 0x6e, 0x30, 0xb0,
	0x00, 0x10, 0x8d, 0x01, 0xaa, 0x0a, 0xd1, 0x4a, 0x07, 0x21, 0x80, 0xa6, 0x00, 0x20, 0x12, 0x2c,
	0x48, 0x82, 0x40, 0x20, 0x51, 0x98, 0x24, 0x65, 0x86, 0x20, 0x2a, 0xd0, 0x02, 0x1d, 0x69, 0x08,
	0xa0, 0x18, 0x30, 0x43, 0x01, 0x81, 0xca, 0x48, 0x30, 0x21, 0x90, 0xa2, 0x99, 0x00, 0x4d, 0x04,
	0x45, 0x80, 0x02, 0x08, 0xc8, 0x08, 0x0c, 0xd2, 0x60, 0xab, 0x75, 0x41, 0x00, 0x30, 0x86, 0x60,
	0x04, 0x30, 0x44, 0x29, 0x14, 0x47, 0x32, 0x94, 0x50, 0x0c, 0x80, 0x64, 0xb0, 0x40, 0x0c, 0xd4,
	0x14, 0x80, 0x1c, 0x00, 0x18, 0x28, 0xcd, 0x8c, 0x08, 0x00, 0x54, 0xe3, 0x38, 0x04, 0xc0, 0x69,
	0x40, 0x45, 0x38, 0xa0, 0x05, 0xc9, 0x22, 0x10, 0x83, 0x20, 0x9c, 0x0c, 0x93, 0x88, 0x60, 0x00,
	0x53, 0x94, 0x40, 0x16, 0x22, 0x3c, 0x20, 0x3a, 0x00, 0x14, 0x01, 0xc1, 0x04, 0x86, 0x08, 0x20,
	0x40, 0x88, 0x00, 0x30, 0x83, 0x48, 0x18, 0xb0, 0x11, 0x47, 0x08, 0x20, 0xe2, 0x22, 0x80, 0x4f,
	0x20, 0x08, 0x80, 0x92, 0x01, 0xa8, 0x05, 0x05, 0x22, 0x04, 0xd3, 0x10, 0xc1, 0x00, 0x8e, 0x0a,
	0xc1, 0x00, 0x08, 0x44, 0x0e, 0x4a, 0x48, 0x23, 0x40, 0x80, 0x44, 0x8a, 0x07, 0x40, 0x50, 0x98,
	0x37, 0x28, 0x10, 0x66, 0x02, 0x94, 0x69, 0x04, 0x29, 0x10, 0x22, 0x30, 0x40, 0x23, 0x14, 0x74,
	0xc4, 0x40, 0x0e, 0x02, 0x00, 0x94, 0x00, 0x09, 0xe8, 0x44, 0x21, 0x90, 0x12, 0x84, 0x81, 0x46,
	0x04, 0x46, 0x31, 0x8a, 0x05, 0xc9, 0x01, 0x50, 0x46, 0xc0, 0x07, 0x10, 0xc0, 0x0c, 0x02, 0x84,
	0xc0, 0x10, 0x08, 0x80, 0x49, 0x06, 0x02, 0x21, 0x04, 0x24, 0x96, 0x40, 0x04, 0x41, 0x1b, 0x0a,
	0x2c, 0x02, 0xca, 0x54, 0x20, 0x03, 0x81, 0xc4, 0x0a, 0x83, 0x40, 0xd2, 0x22, 0x0c, 0x20, 0x46,
	0x00, 0x20, 0x40, 0x1b, 0x20, 0x4c, 0x8d, 0x80, 0x6c, 0x60, 0x99, 0x17, 0xd1, 0x45, 0x20, 0x10,
	0x22, 0x40, 0xae, 0x00, 0x1c, 0x44, 0x50, 0xb0, 0x10, 0x03, 0x74, 0xc8, 0x41, 0x34, 0x36, 0x40,
	0xb1, 0x40, 0x19, 0x00, 0x00, 0x27, 0xc2, 0x22, 0x80, 0x02, 0x61, 0x00, 0xf0, 0x2b, 0x02, 0xb0,
	0x40, 0x09, 0x4c, 0x14, 0x09, 0x08, 0x34, 0x04, 0x49, 0x00, 0x92, 0x41, 0xa4, 0x60, 0x08, 0x48,
	0x70, 0x21, 0x62, 0xb5, 0x45, 0x13, 0xa8, 0x00, 0x80, 0xb2, 0x10, 0x40, 0x0d, 0x03, 0x30, 0x05,
	0x90, 0x11, 0x29, 0x48, 0x8e, 0x40, 0x01, 0x52, 0x11, 0x68, 0x80, 0x89, 0x62, 0x46, 0xa3, 0x28,
	0x40, 0x11, 0x00, 0x40, 0xc3, 0xc1, 0x01, 0x11, 0xc5, 0xa9, 0x10, 0x90, 0x11, 0x30, 0x81, 0xc3,
	0x06, 0x72, 0x21, 0x03, 0x88, 0x00, 0x03, 0x04, 0x12, 0xa5, 0x60, 0x92, 0x59, 0x04, 0xa1, 0x08,
	0x24, 0x21, 0x00, 0xa0, 0x02, 0x24, 0x42, 0x72, 0x10, 0x82, 0x08, 0x80, 0x00, 0x3c, 0x35, 0x11,
	0x0b, 0x50, 0x11, 0x48, 0x08, 0xb6, 0x90, 0x11, 0x00, 0x15, 0x00, 0x46, 0x42, 0x00, 0x03, 0xa0,
	0x86, 0x02, 0x04, 0x44, 0x09, 0x90, 0x00, 0x18, 0x09, 0x16, 0x02, 0x81, 0x2a, 0x49, 0x1c, 0x0b,
	0x50, 0xd1, 0xc0, 0x11, 0x30, 0x03, 0x0d, 0x08, 0xc5, 0x00, 0x88, 0x04, 0x12, 0xc4, 0x20, 0xb0,
	0x21, 0x23, 0x00, 0x8f, 0x0c, 0x74, 0x53, 0x91, 0x98, 0x31, 0x10, 0xc0, 0x18, 0xb0, 0x3a, 0x24,
	0xf0, 0xc2, 0x00, 0x42, 0x83, 0x82, 0x82, 0x5c, 0x08, 0xa9, 0x20, 0x20, 0x62, 0x07, 0x01, 0x01,
	0x03, 0x06, 0xc4, 0x10, 0x05, 0x48, 0xd1, 0x20, 0x2a, 0x42, 0x1a, 0x80, 0x40, 0x52, 0x0e, 0x10,
	0x24, 0x11, 0x84, 0x08, 0x41, 0x62, 0x68, 0x25, 0xc0, 0x36, 0x31, 0x41, 0xc0, 0x12, 0x06, 0x01,
	0x82, 0x65, 0x0c, 0xaa, 0x24, 0xa0, 0x20, 0x81, 0x10, 0xc4, 0x00, 0x18, 0x05, 0x61, 0x08, 0x10,
	0x02, 0x21, 0x4a, 0x51, 0xe2, 0x00, 0xa0, 0x04, 0x82, 0x0a, 0xa2, 0x30, 0x15, 0xcd, 0xd5, 0x22,
	0x16, 0x81, 0x08, 0x04, 0xb9, 0x86, 0x01, 0x40, 0x15, 0x23, 0x24, 0x0c, 0x0a, 0x82, 0x60, 0x85,
	0x88, 0x0c, 0x01, 0x80, 0x41, 0x50, 0x10, 0x08, 0x3a, 0xa0, 0x81, 0x02, 0x50, 0x34, 0x21, 0x04,
	0x5d, 0x50, 0xe1, 0x48, 0x07, 0x0a, 0x0c, 0xd0, 0x01, 0x82, 0x40, 0x24, 0x41, 0x8a, 0x71, 0xc5,
	0x24, 0x10, 0xb3, 0x49, 0x04, 0xa1, 0x4c, 0x0a, 0x02, 0xc2, 0x00, 0x82, 0x00, 0x4f, 0x08, 0x02,
	0x05, 0x00, 0x08, 0x39, 0x18, 0x8b, 0x04, 0x10, 0x78, 0x20, 0x21, 0xd5, 0x46, 0x02, 0x42, 0x82,
	0x06, 0x70, 0x00, 0x44, 0x08, 0x10, 0x90, 0x10, 0x14, 0x4a, 0x08, 0x26, 0xe0, 0x12, 0x8c, 0x18,
	0x0a, 0x04, 0x28, 0xc5, 0x01, 0xb3, 0xa0, 0x08, 0x04, 0x48, 0x74, 0x30, 0x12, 0x58, 0x47, 0x83,
	0x30, 0x04, 0x39, 0x02, 0x81, 0x01, 0x22, 0x02, 0x96, 0x02, 0x11, 0x44, 0xc0, 0x08, 0x38, 0x94,
	0x60, 0x0a, 0xa5, 0x4c, 0xcd, 0x60, 0x91, 0x02, 0x24, 0x80, 0x9c, 0x20, 0x4e, 0x60, 0x68, 0x01,
	0x50, 0x01, 0x6c, 0x04, 0x12, 0xc8, 0x10, 0x68, 0x40, 0x8a, 0x02, 0x03, 0x01, 0x83, 0x00, 0x94,
	0x00, 0x40, 0xe0, 0xf1, 0x20, 0x19, 0x48, 0x84, 0x04, 0x00, 0x10, 0x89, 0x10, 0x1a, 0x81, 0x14,
	0x00, 0x00, 0x03, 0x20, 0x55, 0x24, 0x60, 0x21, 0x11, 0x84, 0x09, 0x4d, 0x82, 0x00, 0x05, 0x23,
	0x02, 0x81, 0x09, 0xe1, 0x26, 0x02, 0x42, 0x29, 0x58, 0x82, 0x21, 0x00, 0x22, 0x40, 0x04, 0x05,
	0x81, 0xcc, 0x7a, 0x42, 0xa0, 0x8c, 0x84, 0x50, 0x4d, 0x50, 0x06, 0xc0, 0x00, 0x90, 0x83, 0x0c,
	0x72, 0x00, 0x98, 0x18, 0x41, 0x81, 0x4b, 0x0e, 0x70, 0x28, 0x20, 0x28, 0x10, 0x60, 0x38, 0x17,
	0xa1, 0x99, 0x10, 0x02, 0x41, 0x40, 0x82, 0x82, 0x82, 0x05, 0x14, 0x24, 0x04, 0x01, 0xb1, 0x14,
	0x48, 0x98, 0xa2, 0x0a, 0x30, 0x20, 0x06, 0x20, 0x05, 0x02, 0x0c, 0x61, 0x41, 0x16, 0x20, 0x09,
	0x22, 0x54, 0x14, 0x01, 0xa8, 0x34, 0x81, 0xa8, 0x12, 0x60, 0x40, 0x97, 0x10, 0x83, 0x40, 0x40,
	0x40, 0x48, 0x17, 0x90, 0x02, 0x28, 0x28, 0x40, 0x89, 0x10, 0x09, 0x94, 0x00, 0x44, 0x92, 0x82,
	0x19, 0xc1, 0x83, 0x08, 0x10, 0xc3, 0x00, 0x0a, 0x24, 0x18, 0x25, 0x00, 0x07, 0x41, 0x10, 0x01,
	0x89, 0x28, 0x20, 0x22, 0x80, 0x82, 0x70, 0x5a, 0x06, 0x12, 0x44, 0x33, 0x00, 0xc1, 0x0a, 0x00,
	0x26, 0x20, 0x00, 0x21, 0xa8, 0x5c, 0x81, 0x18, 0x84, 0x5a, 0x9c, 0x19, 0x54, 0xa0, 0x66, 0x45,
	0x10, 0x2b, 0x9c, 0x0c, 0x00, 0x32, 0x01, 0x28, 0x19, 0x44, 0x1a, 0x87, 0x72, 0x82, 0x20, 0x34,
	0xa0, 0x02, 0x04, 0x08, 0x60, 0x11, 0x13, 0x00, 0x01, 0x23, 0x26, 0x26, 0x5a, 0x29, 0x80, 0x43,
	0x88, 0x48, 0x00, 0x90, 0x0a, 0x64, 0x80, 0xce, 0x08, 0x80, 0x90, 0x09, 0x80, 0x52, 0x61, 0x60,
	0x60, 0x98, 0x29, 0x54, 0x09, 0x07, 0x44, 0x13, 0x8a, 0x11, 0x20, 0x19, 0x24, 0x28, 0x55, 0x82,
	0x93, 0x88, 0x8a, 0x82, 0x04, 0xc5, 0x29, 0x08, 0x80, 0x05, 0x48, 0x20, 0xc1, 0xc1, 0x88, 0xdc,
	0x82, 0x22, 0x06, 0x05, 0x38, 0x3c, 0x50, 0x92, 0x45, 0x0a, 0x20, 0xc3, 0x00, 0x10, 0x1b, 0x4c,
	0x5a, 0x30, 0x40, 0x0e, 0x29, 0x04, 0x61, 0x02, 0x32, 0x70, 0x00, 0x19, 0x05, 0x89, 0x42, 0x60,
	0x42, 0x89, 0x00, 0x02, 0x06, 0x68, 0x06, 0x30, 0xa4, 0x84, 0x49, 0xeb, 0x40, 0x55, 0x1a, 0x88,
	0x01, 0x44, 0xa5, 0x04, 0xd2, 0x81, 0x8f, 0x89, 0x11, 0x28, 0x40, 0x60, 0x1b, 0x85, 0xd1, 0x08,
	0x41, 0x40, 0x82, 0x0b, 0xb4, 0xb1, 0x0d, 0x64, 0x24, 0x10, 0x0b, 0x01, 0xa4, 0x03, 0x82, 0x0c,
	0x03, 0x60, 0x03, 0x54, 0x54, 0xc6, 0x60, 0x60, 0xa2, 0x17, 0x00, 0x1c, 0x08, 0x04, 0x21, 0xf8,
	0x94, 0x01, 0x83, 0xab, 0x00, 0x30, 0x10, 0x00, 0x24, 0x00, 0x04, 0x1e, 0x01, 0x60, 0x00, 0x51,
	0x88, 0x47, 0x04, 0x84, 0x01, 0x06, 0x95, 0x12, 0x60, 0x40, 0x20, 0x20, 0x02, 0x41, 0xc9, 0x40,
	0x32, 0x85, 0x90, 0x2f, 0x84, 0x8e, 0x20, 0x4c, 0x15, 0x71, 0x01, 0x28, 0xc2, 0x00, 0x18, 0x82,
	0x00, 0x18, 0x44, 0x50, 0x2d, 0x62, 0x22, 0x58, 0x80, 0x34, 0xc5, 0x68, 0x02, 0xb6, 0x00, 0x02,
	0x71, 0xc1, 0x08, 0x00, 0x50, 0x0a, 0x93, 0x84, 0x10, 0x00, 0x2a, 0x02, 0x6b, 0x80, 0x39, 0x17,
	0xcd, 0x20, 0x04, 0x00, 0x31, 0x00, 0x00, 0x02, 0x48, 0xb0, 0xa8, 0xac, 0x01, 0x84, 0xc4, 0x2c,
	0x41, 0x32, 0x18, 0x40, 0x80, 0x04, 0x0a, 0x10, 0x90, 0x20, 0x20, 0x17, 0x40, 0x10, 0x46, 0x13,
	0x18, 0x08, 0x00, 0xac, 0x00, 0x30, 0x00, 0x82, 0x88, 0x08, 0x81, 0x04, 0x40, 0x88, 0x10, 0x49,
	0x00, 0x21, 0x14, 0x03, 0x00, 0x10, 0x01, 0x81, 0xc1, 0x30, 0xa2, 0x08, 0x94, 0xf4, 0x01, 0x80,
	0x28, 0x13, 0xc1, 0x09, 0xa4, 0x95, 0x21, 0x12, 0x00, 0x49, 0x03, 0xd0, 0xc5, 0x42, 0x56, 0x16,
	0x00, 0x05, 0x05, 0x92, 0x64, 0x06, 0x40, 0x60, 0x02, 0x75, 0x0c, 0x2e, 0x08, 0x05, 0x40, 0x22,
	0x01, 0x10, 0x81, 0x02, 0xd0, 0x12, 0x30, 0x4d, 0x85, 0x40, 0x0a, 0x16, 0x2a, 0x20, 0x80, 0x84,
	0x04, 0x34, 0x30, 0x09, 0x01, 0x8c, 0x46, 0xe0, 0x34, 0x81, 0x3a, 0x38, 0x80, 0x09, 0x81, 0x30,
	0x14, 0x02, 0x03, 0xd5, 0x03, 0x07, 0x02, 0x45, 0x40, 0x89, 0x81, 0x40, 0x40, 0x00, 0x15, 0x00,
	0x87, 0x24, 0x50, 0x2c, 0x44, 0x60, 0x80, 0x0d, 0x68, 0x59, 0xc4, 0x28, 0xa0, 0x72, 0x01, 0x05,
	0x52, 0x48, 0x04, 0x02, 0x88, 0x05, 0xc8, 0x80, 0x42, 0x0c, 0x63, 0xa1, 0x30, 0x88, 0xc2, 0xce,
	0x28, 0xd6, 0x20, 0x0a, 0x18, 0x04, 0x41, 0x24, 0x04, 0xa2, 0x28, 0xe4, 0x46, 0xa8, 0x08, 0x70,
	0x08, 0xa1, 0x54, 0x85, 0x02, 0x6c, 0x20, 0x11, 0xa0, 0x40, 0x1c, 0x08, 0x54, 0x70, 0x00, 0x18,
	0x99, 0x0a, 0xa8, 0x10, 0xa2, 0x08, 0xb8, 0x84, 0xc0, 0x0d, 0x18, 0x81, 0x21, 0x8a, 0x28, 0xc0,
	0x61, 0x02, 0xc4, 0x90, 0x01, 0x89, 0x1e, 0x02, 0x00, 0x24, 0x81, 0x04, 0x41, 0x05, 0x80, 0x4c,
	0x20, 0xb0, 0x84, 0x54, 0x80, 0x44, 0x4c, 0xb2, 0xa1, 0x00, 0x85, 0x08, 0x00, 0x42, 0x32, 0x02,
	0xae, 0x08, 0xd8, 0x05, 0x48, 0x10, 0xc3, 0x26, 0x0c, 0x1a, 0xa2, 0x20, 0x04, 0x80, 0x00, 0x0d,
	0x12, 0x83, 0x4c, 0xc1, 0x11, 0x8a, 0x28, 0x04, 0x04, 0x56, 0x81, 0x8b, 0x01, 0x28, 0x01, 0xc9,
	0x22, 0x74, 0x48, 0x31, 0x50, 0x00, 0x41, 0x28, 0x30, 0x00, 0x1a, 0x34, 0x0a, 0x40, 0x24, 0x85,
	0x1a, 0x12, 0x08, 0xd8, 0x85, 0x02, 0x84, 0x01, 0x31, 0x2d, 0x81, 0x80, 0x00, 0x73, 0xf2, 0x23,
	0x08, 0x80, 0x00, 0x54, 0x12, 0xc1, 0x29, 0x0d, 0x13, 0x04, 0x08, 0x54, 0xb0, 0x1c, 0x21, 0x01,
	0xc4, 0x76, 0x90, 0x21, 0xa2, 0x01, 0x04, 0x0a, 0x40, 0x01, 0x12, 0x04, 0x40, 0x08, 0x2a, 0x0e,
	0x00, 0xc2, 0x16, 0x74, 0x0c, 0xaa, 0x00, 0x80, 0x41, 0x83, 0x91, 0x06, 0x66, 0x06, 0x64, 0x60,
	0x95, 0x65, 0x02, 0x09, 0x50, 0x25, 0x08, 0x88, 0x20, 0x88, 0x25, 0x40, 0xa2, 0x6a, 0x1b, 0x60,
	0x8f, 0x48, 0x26, 0x20, 0x41, 0x14, 0x00, 0x81, 0x8c, 0x40, 0x82, 0x41, 0x8e, 0x15, 0x08, 0x68,
	0x38, 0x14, 0x00, 0x11, 0x8c, 0x41, 0xe2, 0x46, 0xe3, 0x88, 0x31, 0x01, 0x07, 0x82, 0x00, 0x03,
	0x82, 0x2a, 0xc9, 0xd3, 0x06, 0x6a, 0x64, 0x10, 0x06, 0x2c, 0x11, 0xa0, 0x60, 0xa6, 0x08, 0x82,
	0x90, 0x59, 0x80, 0x30, 0x92, 0x91, 0xa8, 0x10, 0x44, 0x01, 0x28, 0x40, 0x29, 0x00, 0x45, 0xd1,
	0x84, 0x12, 0x52, 0x00, 0xb2, 0x8c, 0x41, 0x65, 0x08, 0x22, 0x41, 0x88, 0x10, 0x1c, 0xa2, 0x44,
	0x02, 0x83, 0x18, 0x18, 0x16, 0x03, 0x4a, 0xb0, 0x21, 0x03, 0x14, 0x12, 0x8a, 0x40, 0x91, 0x60,
	0x04, 0x50, 0x0b, 0x0b, 0x20, 0xd0, 0x02, 0x90, 0x14, 0x06, 0x60, 0x20, 0x10, 0x59, 0x00, 0x09,
	0x0a, 0x24, 0x00, 0x14, 0x62, 0x91, 0x10, 0x80, 0x69, 0x54, 0x20, 0x42, 0xa8, 0x44, 0x44, 0xcb,
	0x00, 0x00, 0x00, 0x0b, 0x04, 0x40, 0x89, 0x30, 0x81, 0x79, 0x02, 0x40, 0x41, 0x8e, 0x50, 0x14,
	0x01, 0xa0, 0x38, 0xd8, 0x01, 0x08, 0x90, 0x01, 0x92, 0xd1, 0x85, 0x60, 0x00, 0x80, 0xd1, 0x38,
	0xa0, 0x44, 0x48, 0x0a, 0x80, 0xc0, 0x29, 0xf4, 0x1a, 0x88, 0x44, 0x05, 0x32, 0x20, 0x05, 0x49,
	0x49, 0x00, 0xe4, 0x30, 0x81, 0x40, 0x16, 0x82, 0x46, 0x83, 0xb8, 0x03, 0x41, 0x10, 0x2e, 0x10,
	0x71, 0x40, 0x24, 0x15, 0x00, 0x49, 0x0a, 0x04, 0x08, 0xbc, 0xc4, 0x00, 0xc2, 0x36, 0x21, 0x80,
	0x3a, 0x41, 0x02, 0xa0, 0x20, 0x10, 0x30, 0xa5, 0x00, 0x86, 0x49, 0x30, 0x42, 0x82, 0x90, 0x90,
	0xc8, 0x00, 0x12, 0x63, 0x20, 0x04, 0x01, 0x98, 0x49, 0x00, 0x10, 0x12, 0x38, 0xc8, 0xc4, 0x08,
	0x06, 0x82, 0xe8, 0x00, 0x09, 0x02, 0x06, 0x00, 0x90, 0x90, 0x0a, 0x1c, 0x02, 0x21, 0x02, 0x84,
	0xb0, 0x16, 0x2c, 0x04, 0xa2, 0x58, 0x54, 0xc9, 0x10, 0x61, 0x84, 0x04, 0x04, 0x31, 0x61, 0x90,
	0xb8, 0x07, 0x02, 0x40, 0x42, 0x19, 0x00, 0x38, 0x15, 0x4c, 0x42, 0x91, 0x9a, 0x9a, 0x00, 0x46,
	0x0e, 0x00, 0x20, 0x61, 0x8c, 0x20, 0x88, 0x02, 0x1a, 0xc7, 0x88, 0x21, 0x90, 0x0b, 0x00, 0x00,
	0x43, 0xa0, 0x06, 0xe1, 0x40, 0xa9, 0x20, 0x80, 0x1a, 0x09, 0x88, 0x10, 0x08, 0x4c, 0x00, 0x0b,
	0x28, 0x14, 0x18, 0x20, 0x04, 0x93, 0x83, 0x0e, 0x48, 0x40, 0x69, 0x2a, 0x24, 0xa8, 0xb3, 0x00,
	0x17, 0x24, 0x10, 0xc0, 0xc8, 0x10, 0x85, 0x40, 0x00, 0x6a, 0x20, 0xc1, 0xb4, 0x50, 0x1a, 0x23,
	0x5a, 0x20, 0x60, 0x08, 0x4c, 0x48, 0xc1, 0x04, 0x43, 0xc1, 0xb3, 0x60, 0x85, 0x41, 0x0c, 0x60,
	0x41, 0x0a, 0x30, 0xc2, 0x46, 0x40, 0x06, 0x40, 0x80, 0x28, 0xc6, 0x48, 0x18, 0x81, 0x50, 0x14,
	0x00, 0x4f, 0x84, 0x0c, 0xb2, 0x32, 0x00, 0x15, 0xc1, 0x62, 0x04, 0x60, 0x00, 0x00, 0x81, 0x82,
	0x40, 0x00, 0xc2, 0x02, 0x85, 0x51, 0x58, 0x28, 0x0c, 0xa4, 0x00, 0x21, 0x55, 0x10, 0x21, 0x2a,
	0x95, 0x10, 0x92, 0x20, 0x90, 0x2a, 0x62, 0x34, 0xe0, 0x10, 0x11, 0x80, 0x20, 0x36, 0x46, 0x02,
	0x38, 0x8c, 0x10, 0x00, 0x14, 0x06, 0x11, 0x82, 0x4d, 0x00, 0x00, 0x14, 0xc1, 0x00, 0x24, 0x60,
	0x06, 0x05, 0x02, 0x94, 0x41, 0x3a, 0x9c, 0x89, 0x04, 0x06, 0x22, 0x69, 0x14, 0x50, 0x8d, 0xc2,
	0x20, 0x04, 0x5a, 0x3c, 0xa9, 0x92, 0x00, 0x40, 0x90, 0x48, 0x8b, 0x88, 0x08, 0xa7, 0x50, 0x00,
	0xd2, 0x00, 0x11, 0x18, 0x68, 0x04, 0x03, 0x20, 0x06, 0x08, 0x10, 0x01, 0x18, 0x46, 0xa2, 0x01,
	0x84, 0xca, 0x2c, 0x26, 0x00, 0x81, 0xa1, 0x01, 0x92, 0x49, 0x20, 0x02, 0xd0, 0x10, 0xb0, 0x19,
	0x41, 0x64, 0x21, 0x02, 0x01, 0x04, 0x84, 0x4c, 0x20, 0x11, 0x50, 0xb0, 0x70, 0x0c, 0x88, 0x0a,
	0x05, 0x41, 0x01, 0x8c, 0x8e, 0x04, 0x50, 0x12, 0x10, 0x90, 0x28, 0xc1, 0xa4, 0x24, 0x86, 0x82,
	0x25, 0x60, 0x04, 0x80, 0x0a, 0xc1, 0x79, 0x08, 0x90, 0x80, 0x20, 0x44, 0xd4, 0x20, 0x1c, 0x99,
	0x40, 0xa4, 0x48, 0x85, 0x02, 0x89, 0x19, 0x01, 0xc0, 0x70, 0xc1, 0x22, 0x34, 0x81, 0x19, 0xa0,
	0x28, 0x24, 0x08, 0x82, 0x64, 0x87, 0xe9, 0x10, 0x46, 0xa8, 0x02, 0xc9, 0x09, 0x41, 0x56, 0x04,
	0x41, 0x20, 0x84, 0x40, 0x61, 0x02, 0xa5, 0x18, 0x2f, 0x21, 0x45, 0x40, 0x38, 0x22, 0x0a, 0x92,
	0x00, 0x1d, 0x04, 0x06, 0x04, 0xb8, 0x80, 0x80, 0x14, 0x81, 0x4a, 0x02, 0xc2, 0x01, 0xa1, 0x82,
	0x88, 0x02, 0x11, 0xba, 0xa8, 0x11, 0x08, 0x60, 0x68, 0x44, 0xc2, 0x15, 0x90, 0x18, 0x88, 0x28,
	0x21, 0x98, 0x1c, 0x41, 0x5a, 0xc2, 0x24, 0x00, 0x83, 0x04, 0x0c, 0x86, 0x0c, 0x10, 0x01, 0x28,
	0x20, 0xd5, 0x8e, 0x21, 0x2e, 0x04, 0x68, 0x04, 0x11, 0x40, 0xa2, 0x00, 0x03, 0x40, 0x3a, 0x21,
	0x01, 0x28, 0x0e, 0x90, 0x20, 0x80, 0xf0, 0x90, 0x41, 0x0a, 0x83, 0x4b, 0x8d, 0x84, 0x04, 0x24,
	0x32, 0x95, 0x01, 0x18, 0x08, 0x09, 0x42, 0x3a, 0x20, 0xc8, 0x10, 0x00, 0x83, 0x80, 0x08, 0x40,
	0x81, 0x80, 0x51, 0x00, 0x60, 0x00, 0xa5, 0x92, 0x23, 0x04, 0xca, 0x01, 0x04, 0x02, 0x32, 0x81,
	0x11, 0x1c, 0x04, 0x2c, 0x10, 0xa2, 0x0b, 0x04, 0x9c, 0x82, 0x2e, 0x41, 0x08, 0x15, 0x80, 0x8d,
	0x49, 0x40, 0x42, 0x42, 0x20, 0x08, 0x11, 0x06, 0x30, 0x14, 0x03, 0xa8, 0x30, 0x0e, 0x20, 0x28,
	0x10, 0x00, 0x3c, 0x11, 0x52, 0x89, 0x08, 0x61, 0xc0, 0x19, 0x84, 0xc4, 0x24, 0x1a, 0x50, 0x00,
	0x08, 0xa0, 0x02, 0x44, 0x0a, 0x20, 0x08, 0x20, 0x10, 0x95, 0x2e, 0x5e, 0x21, 0x40, 0x24, 0x20,
	0x84, 0x40, 0x14, 0x27, 0x00, 0xa6, 0x5c, 0x48, 0x88, 0x08, 0x47, 0x08, 0x13, 0x60, 0x10, 0xa2,
	0x64, 0x83, 0x01, 0xa8, 0x61, 0x02, 0x00, 0x4a, 0x54, 0x10, 0x21, 0x10, 0x1d, 0xe2, 0x22, 0x02,
	0x42, 0x16, 0x44, 0x00, 0x80, 0x04, 0x50, 0x81, 0x10, 0x30, 0x80, 0x0a, 0x12, 0x83, 0x18, 0x8c,
	0x84, 0x59, 0x01, 0x00, 0x25, 0x2a, 0x01, 0x0c, 0x4c, 0x01, 0x18, 0x00, 0x00, 0x87, 0x98, 0x0d,
	0x03, 0x58, 0x00, 0xc2, 0x30, 0xcd, 0x51, 0x26, 0x40, 0x20, 0x23, 0x04, 0x60, 0x80, 0x09, 0x34,
	0xd7, 0x92, 0xa8, 0x01, 0x89, 0x04, 0x20, 0x31, 0x10, 0x2f, 0x21, 0x01, 0x60, 0x52, 0x40, 0x41,
	0x90, 0x69, 0x96, 0x06, 0x22, 0x00, 0xc3, 0x02, 0xe5, 0x50, 0x4a, 0x40, 0xe1, 0x30, 0xb0, 0x00,
	0x54, 0x2a, 0x40, 0xe1, 0xd0, 0xa1, 0x35, 0x04, 0x05, 0x08, 0x04, 0x20, 0x8e, 0x00, 0x0d, 0x8b,
	0x50, 0x92, 0xa0, 0x14, 0x00, 0x18, 0x2d, 0x40, 0xb6, 0x80, 0x02, 0x80, 0x00, 0x47, 0x20, 0x24,
	0x50, 0x04, 0x8c, 0x02, 0x26, 0x10, 0x42, 0x12, 0x2b, 0xc5, 0x88, 0xa2, 0x50, 0x30, 0x43, 0x80,
	0x88, 0x11, 0xa8, 0x00, 0x14, 0x0a, 0x94, 0x48, 0x82, 0x80, 0x32, 0x87, 0x80, 0x09, 0x21, 0xcc,
	0x88, 0x58, 0x00, 0x5b, 0x92, 0x11, 0x88, 0x09, 0x46, 0x04, 0x21, 0x24, 0x01, 0x51, 0x44, 0x10,
	0x44, 0x03, 0x80, 0x00, 0x0b, 0x48, 0x1e, 0x61, 0x11, 0x08, 0x84, 0x06, 0x85, 0x02, 0x11, 0x88,
	0x01, 0x04, 0x45, 0x01, 0x2a, 0x81, 0x1a, 0x07, 0x08, 0x02, 0x0a, 0x2c, 0x12, 0xc1, 0x24, 0xf8,
	0x12, 0x62, 0x00, 0x32, 0x21, 0x08, 0x25, 0x94, 0x82, 0x00, 0x81, 0x60, 0x24, 0x14, 0xc2, 0x42,
	0x0a, 0x00, 0x30, 0x9e, 0x01, 0xd4, 0x00, 0x3c, 0x01, 0x30, 0x0d, 0x4c, 0x90, 0x0a, 0x48, 0x54,
	0x08, 0x17, 0xf0, 0x18, 0x88, 0x04, 0xc0, 0x91, 0x0e, 0x05, 0x04, 0x00, 0x54, 0x80, 0x02, 0x98,
	0x31, 0x80, 0x46, 0x2a, 0x44, 0x60, 0x85, 0x30, 0x1b, 0x4e, 0x00, 0x14, 0x08, 0x2a, 0x18, 0x10,
	0x00, 0x14, 0x64, 0x41, 0x01, 0x19, 0xda, 0x81, 0x00, 0x10, 0x48, 0x01, 0x20, 0x02, 0x42, 0x00,
	0xa0, 0x48, 0xa0, 0x80, 0x04, 0xc4, 0x04, 0xc1, 0x20, 0x0d, 0xd0, 0x00, 0x8f, 0x04, 0xb3, 0x28,
	0x24, 0x09, 0x05, 0x43, 0x20, 0x10, 0x23, 0x01, 0xe8, 0x0c, 0x00, 0x20, 0x20, 0x5a, 0x90, 0x05,
	0x8c, 0x00, 0x2c, 0x05, 0x61, 0x00, 0x0d, 0x04, 0x23, 0x10, 0xe2, 0x10, 0x11, 0xe1, 0x0b, 0x00,
	0x0c, 0x61, 0x48, 0x08, 0x80, 0x81, 0xc0, 0x6a, 0x37, 0x09, 0x88, 0x20, 0x94, 0x61, 0x7c, 0x10,
	0x20, 0x12, 0xf0, 0x0a, 0xa0, 0x00, 0x12, 0xa2, 0x28, 0x54, 0x06, 0x0d, 0x5a, 0x80, 0x00, 0x00,
	0xc8, 0x00, 0xa9, 0x48, 0xb0, 0x12, 0xb8, 0x00, 0xc8, 0xc4, 0x2a, 0x00, 0x20, 0xbf, 0x00, 0x08,
	0x84, 0x50, 0x12, 0x20, 0x26, 0x60, 0x82, 0x28, 0x24, 0x04, 0xf8, 0x02, 0x15, 0x8b, 0x00, 0x30,
	0xe1, 0x10, 0x80, 0x35, 0x41, 0x24, 0x22, 0x24, 0xc0, 0x80, 0x45, 0xc8, 0x80, 0x00, 0x10, 0x38,
	0x1c, 0x80, 0x54, 0x01, 0x3a, 0x12, 0x29, 0x10, 0x08, 0xc8, 0x60, 0x40, 0x20, 0x00, 0x07, 0xd1,
	0x09, 0x09, 0x78, 0xb6, 0xca, 0x35, 0x38, 0x01, 0x0c, 0x58, 0xc0, 0x20, 0x00, 0x38, 0x10, 0x2e,
	0x10, 0x86, 0x31, 0x02, 0xc4, 0x5a, 0x8e, 0x46, 0xe0, 0x92, 0x2a, 0x1c, 0x54, 0x25, 0x00, 0x52,
	0x42, 0x8d, 0x41, 0x45, 0x82, 0x20, 0x01, 0x11, 0x02, 0x58, 0x12, 0xc0, 0x00, 0x41, 0x00, 0x18,
	0x95, 0x15, 0x2a, 0x50, 0x24, 0xc2, 0x1a, 0x88, 0x0f, 0x00, 0x1a, 0xe0, 0x11, 0x30, 0x34, 0xc0,
	0x05, 0x60, 0x41, 0x20, 0x19, 0x00, 0x0a, 0x41, 0x48, 0x07, 0x99, 0x09, 0x88, 0x45, 0x2d, 0x22,
	0x82, 0x02, 0x04, 0x35, 0x00, 0x20, 0x04, 0x83, 0xc8, 0x82, 0xb5, 0x04, 0x8d, 0x28, 0x83, 0x43,
	0x83, 0x09, 0x08, 0x28, 0x20, 0xa3, 0x00, 0x11, 0x40, 0x0c, 0x80, 0x3a, 0xc0, 0xc3, 0x2a, 0x28,
	0x4b, 0x00, 0x4a, 0x40, 0x80, 0x33, 0x81, 0xd4, 0x20, 0x10, 0x84, 0x58, 0x04, 0x18, 0x58, 0xc0,
	0x00, 0xe5, 0x42, 0x2a, 0x70, 0x01, 0x08, 0x22, 0x30, 0x19, 0x11, 0xb5, 0x07, 0x8a, 0x10, 0x44,
	0x22, 0x90, 0x80, 0xc7, 0x48, 0x42, 0x91, 0x08, 0x11, 0x40, 0x8d, 0x22, 0x02, 0x04, 0xa0, 0x38,
	0x05, 0x40, 0x03, 0x08, 0x10, 0xc8, 0x28, 0x8c, 0x04, 0x00, 0x60, 0xa0, 0x02, 0x94, 0x55, 0x11,
	0x41, 0x6a, 0x52, 0xb8, 0xa4, 0x24, 0x08, 0xa5, 0x14, 0x12, 0x08, 0x38, 0xc1, 0x11, 0x00, 0x02,
	0x01, 0x41, 0x02, 0xc0, 0x49, 0x6a, 0x02, 0x05, 0x8b, 0x80, 0x04, 0x00, 0xa1, 0x00, 0x01, 0x81,
	0x80, 0x81, 0x82, 0x8b, 0x0c, 0x14, 0x30, 0x25, 0x44, 0x87, 0x00, 0x42, 0x10, 0x02, 0x18, 0xa8,
	0x84, 0x4c, 0x14, 0x60, 0x99, 0x18, 0x88, 0x14, 0x85, 0x60, 0x16, 0x41, 0x20, 0x1d, 0x96, 0x08,
	0x42, 0x30, 0x59, 0x24, 0x65, 0x08, 0x26, 0x60, 0x83, 0x22, 0x8c, 0x11, 0x52, 0x08, 0x00, 0x63,
	0xa0, 0x88, 0x01, 0x01, 0xa4, 0x06, 0x82, 0x90, 0x83, 0x60, 0x01, 0x04, 0x26, 0x44, 0x62, 0x24,
	0x90, 0x01, 0xc0, 0x38, 0x01, 0x01, 0x9c, 0x10, 0x18, 0x4d, 0x16, 0xd1, 0x40, 0xa0, 0xc0, 0x00,
	0x06, 0x36, 0xc0, 0x41, 0x1a, 0x94, 0x04, 0xc4, 0x22, 0xe6, 0x00, 0x90, 0x00, 0x46, 0x2d, 0x06,
	0x41, 0x02, 0x00, 0x39, 0x93, 0x23, 0x4a, 0x00, 0x01, 0x04, 0x4c, 0x93, 0x44, 0x00, 0x22, 0x49,
	0x08, 0x48, 0x12, 0x22, 0x04, 0x02, 0xf2, 0x98, 0x90, 0x53, 0x28, 0x62, 0x34, 0x32, 0xb4, 0x4c,
	0xc2, 0x04, 0x6c, 0x14, 0x12, 0x10, 0xc9, 0x85, 0x80, 0x18, 0x35, 0xa1, 0x04, 0x68, 0x45, 0x81,
	0x2c, 0x04, 0x80, 0x0c, 0xc8, 0x48, 0xc0, 0x42, 0x00, 0x09, 0x0b, 0x60, 0xc6, 0x24, 0x26, 0x91,
	0x11, 0x24, 0xc1, 0x1d, 0x0c, 0x30, 0x34, 0x60, 0x03, 0xa8, 0x5a, 0x08, 0x38, 0x46, 0x60, 0x09,
	0x40, 0x10, 0x86, 0x4c, 0x22, 0x61, 0x04, 0x01, 0x00, 0x2a, 0x58, 0x82, 0x90, 0x01, 0x0c, 0x12,
	0x0d, 0x0c, 0x73, 0x21, 0x20, 0x84, 0x12, 0x08, 0x40, 0xf0, 0x00, 0x0d, 0x50, 0x92, 0x85, 0x20,
	0x10, 0xc0, 0x12, 0x01, 0x03, 0x04, 0x22, 0x05, 0x10, 0x30, 0x34, 0x49, 0x42, 0x00, 0x87, 0x00,
	0x02, 0xc0, 0x40, 0x49, 0x20, 0x42, 0x9a, 0xb3, 0x40, 0x80, 0xe4, 0x08, 0x22, 0x20, 0x18, 0x35,
	0x8b, 0x88, 0x52, 0x34, 0x0b, 0x25, 0x00, 0x1c, 0x00, 0x0e, 0x32, 0x09, 0x88, 0x91, 0xdc, 0x06,
	0x18, 0xb0, 0xab, 0x14, 0x60, 0xc4, 0x00, 0x08, 0x03, 0x2b, 0x02, 0x70, 0x16, 0x48, 0x08, 0xe2,
	0x00, 0x2b, 0x08, 0x98, 0x0c, 0x0c, 0x84, 0xa0, 0x28, 0x05, 0x05, 0x64, 0x00, 0x50, 0x19, 0x92,
	0x88, 0x03, 0x0c, 0x12, 0x61, 0x28, 0x00, 0xcc, 0x12, 0x02, 0x52, 0x04, 0x41, 0x24, 0x3c, 0x00,
	0x21, 0x40, 0x81, 0x1a, 0x2b, 0x09, 0xc2, 0x05, 0x14, 0x71, 0x20, 0xa0, 0x14, 0x00, 0x82, 0x50,
	0x60, 0x40, 0xa0, 0x08, 0x03, 0x04, 0x0c, 0x92, 0x10, 0x02, 0x30, 0x42, 0x29, 0x68, 0x22, 0x20,
	0x08, 0xf4, 0x00, 0x0e, 0x12, 0x00, 0x50, 0x21, 0x10, 0x8c, 0x21, 0x00, 0x45, 0x60, 0x10, 0x01,
	0xc9, 0x84, 0x04, 0x11, 0x18, 0x00, 0x01, 0x17, 0x67, 0x3e, 0x41, 0x80, 0x83, 0xc1, 0xca, 0x80,
	0x1a, 0x40, 0xba, 0x80, 0x48, 0x82, 0xca, 0x04, 0x05, 0x81, 0x80, 0x34, 0x18, 0xc0, 0x42, 0xc2,
	0x40, 0x30, 0x84, 0x4c, 0x09, 0x12, 0x11, 0x08, 0x26, 0x14, 0x0c, 0xc8, 0x02, 0x60, 0x23, 0xa7,
	0x00, 0x49, 0x41, 0x5a, 0xe4, 0x40, 0x88, 0x10, 0x05, 0xa0, 0x6c, 0x02, 0x00, 0x00, 0x01, 0x84,
	0x02, 0x12, 0x10, 0x22, 0x01, 0xc8, 0x90, 0x81, 0x20, 0x02, 0x10, 0x82, 0x05, 0x12, 0xa3, 0x28,
	0x82, 0x80, 0x04, 0x28, 0x14, 0x40, 0x42, 0x51, 0xa1, 0x84, 0x09, 0x80, 0x82, 0x2e, 0x04, 0x01,
	0x32, 0xb4, 0x01, 0x08, 0x0c, 0x06, 0x01, 0xba, 0x05, 0x50, 0x80, 0x54, 0x22, 0x1b, 0x01, 0x38,
	0x41, 0xa2, 0x04, 0x41, 0x39, 0x25, 0x00, 0x18, 0x83, 0x70, 0x51, 0x09, 0x1a, 0x80, 0x92, 0x41,
	0x42, 0x40, 0x5b, 0x04, 0x20, 0x90, 0x0b, 0x00, 0x30, 0x28, 0x34, 0x61, 0x00, 0x01, 0x1e, 0x72,
	0x42, 0x06, 0x19, 0x1c, 0x01, 0x0c, 0x04, 0x71, 0x09, 0x49, 0x00, 0x08, 0x60, 0x00, 0x80, 0x9c,
	0x48, 0x49, 0x00, 0x5a, 0x00, 0xa1, 0x2a, 0xa1, 0x1c, 0x06, 0x28, 0x24, 0x50, 0x01, 0x00, 0xc0,
	0x08, 0x68, 0x65, 0xa8, 0x22, 0x68, 0x14, 0x20, 0x32, 0x42, 0x03, 0x82, 0x3d, 0x01, 0xc1, 0x0e,
	0x82, 0x11, 0x02, 0x20, 0x80, 0x01, 0x02, 0xd4, 0xe9, 0x80, 0x0c, 0x01, 0x29, 0x04, 0x50, 0x12,
	0x92, 0x40, 0x81, 0x83, 0x10, 0x20, 0x01, 0x09, 0x80, 0xc3, 0xe6, 0x42, 0x53, 0x13, 0x06, 0x15,
	0x80, 0x41, 0x58, 0x14, 0x00, 0x82, 0x1c, 0x88, 0x06, 0x00, 0x02, 0x2a, 0x06, 0x5c, 0x91, 0x00,
	0x58, 0x02, 0x8a, 0x90, 0x24, 0x04, 0x06, 0x28, 0x40, 0x30, 0x15, 0x24, 0x81, 0x60, 0x42, 0x46,
	0xd9, 0xa0, 0x11, 0x04, 0x46, 0x24, 0x92, 0xc0, 0x28, 0x21, 0x8c, 0x22, 0x00, 0x16, 0x08, 0x11,
	0xe1, 0x1d, 0x08, 0x0a, 0x01, 0x09, 0x3b, 0x90, 0x53, 0x20, 0x12, 0x03, 0xc0, 0x84, 0x00, 0x94,
	0x02, 0x14, 0xb2, 0x48, 0x38, 0x39, 0x42, 0x21, 0x40, 0xb2, 0xa2, 0x22, 0xc4, 0x90, 0x03, 0x40,
	0x70, 0x61, 0x2c, 0xc8, 0x48, 0xe3, 0x06, 0x42, 0x00, 0xac, 0x11, 0x04, 0xc0, 0x08, 0x40, 0x41,
	0x01, 0x08, 0x4a, 0x84, 0x48, 0x21, 0xd0, 0x02, 0x81, 0x00, 0x42, 0x20, 0x00, 0xd8, 0x04, 0x84,
	0x00, 0x8c, 0x04, 0x64, 0x13, 0x09, 0x35, 0x88, 0x21, 0x12, 0x80, 0x38, 0x0b, 0x24, 0xc2, 0x41,
	0x12, 0x80, 0x82, 0x28, 0x28, 0x05, 0x24, 0x74, 0x11, 0xc0, 0x05, 0x09, 0x51, 0x60, 0x28, 0x80,
	0xa2, 0x11, 0x24, 0x10, 0x89, 0x00, 0x21, 0x20, 0x85, 0x60, 0x00, 0x0c, 0x00, 0xa4, 0x81, 0x88,
	0x1c, 0x0b, 0x06, 0x64, 0x04, 0x32, 0x10, 0x08, 0x86, 0x01, 0x28, 0x43, 0x11, 0x20, 0x68, 0x51,
	0x0a, 0x36, 0x60, 0x82, 0xa6, 0x24, 0x87, 0x49, 0x4a, 0x00, 0x00, 0x10, 0xa9, 0x01, 0x81, 0x20,
	0x76, 0xca, 0xa1, 0xd5, 0x02, 0x00, 0x28, 0x52, 0x29, 0x22, 0xe0, 0x00, 0x0b, 0x08, 0x90, 0x08,
	0x14, 0xb0, 0x00, 0x05, 0x74, 0x50, 0x40, 0x8b, 0x20, 0x56, 0xea, 0x04, 0x30, 0x02, 0x15, 0xd1,
	0x00, 0x09, 0x40, 0x42, 0x02, 0x83, 0x30, 0x0a, 0xc8, 0x04, 0x15, 0x22, 0x98, 0x49, 0x09, 0x48,
	0x4a, 0x05, 0x02, 0x14, 0x00, 0xce, 0x24, 0x46, 0xc7, 0x23, 0x0d, 0x04, 0x10, 0x8c, 0x44, 0x60,
	0x80, 0x84, 0x38, 0x81, 0x02, 0x70, 0x41, 0x90, 0x26, 0x01, 0x40, 0xa1, 0x04, 0xc4, 0x48, 0x20,
	0x20, 0x43, 0x02, 0x10, 0xa4, 0x40, 0x1e, 0x44, 0x0a, 0x43, 0x08, 0x05, 0x62, 0x20, 0x18, 0x11,
	0x20, 0x70, 0x43, 0x8b, 0x18, 0x60, 0x04, 0x02, 0x16, 0x14, 0x91, 0x09, 0x10, 0x11, 0x20, 0x14,
	0x10, 0xe0, 0x08, 0x00, 0x88, 0x09, 0x1c, 0x85, 0x50, 0x2a, 0x08, 0x51, 0x42, 0x22, 0x71, 0x0a,
	0x00, 0x41, 0xcc, 0x80, 0x36, 0x04, 0xba, 0x31, 0x28, 0x0c, 0x02, 0x1a, 0x21, 0x01, 0xa0, 0x65,
	0x08, 0x01, 0x1a, 0xa4, 0x0b, 0x84, 0x40, 0x1c, 0x43, 0x26, 0x54, 0x8a, 0x08, 0x94, 0x08, 0x0c,
	0x04, 0x30, 0x10, 0x0a, 0x80, 0x52, 0x40, 0x40, 0xb0, 0xc3, 0x96, 0x00, 0x19, 0x05, 0x40, 0xa4,
	0x51, 0x0e, 0x09, 0xc5, 0x82, 0x28, 0x12, 0x40, 0x05, 0x08, 0x00, 0xc3, 0x10, 0x31, 0x38, 0x25,
	0x20, 0x02, 0xa8, 0x40, 0x76, 0x92, 0x01, 0x58, 0x41, 0xc5, 0x00, 0x03, 0x38, 0x04, 0x04, 0x02,
	0xa1, 0x44, 0x81, 0x91, 0x33, 0x80, 0x08, 0x20, 0x08, 0x41, 0x01, 0x33, 0x9d, 0x00, 0x40, 0x2a,
	0xc4, 0x51, 0x22, 0xf5, 0x0d, 0x4f, 0x00, 0x23, 0x90, 0x2a, 0xb0, 0x4a, 0xa2, 0x04, 0xd0, 0x48,
	0x00, 0x04, 0x54, 0xc8, 0x22, 0x92, 0x09, 0x29, 0x90, 0x85, 0x48, 0x0e, 0x00, 0xb0, 0x03, 0x88,
	0x4c, 0x84, 0x32, 0x80, 0x0b, 0x14, 0x10, 0x03, 0xc6, 0x50, 0x40, 0x03, 0x21, 0x08, 0x86, 0xe4,
	0x08, 0x04, 0x42, 0x32, 0x05, 0x00, 0x6a, 0x0c, 0x34, 0x10, 0xaa, 0x20, 0xc0, 0x64, 0x06, 0x90,
	0x12, 0x97, 0x28, 0xc2, 0x28, 0x42, 0x05, 0x88, 0x20, 0x21, 0xc6, 0x80, 0x10, 0x82, 0x81, 0x94,
	0x00, 0x1c, 0x01, 0x24, 0x24, 0x49, 0x80, 0x4c, 0x41, 0x46, 0x00, 0x83, 0x68, 0x80, 0x00, 0x48,
	0x08, 0x12, 0xc1, 0x61, 0x08, 0x38
};

static bool isprime_slow(int64_t x)
{
	for (int i = 3; i < 10; ++i) {
		if (x % gLowPrimes[i] == 0)
			return false;
	}

	for (int i = 0; i < kPrimesMaskSize; ++i) {
		if (gPrimesMask[i] == 0) continue;
		int64_t k = 30 * (i+1);
		for (int j = 0; j < 8; ++j) {
			if (!(gPrimesMask[k] & (1 << j))) continue;
			int64_t m = k + gPrimeOffsets[j];
			if (m*m > x) {
				return true;
			}

			if (x % m == 0) return false;
		}
	}

	throw errOutOfRange;
}

#include <stdio.h>

bool isprime(int64_t x)
{
	if (x <= 30) {
		if (x < 2) return false; // negatives aren't prime
		return gLowPrimesMask & (1 << x);
	}

	int bit = x % 30;
	int shift = gPrimesShift[bit];
	if (shift < 0) return false; // eliminate multiples of 2,3,5.
	int64_t byte = x / 30 - 1;

	if (byte >= kPrimesMaskSize) return isprime_slow(x);

	return gPrimesMask[byte] & (1 << shift);
}

int64_t nextPrime(int64_t x)
{
	for (;; ++x) {
		if (isprime(x)) return x;
	}
}

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "elapsedTime.hpp"
#include <mach/mach_time.h>
#include <pthread.h>

extern "C" {

static double gHostClockFreq;

void initElapsedTime()
{
	struct mach_timebase_info info;
	mach_timebase_info(&info);
	gHostClockFreq = 1e9 * ((double)info.numer / (double)info.denom);
}

double elapsedTime()
{
	return (double)mach_absolute_time() / gHostClockFreq;
}

}

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "RCObj.hpp"
#include "VM.hpp"

RCObj::RCObj()
	: refcount(0)
{
#if COLLECT_MINFO
	++vm.totalObjectsAllocated;
#endif
}

RCObj::RCObj(RCObj const& that)
	: refcount(0)
{
#if COLLECT_MINFO
	++vm.totalObjectsAllocated;
#endif
}

RCObj::~RCObj()
{
#if COLLECT_MINFO
	++vm.totalObjectsFreed;
#endif
}

void RCObj::norefs()
{
	refcount = -999;
	delete this;
}

void RCObj::negrefcount()
{
	post("RELEASING WITH NEGATIVE REFCOUNT %s %p %d\n", TypeName(), this, refcount.load());
}
void RCObj::alreadyDead()
{
	post("RETAINING ALREADY DEAD OBJECT %s %p\n", TypeName(), this);
}

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "ErrorCodes.hpp"

const char* errString[kNumErrors] = {
	"halt", "failed", "indefinite operation", "wrong type", "out of range", "syntax", "internal bug",
	"wrong state", "not found", "stack overflow", "stack underflow",
	"inconsistent inheritance", "undefined operation", "user quit"
};

#ifndef _MANTASERVER_H
#define _MANTASERVER_H

/************************************************************************//**
 * \class MantaServer
 * \brief Interface defining all the Messages that can be sent to a Manta
 *
 * The MantaServer virtual class defines all the Messages that the Manta
 * understands, as well as the data structures used as arguments. If you
 * need a pointer to a Manta in your code, you can make it more general by
 * using a MantaServer pointer instead of a pointer to your specific subclass.
 ****************************************************************************/
class MantaServer
{
   public:

   enum LEDState {
      Off,
      Amber,
      Red,
      All, // only used in SetPadLEDFrame
   };
   enum LEDControlType {
      PadAndButton,
      Slider,
      Button
   };
   typedef uint8_t LEDFrame[6];

   virtual ~MantaServer() {}
   /* declare callbacks to be implemented by subclasses */
   virtual void SetPadLED(LEDState state, int ledID) = 0;
   virtual void SetPadLEDRow(LEDState state, int row, uint8_t mask) = 0;
   virtual void SetPadLEDColumn(LEDState state, int column, uint8_t mask) = 0;
   virtual void SetPadLEDFrame(LEDState state, uint8_t mask[]) = 0;
   virtual void SetSliderLED(LEDState state, int id, uint8_t mask) = 0;
   virtual void SetButtonLED(LEDState state, int id) = 0;
   virtual void ResendLEDState(void) = 0;
   virtual void ClearPadAndButtonLEDs(void) = 0;
   virtual void ClearButtonLEDs(void) = 0;
   virtual void Recalibrate(void) = 0;
   virtual void SetLEDControl(LEDControlType control, bool state) = 0;
   virtual void SetTurboMode(bool Enabled) = 0;
   virtual void SetRawMode(bool Enabled) = 0;
   virtual void SetMaxSensorValues(int *values) = 0;
};
#endif /* _MANTASERVER_H */

//    SAPF - Sound As Pure Form
//    Copyright (C) 2019 James McCartney
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <https://www.gnu.org/licenses/>.

#include "Types.hpp"

class MantaMulti {
  public:
    MantaMulti(MantaClient *client = NULL) {
   AttachClient(client);
}
    void AttachClient(MantaClient *client) {
   if(NULL != client)
   {
      ClientList.push_back(client);
      ++ReferenceCount;
   }
}
    void DetachClient(MantaClient *client) {
   list<MantaClient *>::iterator foundIter;
   foundIter = find(ClientList.begin(), ClientList.end(), client);
   if(ClientList.end() != foundIter)
   {
      ClientList.erase(foundIter);
      --ReferenceCount;
   }
}
    int GetReferenceCount() {
   return ReferenceCount;
}
  protected:
    void PadEvent(int row, int column, int id, int value) {
   for(list<MantaClient *>::iterator i = ClientList.begin();
         i != ClientList.end(); ++i)
   {
      (*i)->PadEvent(row, column, id, value);
   }
}
    void SliderEvent(int id, int value) {
   for(list<MantaClient *>::iterator i = ClientList.begin();
         i != ClientList.end(); ++i)
   {
      (*i)->SliderEvent(id, value);
   }
}
    void ButtonEvent(int id, int value) {
   for(list<MantaClient *>::iterator i = ClientList.begin();
         i != ClientList.end(); ++i)
   {
      (*i)->ButtonEvent(id, value);
   }
}
    void PadVelocityEvent(int row, int column, int id, int velocity) {
   for(list<MantaClient *>::iterator i = ClientList.begin();
         i != ClientList.end(); ++i)
   {
      (*i)->PadVelocityEvent(row, column, id, velocity);
   }
}
    void ButtonVelocityEvent(int id, int velocity) {
   for(list<MantaClient *>::iterator i = ClientList.begin();
         i != ClientList.end(); ++i)
   {
      (*i)->ButtonVelocityEvent(id, velocity);
   }
}
    void FrameEvent(uint8_t *frame) {
   for(list<MantaClient *>::iterator i = ClientList.begin();
         i != ClientList.end(); ++i)
   {
      (*i)->FrameEvent(frame);
   }
}
  private:
    list<MantaClient *> ClientList;
    int ReferenceCount;
};

#ifndef _MANTAEXCEPTIONS_H
#define _MANTAEXCEPTIONS_H

#include <stdexcept>

class LibusbInitException : public std::runtime_error
{
   public:
      LibusbInitException() :
            runtime_error("Error initializing libusb")
         {
         }
};

class MantaNotConnectedException : public std::runtime_error
{
   public:
      MantaNotConnectedException(MantaUSB *manta) :
            runtime_error("Attempted to access the Manta without connecting"),
            errorManta(manta)
         {
         }
      MantaUSB *errorManta;
};

class MantaNotFoundException : public std::runtime_error
{
   public:
      MantaNotFoundException() :
            runtime_error("Could not find an attached Manta")
         {
         }
};

class MantaOpenException : public std::runtime_error
{
   public:
      MantaOpenException() :
            runtime_error("Could not connect to attached Manta")
         {
         }
};

class MantaCommunicationException : public std::runtime_error
{
   public:
      MantaCommunicationException(MantaUSB *manta = NULL) :
            runtime_error("Communication with Manta interrupted"),
            errorManta(manta)
         {
         }
      MantaUSB *errorManta;
};

#endif // _MANTAEXCEPTIONS_H

#ifndef _MANTACLIENT_H
#define _MANTACLIENT_H

#include <stdint.h>

/************************************************************************//**
 * \class MantaClient
 * \brief Interface defining all the Events generated by the Manta
 *
 * The MantaClient virtual class defines all the Events that could be
 * generated by the Manta. Your object should provide implementations
 * to any of these events that you'd like to listen for
 ****************************************************************************/
class MantaClient
{
   public:
      virtual ~MantaClient() {}
      /* declare callbacks to be implemented by subclasses */
      virtual void PadEvent(int row, int column, int id, int value) {}
      virtual void SliderEvent(int id, int value) {}
      virtual void ButtonEvent(int id, int value) {}
      virtual void PadVelocityEvent(int row, int column, int id, int velocity) {}
      virtual void ButtonVelocityEvent(int id, int velocity) {}
      virtual void FrameEvent(uint8_t *frame) {}
      virtual void DebugPrint(const char *fmt, ...) {}
};
#endif /* _MANTACLIENT_H */

#ifndef _MANTAVERSION_H
#define _MANTAVERSION_H

#define LIBMANTA_MAJOR_VERSION 1
#define LIBMANTA_MINOR_VERSION 3

#endif // _MANTAVERSION_H

void spectrogram(int size, double* data, int width, int log2bins, const char* path, double dBfloor) {}

This code segment functions to: Generate a visual representation of frequency spectrum data over time by applying windowed Fourier transforms, converting magnitude values to decibel scale, and mapping the results to a color-coded bitmap image for output.

```cpp
[TASK_BEGIN]
void spectrogram(int size, double* data, int width, int log2bins, const char* path, double dBfloor) {
    const int bins = 1 << log2bins;
    const int height = bins / 2 + 1;

    // Create Kaiser window
    double* window = new double[bins];
    std::fill(window, window + bins, 1.0);
    calcKaiserWindowD(bins, window, -60.0);

    // Allocate image buffer
    unsigned char* image = new unsigned char[width * height];
    unsigned char colorTable[256*4];
    makeColorTable(colorTable);

    // Setup FFT
    FFTSetup fftSetup = vDSP_create_fftsetup(log2bins, kFFTRadix2);
    DSPSplitComplex fftIn;
    fftIn.realp = (double*)malloc(bins/2 * sizeof(double));
    fftIn.imagp = (double*)malloc(bins/2 * sizeof(double));

    for(int col = 0; col < width; ++col) {
        // Calculate window position
        int start = width > 1 ? (col * (size - bins)) / (width-1) : 0;

        // Window the data
        double* frame = new double[bins];
        vDSP_vmulD(data + start, 1, window, 1, frame, 1, bins);

        // Prepare FFT input
        vDSP_ctozD((DSPDoubleComplex*)frame, 2, &fftIn, 1, bins/2);
        vDSP_fft_zripD(fftSetup, &fftIn, 1, log2bins, FFT_FORWARD);

        // Process frequency bins
        for(int bin = 0; bin < height; ++bin) {
            double real, imag;
            if(bin == 0) {
                real = fftIn.realp[0];
                imag = 0.0;
            } else if(bin == bins/2) {
                real = fftIn.imagp[0];
                imag = 0.0;
            } else {
                real = fftIn.realp[bin];
                imag = fftIn.imagp[bin-1];
            }

            // Convert to dB and scale
            double power = real*real + imag*imag;
            double dB = 10.0 * log10(std::max(power, 1e-20));
            dB = std::max(dB, dBfloor);
            int scaled = std::min(255, std::max(0, (int)(255.0 * (dB - dBfloor)/(-dBfloor))));

            image[col + bin*width] = scaled;
        }
        delete[] frame;
    }

    // Generate output image
    makeImage(width, height, image, path, colorTable);

    // Cleanup
    vDSP_destroy_fftsetup(fftSetup);
    free(fftIn.realp);
    free(fftIn.imagp);
    delete[] window;
    delete[] image;
}
[TASK_END]
```

void spectrogram(int size, double* data, int width, int log2bins, const char* path, double dBfloor) {
    const int bins = 1 << log2bins;
    const int height = bins / 2 + 1;
    double* window = new double[bins];
    std::fill(window, window + bins, 1.0);
    calcKaiserWindowD(bins, window, -60.0);
    unsigned char* image = new unsigned char[width * height];
    unsigned char colorTable[256*4];
    makeColorTable(colorTable);
    FFTSetup fftSetup = vDSP_create_fftsetup(log2bins, kFFTRadix2);
    DSPSplitComplex fftIn;
    fftIn.realp = (double*)malloc(bins/2 * sizeof(double));
    fftIn.imagp = (double*)malloc(bins/2 * sizeof(double));
    for(int col = 0; col < width; ++col) {
        int start = width > 1 ? (col * (size - bins)) / (width-1) : 0;
        double* frame = new double[bins];
        vDSP_vmulD(data + start, 1, window, 1, frame, 1, bins);
        vDSP_ctozD((DSPDoubleComplex*)frame, 2, &fftIn, 1, bins/2);
        vDSP_fft_zripD(fftSetup, &fftIn, 1, log2bins, FFT_FORWARD);
        for(int bin = 0; bin < height; ++bin) {
            double real, imag;
            if(bin == 0) {
                real = fftIn.realp[0];
                imag = 0.0;
            } else if(bin == bins/2) {
                real = fftIn.imagp[0];
                imag = 0.0;
            } else {
                real = fftIn.realp[bin];
                imag = fftIn.imagp[bin-1];
            }
            double power = real*real + imag*imag;
            double dB = 10.0 * log10(std::max(power, 1e-20));
            dB = std::max(dB, dBfloor);
            int scaled = std::min(255, std::max(0, (int)(255.0 * (dB - dBfloor)/(-dBfloor))));
            image[col + bin*width] = scaled;
        }
        delete[] frame;
    }
    makeImage(width, height, image, path, colorTable);
    vDSP_destroy_fftsetup(fftSetup);
    free(fftIn.realp);
    free(fftIn.imagp);
    delete[] window;
    delete[] image;
}