changeset 35:c795fab4c4be

Merge
author Chris Cannam
date Tue, 10 Nov 2015 07:30:50 +0000
parents afbb64027d79 (current diff) bbf5d4e825eb (diff)
children 6a99a85fd815
files fft/index.html fft/native.cpp
diffstat 60 files changed, 12031 insertions(+), 62 deletions(-) [+]
line wrap: on
line diff
--- a/fft/index.html	Fri Oct 16 17:18:45 2015 +0100
+++ b/fft/index.html	Tue Nov 10 07:30:50 2015 +0000
@@ -12,6 +12,8 @@
     
     <script src="nayuki/fft.js"></script>
     <script src="nayuki-obj/fft.js"></script>
+    <script src="nayukic/NayukiCFFT.js"></script>
+    <script src="nayukic/FFT.js"></script>
     <script src="fft.js/lib/complex.js"></script>
     <script src="jsfft/lib/complex_array.js"></script>
     <script src="jsfft/lib/fft.js"></script>
@@ -38,6 +40,8 @@
       <td>Nayuki</td><td id="nayuki-result"></td><td id="nayuki-1"></td><td id="nayuki-2"></td><td id="nayuki-itr"></td>
     </tr><tr> 
       <td>Nayuki (obj)</td><td id="nayukiobj-result"></td><td id="nayukiobj-1"></td><td id="nayukiobj-2"></td><td id="nayukiobj-itr"></td>
+    </tr><tr> 
+      <td>Nayuki (C)</td><td id="nayukic-result"></td><td id="nayukic-1"></td><td id="nayukic-2"></td><td id="nayukic-itr"></td>
     </tr><tr>
       <td>KissFFT</td><td id="kissfft-result"></td><td id="kissfft-1"></td><td id="kissfft-2"></td><td id="kissfft-itr"></td>
     </tr><tr>
@@ -56,6 +60,7 @@
   <ul>
     <li><b>Nayuki</b>: in-place double-precision (native JS number type) complex-complex. Around 7kb.</li>
     <li><b>Nayuki (obj)</b>: Nayuki with the sin/cos tables pre-calculated on object construction. Around 4kb.</li>
+    <li><b>Nayuki (C)</b>: Nayuki C implementation compiled with Emscripten. Does not have pre-calculated sin/cos tables.</li>
     <li><b>Nockert</b>: double-precision real-complex. Around 25kb.</li>
     <li><b>Dntj</b>: double-precision complex-complex. Forward
     transform is scaled and I've scaled it back again here. Around 10kb.</li>
--- a/fft/kissfft/KissFFT.js	Fri Oct 16 17:18:45 2015 +0100
+++ b/fft/kissfft/KissFFT.js	Tue Nov 10 07:30:50 2015 +0000
@@ -1,11 +1,11 @@
 var KissFFTModule = function(Module) {
   Module = Module || {};
 
-var Module;if(!Module)Module=(typeof KissFFTModule!=="undefined"?KissFFTModule:null)||{};var moduleOverrides={};for(var key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var ENVIRONMENT_IS_WEB=typeof window==="object";var ENVIRONMENT_IS_WORKER=typeof importScripts==="function";var ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof require==="function"&&!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_WORKER;var ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;if(ENVIRONMENT_IS_NODE){if(!Module["print"])Module["print"]=function print(x){process["stdout"].write(x+"\n")};if(!Module["printErr"])Module["printErr"]=function printErr(x){process["stderr"].write(x+"\n")};var nodeFS=require("fs");var nodePath=require("path");Module["read"]=function read(filename,binary){filename=nodePath["normalize"](filename);var ret=nodeFS["readFileSync"](filename);if(!ret&&filename!=nodePath["resolve"](filename)){filename=path.join(__dirname,"..","src",filename);ret=nodeFS["readFileSync"](filename)}if(ret&&!binary)ret=ret.toString();return ret};Module["readBinary"]=function readBinary(filename){var ret=Module["read"](filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};Module["load"]=function load(f){globalEval(read(f))};if(!Module["thisProgram"]){if(process["argv"].length>1){Module["thisProgram"]=process["argv"][1].replace(/\\/g,"/")}else{Module["thisProgram"]="unknown-program"}}Module["arguments"]=process["argv"].slice(2);if(typeof module!=="undefined"){module["exports"]=Module}process["on"]("uncaughtException",(function(ex){if(!(ex instanceof ExitStatus)){throw ex}}));Module["inspect"]=(function(){return"[Emscripten Module object]"})}else if(ENVIRONMENT_IS_SHELL){if(!Module["print"])Module["print"]=print;if(typeof printErr!="undefined")Module["printErr"]=printErr;if(typeof read!="undefined"){Module["read"]=read}else{Module["read"]=function read(){throw"no read() available (jsc?)"}}Module["readBinary"]=function readBinary(f){if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}var data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){Module["arguments"]=scriptArgs}else if(typeof arguments!="undefined"){Module["arguments"]=arguments}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){Module["read"]=function read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(typeof arguments!="undefined"){Module["arguments"]=arguments}if(typeof console!=="undefined"){if(!Module["print"])Module["print"]=function print(x){console.log(x)};if(!Module["printErr"])Module["printErr"]=function printErr(x){console.log(x)}}else{var TRY_USE_DUMP=false;if(!Module["print"])Module["print"]=TRY_USE_DUMP&&typeof dump!=="undefined"?(function(x){dump(x)}):(function(x){})}if(ENVIRONMENT_IS_WORKER){Module["load"]=importScripts}if(typeof Module["setWindowTitle"]==="undefined"){Module["setWindowTitle"]=(function(title){document.title=title})}}else{throw"Unknown runtime environment. Where are we?"}function globalEval(x){eval.call(null,x)}if(!Module["load"]&&Module["read"]){Module["load"]=function load(f){globalEval(Module["read"](f))}}if(!Module["print"]){Module["print"]=(function(){})}if(!Module["printErr"]){Module["printErr"]=Module["print"]}if(!Module["arguments"]){Module["arguments"]=[]}if(!Module["thisProgram"]){Module["thisProgram"]="./this.program"}Module.print=Module["print"];Module.printErr=Module["printErr"];Module["preRun"]=[];Module["postRun"]=[];for(var key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}var Runtime={setTempRet0:(function(value){tempRet0=value}),getTempRet0:(function(){return tempRet0}),stackSave:(function(){return STACKTOP}),stackRestore:(function(stackTop){STACKTOP=stackTop}),getNativeTypeSize:(function(type){switch(type){case"i1":case"i8":return 1;case"i16":return 2;case"i32":return 4;case"i64":return 8;case"float":return 4;case"double":return 8;default:{if(type[type.length-1]==="*"){return Runtime.QUANTUM_SIZE}else if(type[0]==="i"){var bits=parseInt(type.substr(1));assert(bits%8===0);return bits/8}else{return 0}}}}),getNativeFieldSize:(function(type){return Math.max(Runtime.getNativeTypeSize(type),Runtime.QUANTUM_SIZE)}),STACK_ALIGN:16,prepVararg:(function(ptr,type){if(type==="double"||type==="i64"){if(ptr&7){assert((ptr&7)===4);ptr+=4}}else{assert((ptr&3)===0)}return ptr}),getAlignSize:(function(type,size,vararg){if(!vararg&&(type=="i64"||type=="double"))return 8;if(!type)return Math.min(size,8);return Math.min(size||(type?Runtime.getNativeFieldSize(type):0),Runtime.QUANTUM_SIZE)}),dynCall:(function(sig,ptr,args){if(args&&args.length){if(!args.splice)args=Array.prototype.slice.call(args);args.splice(0,0,ptr);return Module["dynCall_"+sig].apply(null,args)}else{return Module["dynCall_"+sig].call(null,ptr)}}),functionPointers:[],addFunction:(function(func){for(var i=0;i<Runtime.functionPointers.length;i++){if(!Runtime.functionPointers[i]){Runtime.functionPointers[i]=func;return 2*(1+i)}}throw"Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS."}),removeFunction:(function(index){Runtime.functionPointers[(index-2)/2]=null}),warnOnce:(function(text){if(!Runtime.warnOnce.shown)Runtime.warnOnce.shown={};if(!Runtime.warnOnce.shown[text]){Runtime.warnOnce.shown[text]=1;Module.printErr(text)}}),funcWrappers:{},getFuncWrapper:(function(func,sig){assert(sig);if(!Runtime.funcWrappers[sig]){Runtime.funcWrappers[sig]={}}var sigCache=Runtime.funcWrappers[sig];if(!sigCache[func]){sigCache[func]=function dynCall_wrapper(){return Runtime.dynCall(sig,func,arguments)}}return sigCache[func]}),getCompilerSetting:(function(name){throw"You must build with -s RETAIN_COMPILER_SETTINGS=1 for Runtime.getCompilerSetting or emscripten_get_compiler_setting to work"}),stackAlloc:(function(size){var ret=STACKTOP;STACKTOP=STACKTOP+size|0;STACKTOP=STACKTOP+15&-16;return ret}),staticAlloc:(function(size){var ret=STATICTOP;STATICTOP=STATICTOP+size|0;STATICTOP=STATICTOP+15&-16;return ret}),dynamicAlloc:(function(size){var ret=DYNAMICTOP;DYNAMICTOP=DYNAMICTOP+size|0;DYNAMICTOP=DYNAMICTOP+15&-16;if(DYNAMICTOP>=TOTAL_MEMORY){var success=enlargeMemory();if(!success){DYNAMICTOP=ret;return 0}}return ret}),alignMemory:(function(size,quantum){var ret=size=Math.ceil(size/(quantum?quantum:16))*(quantum?quantum:16);return ret}),makeBigInt:(function(low,high,unsigned){var ret=unsigned?+(low>>>0)+ +(high>>>0)*+4294967296:+(low>>>0)+ +(high|0)*+4294967296;return ret}),GLOBAL_BASE:8,QUANTUM_SIZE:4,__dummy__:0};Module["Runtime"]=Runtime;var __THREW__=0;var ABORT=false;var EXITSTATUS=0;var undef=0;var tempValue,tempInt,tempBigInt,tempInt2,tempBigInt2,tempPair,tempBigIntI,tempBigIntR,tempBigIntS,tempBigIntP,tempBigIntD,tempDouble,tempFloat;var tempI64,tempI64b;var tempRet0,tempRet1,tempRet2,tempRet3,tempRet4,tempRet5,tempRet6,tempRet7,tempRet8,tempRet9;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var globalScope=this;function getCFunc(ident){var func=Module["_"+ident];if(!func){try{func=eval("_"+ident)}catch(e){}}assert(func,"Cannot call unknown function "+ident+" (perhaps LLVM optimizations or closure removed it?)");return func}var cwrap,ccall;((function(){var JSfuncs={"stackSave":(function(){Runtime.stackSave()}),"stackRestore":(function(){Runtime.stackRestore()}),"arrayToC":(function(arr){var ret=Runtime.stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}),"stringToC":(function(str){var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=Runtime.stackAlloc((str.length<<2)+1);writeStringToMemory(str,ret)}return ret})};var toC={"string":JSfuncs["stringToC"],"array":JSfuncs["arrayToC"]};ccall=function ccallFunc(ident,returnType,argTypes,args,opts){var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(stack===0)stack=Runtime.stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func.apply(null,cArgs);if(returnType==="string")ret=Pointer_stringify(ret);if(stack!==0){if(opts&&opts.async){EmterpreterAsync.asyncFinalizers.push((function(){Runtime.stackRestore(stack)}));return}Runtime.stackRestore(stack)}return ret};var sourceRegex=/^function\s*\(([^)]*)\)\s*{\s*([^*]*?)[\s;]*(?:return\s*(.*?)[;\s]*)?}$/;function parseJSFunc(jsfunc){var parsed=jsfunc.toString().match(sourceRegex).slice(1);return{arguments:parsed[0],body:parsed[1],returnValue:parsed[2]}}var JSsource={};for(var fun in JSfuncs){if(JSfuncs.hasOwnProperty(fun)){JSsource[fun]=parseJSFunc(JSfuncs[fun])}}cwrap=function cwrap(ident,returnType,argTypes){argTypes=argTypes||[];var cfunc=getCFunc(ident);var numericArgs=argTypes.every((function(type){return type==="number"}));var numericRet=returnType!=="string";if(numericRet&&numericArgs){return cfunc}var argNames=argTypes.map((function(x,i){return"$"+i}));var funcstr="(function("+argNames.join(",")+") {";var nargs=argTypes.length;if(!numericArgs){funcstr+="var stack = "+JSsource["stackSave"].body+";";for(var i=0;i<nargs;i++){var arg=argNames[i],type=argTypes[i];if(type==="number")continue;var convertCode=JSsource[type+"ToC"];funcstr+="var "+convertCode.arguments+" = "+arg+";";funcstr+=convertCode.body+";";funcstr+=arg+"="+convertCode.returnValue+";"}}var cfuncname=parseJSFunc((function(){return cfunc})).returnValue;funcstr+="var ret = "+cfuncname+"("+argNames.join(",")+");";if(!numericRet){var strgfy=parseJSFunc((function(){return Pointer_stringify})).returnValue;funcstr+="ret = "+strgfy+"(ret);"}if(!numericArgs){funcstr+=JSsource["stackRestore"].body.replace("()","(stack)")+";"}funcstr+="return ret})";return eval(funcstr)}}))();Module["ccall"]=ccall;Module["cwrap"]=cwrap;function setValue(ptr,value,type,noSafe){type=type||"i8";if(type.charAt(type.length-1)==="*")type="i32";switch(type){case"i1":HEAP8[ptr>>0]=value;break;case"i8":HEAP8[ptr>>0]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":tempI64=[value>>>0,(tempDouble=value,+Math_abs(tempDouble)>=+1?tempDouble>+0?(Math_min(+Math_floor(tempDouble/+4294967296),+4294967295)|0)>>>0:~~+Math_ceil((tempDouble- +(~~tempDouble>>>0))/+4294967296)>>>0:0)],HEAP32[ptr>>2]=tempI64[0],HEAP32[ptr+4>>2]=tempI64[1];break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;default:abort("invalid type for setValue: "+type)}}Module["setValue"]=setValue;function getValue(ptr,type,noSafe){type=type||"i8";if(type.charAt(type.length-1)==="*")type="i32";switch(type){case"i1":return HEAP8[ptr>>0];case"i8":return HEAP8[ptr>>0];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":return HEAP32[ptr>>2];case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];default:abort("invalid type for setValue: "+type)}return null}Module["getValue"]=getValue;var ALLOC_NORMAL=0;var ALLOC_STACK=1;var ALLOC_STATIC=2;var ALLOC_DYNAMIC=3;var ALLOC_NONE=4;Module["ALLOC_NORMAL"]=ALLOC_NORMAL;Module["ALLOC_STACK"]=ALLOC_STACK;Module["ALLOC_STATIC"]=ALLOC_STATIC;Module["ALLOC_DYNAMIC"]=ALLOC_DYNAMIC;Module["ALLOC_NONE"]=ALLOC_NONE;function allocate(slab,types,allocator,ptr){var zeroinit,size;if(typeof slab==="number"){zeroinit=true;size=slab}else{zeroinit=false;size=slab.length}var singleType=typeof types==="string"?types:null;var ret;if(allocator==ALLOC_NONE){ret=ptr}else{ret=[_malloc,Runtime.stackAlloc,Runtime.staticAlloc,Runtime.dynamicAlloc][allocator===undefined?ALLOC_STATIC:allocator](Math.max(size,singleType?1:types.length))}if(zeroinit){var ptr=ret,stop;assert((ret&3)==0);stop=ret+(size&~3);for(;ptr<stop;ptr+=4){HEAP32[ptr>>2]=0}stop=ret+size;while(ptr<stop){HEAP8[ptr++>>0]=0}return ret}if(singleType==="i8"){if(slab.subarray||slab.slice){HEAPU8.set(slab,ret)}else{HEAPU8.set(new Uint8Array(slab),ret)}return ret}var i=0,type,typeSize,previousType;while(i<size){var curr=slab[i];if(typeof curr==="function"){curr=Runtime.getFunctionIndex(curr)}type=singleType||types[i];if(type===0){i++;continue}if(type=="i64")type="i32";setValue(ret+i,curr,type);if(previousType!==type){typeSize=Runtime.getNativeTypeSize(type);previousType=type}i+=typeSize}return ret}Module["allocate"]=allocate;function getMemory(size){if(!staticSealed)return Runtime.staticAlloc(size);if(typeof _sbrk!=="undefined"&&!_sbrk.called||!runtimeInitialized)return Runtime.dynamicAlloc(size);return _malloc(size)}Module["getMemory"]=getMemory;function Pointer_stringify(ptr,length){if(length===0||!ptr)return"";var hasUtf=0;var t;var i=0;while(1){t=HEAPU8[ptr+i>>0];hasUtf|=t;if(t==0&&!length)break;i++;if(length&&i==length)break}if(!length)length=i;var ret="";if(hasUtf<128){var MAX_CHUNK=1024;var curr;while(length>0){curr=String.fromCharCode.apply(String,HEAPU8.subarray(ptr,ptr+Math.min(length,MAX_CHUNK)));ret=ret?ret+curr:curr;ptr+=MAX_CHUNK;length-=MAX_CHUNK}return ret}return Module["UTF8ToString"](ptr)}Module["Pointer_stringify"]=Pointer_stringify;function AsciiToString(ptr){var str="";while(1){var ch=HEAP8[ptr++>>0];if(!ch)return str;str+=String.fromCharCode(ch)}}Module["AsciiToString"]=AsciiToString;function stringToAscii(str,outPtr){return writeAsciiToMemory(str,outPtr,false)}Module["stringToAscii"]=stringToAscii;function UTF8ArrayToString(u8Array,idx){var u0,u1,u2,u3,u4,u5;var str="";while(1){u0=u8Array[idx++];if(!u0)return str;if(!(u0&128)){str+=String.fromCharCode(u0);continue}u1=u8Array[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}u2=u8Array[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u3=u8Array[idx++]&63;if((u0&248)==240){u0=(u0&7)<<18|u1<<12|u2<<6|u3}else{u4=u8Array[idx++]&63;if((u0&252)==248){u0=(u0&3)<<24|u1<<18|u2<<12|u3<<6|u4}else{u5=u8Array[idx++]&63;u0=(u0&1)<<30|u1<<24|u2<<18|u3<<12|u4<<6|u5}}}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}}Module["UTF8ArrayToString"]=UTF8ArrayToString;function UTF8ToString(ptr){return UTF8ArrayToString(HEAPU8,ptr)}Module["UTF8ToString"]=UTF8ToString;function stringToUTF8Array(str,outU8Array,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127){if(outIdx>=endIdx)break;outU8Array[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;outU8Array[outIdx++]=192|u>>6;outU8Array[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;outU8Array[outIdx++]=224|u>>12;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else if(u<=2097151){if(outIdx+3>=endIdx)break;outU8Array[outIdx++]=240|u>>18;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else if(u<=67108863){if(outIdx+4>=endIdx)break;outU8Array[outIdx++]=248|u>>24;outU8Array[outIdx++]=128|u>>18&63;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else{if(outIdx+5>=endIdx)break;outU8Array[outIdx++]=252|u>>30;outU8Array[outIdx++]=128|u>>24&63;outU8Array[outIdx++]=128|u>>18&63;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}}outU8Array[outIdx]=0;return outIdx-startIdx}Module["stringToUTF8Array"]=stringToUTF8Array;function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}Module["stringToUTF8"]=stringToUTF8;function lengthBytesUTF8(str){var len=0;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127){++len}else if(u<=2047){len+=2}else if(u<=65535){len+=3}else if(u<=2097151){len+=4}else if(u<=67108863){len+=5}else{len+=6}}return len}Module["lengthBytesUTF8"]=lengthBytesUTF8;function UTF16ToString(ptr){var i=0;var str="";while(1){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)return str;++i;str+=String.fromCharCode(codeUnit)}}Module["UTF16ToString"]=UTF16ToString;function stringToUTF16(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite<str.length*2?maxBytesToWrite/2:str.length;for(var i=0;i<numCharsToWrite;++i){var codeUnit=str.charCodeAt(i);HEAP16[outPtr>>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr}Module["stringToUTF16"]=stringToUTF16;function lengthBytesUTF16(str){return str.length*2}Module["lengthBytesUTF16"]=lengthBytesUTF16;function UTF32ToString(ptr){var i=0;var str="";while(1){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)return str;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}}Module["UTF32ToString"]=UTF32ToString;function stringToUTF32(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr}Module["stringToUTF32"]=stringToUTF32;function lengthBytesUTF32(str){var len=0;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343)++i;len+=4}return len}Module["lengthBytesUTF32"]=lengthBytesUTF32;function demangle(func){var hasLibcxxabi=!!Module["___cxa_demangle"];if(hasLibcxxabi){try{var buf=_malloc(func.length);writeStringToMemory(func.substr(1),buf);var status=_malloc(4);var ret=Module["___cxa_demangle"](buf,0,0,status);if(getValue(status,"i32")===0&&ret){return Pointer_stringify(ret)}}catch(e){}finally{if(buf)_free(buf);if(status)_free(status);if(ret)_free(ret)}}var i=3;var basicTypes={"v":"void","b":"bool","c":"char","s":"short","i":"int","l":"long","f":"float","d":"double","w":"wchar_t","a":"signed char","h":"unsigned char","t":"unsigned short","j":"unsigned int","m":"unsigned long","x":"long long","y":"unsigned long long","z":"..."};var subs=[];var first=true;function dump(x){if(x)Module.print(x);Module.print(func);var pre="";for(var a=0;a<i;a++)pre+=" ";Module.print(pre+"^")}function parseNested(){i++;if(func[i]==="K")i++;var parts=[];while(func[i]!=="E"){if(func[i]==="S"){i++;var next=func.indexOf("_",i);var num=func.substring(i,next)||0;parts.push(subs[num]||"?");i=next+1;continue}if(func[i]==="C"){parts.push(parts[parts.length-1]);i+=2;continue}var size=parseInt(func.substr(i));var pre=size.toString().length;if(!size||!pre){i--;break}var curr=func.substr(i+pre,size);parts.push(curr);subs.push(curr);i+=pre+size}i++;return parts}function parse(rawList,limit,allowVoid){limit=limit||Infinity;var ret="",list=[];function flushList(){return"("+list.join(", ")+")"}var name;if(func[i]==="N"){name=parseNested().join("::");limit--;if(limit===0)return rawList?[name]:name}else{if(func[i]==="K"||first&&func[i]==="L")i++;var size=parseInt(func.substr(i));if(size){var pre=size.toString().length;name=func.substr(i+pre,size);i+=pre+size}}first=false;if(func[i]==="I"){i++;var iList=parse(true);var iRet=parse(true,1,true);ret+=iRet[0]+" "+name+"<"+iList.join(", ")+">"}else{ret=name}paramLoop:while(i<func.length&&limit-->0){var c=func[i++];if(c in basicTypes){list.push(basicTypes[c])}else{switch(c){case"P":list.push(parse(true,1,true)[0]+"*");break;case"R":list.push(parse(true,1,true)[0]+"&");break;case"L":{i++;var end=func.indexOf("E",i);var size=end-i;list.push(func.substr(i,size));i+=size+2;break};case"A":{var size=parseInt(func.substr(i));i+=size.toString().length;if(func[i]!=="_")throw"?";i++;list.push(parse(true,1,true)[0]+" ["+size+"]");break};case"E":break paramLoop;default:ret+="?"+c;break paramLoop}}}if(!allowVoid&&list.length===1&&list[0]==="void")list=[];if(rawList){if(ret){list.push(ret+"?")}return list}else{return ret+flushList()}}var parsed=func;try{if(func=="Object._main"||func=="_main"){return"main()"}if(typeof func==="number")func=Pointer_stringify(func);if(func[0]!=="_")return func;if(func[1]!=="_")return func;if(func[2]!=="Z")return func;switch(func[3]){case"n":return"operator new()";case"d":return"operator delete()"}parsed=parse()}catch(e){parsed+="?"}if(parsed.indexOf("?")>=0&&!hasLibcxxabi){Runtime.warnOnce("warning: a problem occurred in builtin C++ name demangling; build with  -s DEMANGLE_SUPPORT=1  to link in libcxxabi demangling")}return parsed}function demangleAll(text){return text.replace(/__Z[\w\d_]+/g,(function(x){var y=demangle(x);return x===y?x:x+" ["+y+"]"}))}function jsStackTrace(){var err=new Error;if(!err.stack){try{throw new Error(0)}catch(e){err=e}if(!err.stack){return"(no stack trace available)"}}return err.stack.toString()}function stackTrace(){return demangleAll(jsStackTrace())}Module["stackTrace"]=stackTrace;var PAGE_SIZE=4096;function alignMemoryPage(x){if(x%4096>0){x+=4096-x%4096}return x}var HEAP;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;var STATIC_BASE=0,STATICTOP=0,staticSealed=false;var STACK_BASE=0,STACKTOP=0,STACK_MAX=0;var DYNAMIC_BASE=0,DYNAMICTOP=0;function enlargeMemory(){abort("Cannot enlarge memory arrays. Either (1) compile with -s TOTAL_MEMORY=X with X higher than the current value "+TOTAL_MEMORY+", (2) compile with ALLOW_MEMORY_GROWTH which adjusts the size at runtime but prevents some optimizations, or (3) set Module.TOTAL_MEMORY before the program runs.")}var TOTAL_STACK=Module["TOTAL_STACK"]||5242880;var TOTAL_MEMORY=Module["TOTAL_MEMORY"]||16777216;var totalMemory=64*1024;while(totalMemory<TOTAL_MEMORY||totalMemory<2*TOTAL_STACK){if(totalMemory<16*1024*1024){totalMemory*=2}else{totalMemory+=16*1024*1024}}if(totalMemory!==TOTAL_MEMORY){TOTAL_MEMORY=totalMemory}assert(typeof Int32Array!=="undefined"&&typeof Float64Array!=="undefined"&&!!(new Int32Array(1))["subarray"]&&!!(new Int32Array(1))["set"],"JS engine does not provide full typed array support");var buffer;buffer=new ArrayBuffer(TOTAL_MEMORY);HEAP8=new Int8Array(buffer);HEAP16=new Int16Array(buffer);HEAP32=new Int32Array(buffer);HEAPU8=new Uint8Array(buffer);HEAPU16=new Uint16Array(buffer);HEAPU32=new Uint32Array(buffer);HEAPF32=new Float32Array(buffer);HEAPF64=new Float64Array(buffer);HEAP32[0]=255;assert(HEAPU8[0]===255&&HEAPU8[3]===0,"Typed arrays 2 must be run on a little-endian system");Module["HEAP"]=HEAP;Module["buffer"]=buffer;Module["HEAP8"]=HEAP8;Module["HEAP16"]=HEAP16;Module["HEAP32"]=HEAP32;Module["HEAPU8"]=HEAPU8;Module["HEAPU16"]=HEAPU16;Module["HEAPU32"]=HEAPU32;Module["HEAPF32"]=HEAPF32;Module["HEAPF64"]=HEAPF64;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback();continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Runtime.dynCall("v",func)}else{Runtime.dynCall("vi",func,[callback.arg])}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATEXIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function ensureInitRuntime(){if(runtimeInitialized)return;runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){callRuntimeCallbacks(__ATEXIT__);runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}Module["addOnPreRun"]=addOnPreRun;function addOnInit(cb){__ATINIT__.unshift(cb)}Module["addOnInit"]=addOnInit;function addOnPreMain(cb){__ATMAIN__.unshift(cb)}Module["addOnPreMain"]=addOnPreMain;function addOnExit(cb){__ATEXIT__.unshift(cb)}Module["addOnExit"]=addOnExit;function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}Module["addOnPostRun"]=addOnPostRun;function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}Module["intArrayFromString"]=intArrayFromString;function intArrayToString(array){var ret=[];for(var i=0;i<array.length;i++){var chr=array[i];if(chr>255){chr&=255}ret.push(String.fromCharCode(chr))}return ret.join("")}Module["intArrayToString"]=intArrayToString;function writeStringToMemory(string,buffer,dontAddNull){var array=intArrayFromString(string,dontAddNull);var i=0;while(i<array.length){var chr=array[i];HEAP8[buffer+i>>0]=chr;i=i+1}}Module["writeStringToMemory"]=writeStringToMemory;function writeArrayToMemory(array,buffer){for(var i=0;i<array.length;i++){HEAP8[buffer++>>0]=array[i]}}Module["writeArrayToMemory"]=writeArrayToMemory;function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i<str.length;++i){HEAP8[buffer++>>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}Module["writeAsciiToMemory"]=writeAsciiToMemory;function unSign(value,bits,ignore){if(value>=0){return value}return bits<=32?2*Math.abs(1<<bits-1)+value:Math.pow(2,bits)+value}function reSign(value,bits,ignore){if(value<=0){return value}var half=bits<=32?Math.abs(1<<bits-1):Math.pow(2,bits-1);if(value>=half&&(bits<=32||value>half)){value=-2*half+value}return value}if(!Math["imul"]||Math["imul"](4294967295,5)!==-5)Math["imul"]=function imul(a,b){var ah=a>>>16;var al=a&65535;var bh=b>>>16;var bl=b&65535;return al*bl+(ah*bl+al*bh<<16)|0};Math.imul=Math["imul"];if(!Math["clz32"])Math["clz32"]=(function(x){x=x>>>0;for(var i=0;i<32;i++){if(x&1<<31-i)return i}return 32});Math.clz32=Math["clz32"];var Math_abs=Math.abs;var Math_cos=Math.cos;var Math_sin=Math.sin;var Math_tan=Math.tan;var Math_acos=Math.acos;var Math_asin=Math.asin;var Math_atan=Math.atan;var Math_atan2=Math.atan2;var Math_exp=Math.exp;var Math_log=Math.log;var Math_sqrt=Math.sqrt;var Math_ceil=Math.ceil;var Math_floor=Math.floor;var Math_pow=Math.pow;var Math_imul=Math.imul;var Math_fround=Math.fround;var Math_min=Math.min;var Math_clz32=Math.clz32;var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}Module["addRunDependency"]=addRunDependency;function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["removeRunDependency"]=removeRunDependency;Module["preloadedImages"]={};Module["preloadedAudios"]={};var memoryInitializer=null;var ASM_CONSTS=[];STATIC_BASE=8;STATICTOP=STATIC_BASE+752;__ATINIT__.push();allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,239,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,82,101,97,108,32,70,70,84,32,111,112,116,105,109,105,122,97,116,105,111,110,32,109,117,115,116,32,98,101,32,101,118,101,110,46,10,0,107,105,115,115,32,102,102,116,32,117,115,97,103,101,32,101,114,114,111,114,58,32,105,109,112,114,111,112,101,114,32,97,108,108,111,99,10,0,0,0,0,0,0,0,0,0],"i8",ALLOC_NONE,Runtime.GLOBAL_BASE);var tempDoublePtr=Runtime.alignMemory(allocate(12,"i8",ALLOC_STATIC),8);assert(tempDoublePtr%8==0);function copyTempFloat(ptr){HEAP8[tempDoublePtr]=HEAP8[ptr];HEAP8[tempDoublePtr+1]=HEAP8[ptr+1];HEAP8[tempDoublePtr+2]=HEAP8[ptr+2];HEAP8[tempDoublePtr+3]=HEAP8[ptr+3]}function copyTempDouble(ptr){HEAP8[tempDoublePtr]=HEAP8[ptr];HEAP8[tempDoublePtr+1]=HEAP8[ptr+1];HEAP8[tempDoublePtr+2]=HEAP8[ptr+2];HEAP8[tempDoublePtr+3]=HEAP8[ptr+3];HEAP8[tempDoublePtr+4]=HEAP8[ptr+4];HEAP8[tempDoublePtr+5]=HEAP8[ptr+5];HEAP8[tempDoublePtr+6]=HEAP8[ptr+6];HEAP8[tempDoublePtr+7]=HEAP8[ptr+7]}function __exit(status){Module["exit"](status)}function _exit(status){__exit(status)}function ___setErrNo(value){if(Module["___errno_location"])HEAP32[Module["___errno_location"]()>>2]=value;return value}var ERRNO_CODES={EPERM:1,ENOENT:2,ESRCH:3,EINTR:4,EIO:5,ENXIO:6,E2BIG:7,ENOEXEC:8,EBADF:9,ECHILD:10,EAGAIN:11,EWOULDBLOCK:11,ENOMEM:12,EACCES:13,EFAULT:14,ENOTBLK:15,EBUSY:16,EEXIST:17,EXDEV:18,ENODEV:19,ENOTDIR:20,EISDIR:21,EINVAL:22,ENFILE:23,EMFILE:24,ENOTTY:25,ETXTBSY:26,EFBIG:27,ENOSPC:28,ESPIPE:29,EROFS:30,EMLINK:31,EPIPE:32,EDOM:33,ERANGE:34,ENOMSG:42,EIDRM:43,ECHRNG:44,EL2NSYNC:45,EL3HLT:46,EL3RST:47,ELNRNG:48,EUNATCH:49,ENOCSI:50,EL2HLT:51,EDEADLK:35,ENOLCK:37,EBADE:52,EBADR:53,EXFULL:54,ENOANO:55,EBADRQC:56,EBADSLT:57,EDEADLOCK:35,EBFONT:59,ENOSTR:60,ENODATA:61,ETIME:62,ENOSR:63,ENONET:64,ENOPKG:65,EREMOTE:66,ENOLINK:67,EADV:68,ESRMNT:69,ECOMM:70,EPROTO:71,EMULTIHOP:72,EDOTDOT:73,EBADMSG:74,ENOTUNIQ:76,EBADFD:77,EREMCHG:78,ELIBACC:79,ELIBBAD:80,ELIBSCN:81,ELIBMAX:82,ELIBEXEC:83,ENOSYS:38,ENOTEMPTY:39,ENAMETOOLONG:36,ELOOP:40,EOPNOTSUPP:95,EPFNOSUPPORT:96,ECONNRESET:104,ENOBUFS:105,EAFNOSUPPORT:97,EPROTOTYPE:91,ENOTSOCK:88,ENOPROTOOPT:92,ESHUTDOWN:108,ECONNREFUSED:111,EADDRINUSE:98,ECONNABORTED:103,ENETUNREACH:101,ENETDOWN:100,ETIMEDOUT:110,EHOSTDOWN:112,EHOSTUNREACH:113,EINPROGRESS:115,EALREADY:114,EDESTADDRREQ:89,EMSGSIZE:90,EPROTONOSUPPORT:93,ESOCKTNOSUPPORT:94,EADDRNOTAVAIL:99,ENETRESET:102,EISCONN:106,ENOTCONN:107,ETOOMANYREFS:109,EUSERS:87,EDQUOT:122,ESTALE:116,ENOTSUP:95,ENOMEDIUM:123,EILSEQ:84,EOVERFLOW:75,ECANCELED:125,ENOTRECOVERABLE:131,EOWNERDEAD:130,ESTRPIPE:86};function _sysconf(name){switch(name){case 30:return PAGE_SIZE;case 85:return totalMemory/PAGE_SIZE;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 80:case 81:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:return 200809;case 79:return 0;case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:return-1;case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32;case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1e3;case 89:return 700;case 71:return 256;case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:{if(typeof navigator==="object")return navigator["hardwareConcurrency"]||1;return 1}}___setErrNo(ERRNO_CODES.EINVAL);return-1}Module["_memset"]=_memset;function _pthread_cleanup_push(routine,arg){__ATEXIT__.push((function(){Runtime.dynCall("vi",routine,[arg])}));_pthread_cleanup_push.level=__ATEXIT__.length}function _pthread_cleanup_pop(){assert(_pthread_cleanup_push.level==__ATEXIT__.length,"cannot pop if something else added meanwhile!");__ATEXIT__.pop();_pthread_cleanup_push.level=__ATEXIT__.length}function _abort(){Module["abort"]()}function _emscripten_memcpy_big(dest,src,num){HEAPU8.set(HEAPU8.subarray(src,src+num),dest);return dest}Module["_memcpy"]=_memcpy;var SYSCALLS={varargs:0,get:(function(varargs){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret}),getStr:(function(){var ret=Pointer_stringify(SYSCALLS.get());return ret}),get64:(function(){var low=SYSCALLS.get(),high=SYSCALLS.get();if(low>=0)assert(high===0);else assert(high===-1);return low}),getZero:(function(){assert(SYSCALLS.get()===0)})};function ___syscall6(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD();FS.close(stream);return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}var _cos=Math_cos;function _sbrk(bytes){var self=_sbrk;if(!self.called){DYNAMICTOP=alignMemoryPage(DYNAMICTOP);self.called=true;assert(Runtime.dynamicAlloc);self.alloc=Runtime.dynamicAlloc;Runtime.dynamicAlloc=(function(){abort("cannot dynamically allocate, sbrk now has control")})}var ret=DYNAMICTOP;if(bytes!=0){var success=self.alloc(bytes);if(!success)return-1>>>0}return ret}var _floor=Math_floor;var _sqrt=Math_sqrt;function _time(ptr){var ret=Date.now()/1e3|0;if(ptr){HEAP32[ptr>>2]=ret}return ret}function _pthread_self(){return 0}function ___syscall140(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(),offset_high=SYSCALLS.get(),offset_low=SYSCALLS.get(),result=SYSCALLS.get(),whence=SYSCALLS.get();var offset=offset_low;assert(offset_high===0);FS.llseek(stream,offset,whence);HEAP32[result>>2]=stream.position;if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___syscall146(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.get(),iov=SYSCALLS.get(),iovcnt=SYSCALLS.get();var ret=0;if(!___syscall146.buffer)___syscall146.buffer=[];var buffer=___syscall146.buffer;for(var i=0;i<iovcnt;i++){var ptr=HEAP32[iov+i*8>>2];var len=HEAP32[iov+(i*8+4)>>2];for(var j=0;j<len;j++){var curr=HEAPU8[ptr+j];if(curr===0||curr===10){Module["print"](UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}}ret+=len}return ret}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}var _sin=Math_sin;STACK_BASE=STACKTOP=Runtime.alignMemory(STATICTOP);staticSealed=true;STACK_MAX=STACK_BASE+TOTAL_STACK;DYNAMIC_BASE=DYNAMICTOP=Runtime.alignMemory(STACK_MAX);assert(DYNAMIC_BASE<TOTAL_MEMORY,"TOTAL_MEMORY not big enough for stack");function invoke_ii(index,a1){try{return Module["dynCall_ii"](index,a1)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;asm["setThrew"](1,0)}}function invoke_iiii(index,a1,a2,a3){try{return Module["dynCall_iiii"](index,a1,a2,a3)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;asm["setThrew"](1,0)}}function invoke_vi(index,a1){try{Module["dynCall_vi"](index,a1)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;asm["setThrew"](1,0)}}Module.asmGlobalArg={"Math":Math,"Int8Array":Int8Array,"Int16Array":Int16Array,"Int32Array":Int32Array,"Uint8Array":Uint8Array,"Uint16Array":Uint16Array,"Uint32Array":Uint32Array,"Float32Array":Float32Array,"Float64Array":Float64Array,"NaN":NaN,"Infinity":Infinity};Module.asmLibraryArg={"abort":abort,"assert":assert,"invoke_ii":invoke_ii,"invoke_iiii":invoke_iiii,"invoke_vi":invoke_vi,"_pthread_cleanup_pop":_pthread_cleanup_pop,"_floor":_floor,"_sin":_sin,"_cos":_cos,"_sysconf":_sysconf,"_pthread_self":_pthread_self,"___syscall6":___syscall6,"___setErrNo":___setErrNo,"_abort":_abort,"_sbrk":_sbrk,"_time":_time,"_pthread_cleanup_push":_pthread_cleanup_push,"_emscripten_memcpy_big":_emscripten_memcpy_big,"_sqrt":_sqrt,"___syscall140":___syscall140,"_exit":_exit,"__exit":__exit,"___syscall146":___syscall146,"STACKTOP":STACKTOP,"STACK_MAX":STACK_MAX,"tempDoublePtr":tempDoublePtr,"ABORT":ABORT};// EMSCRIPTEN_START_ASM
+var Module;if(!Module)Module=(typeof KissFFTModule!=="undefined"?KissFFTModule:null)||{};var moduleOverrides={};for(var key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var ENVIRONMENT_IS_WEB=typeof window==="object";var ENVIRONMENT_IS_WORKER=typeof importScripts==="function";var ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof require==="function"&&!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_WORKER;var ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;if(ENVIRONMENT_IS_NODE){if(!Module["print"])Module["print"]=function print(x){process["stdout"].write(x+"\n")};if(!Module["printErr"])Module["printErr"]=function printErr(x){process["stderr"].write(x+"\n")};var nodeFS=require("fs");var nodePath=require("path");Module["read"]=function read(filename,binary){filename=nodePath["normalize"](filename);var ret=nodeFS["readFileSync"](filename);if(!ret&&filename!=nodePath["resolve"](filename)){filename=path.join(__dirname,"..","src",filename);ret=nodeFS["readFileSync"](filename)}if(ret&&!binary)ret=ret.toString();return ret};Module["readBinary"]=function readBinary(filename){var ret=Module["read"](filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};Module["load"]=function load(f){globalEval(read(f))};if(!Module["thisProgram"]){if(process["argv"].length>1){Module["thisProgram"]=process["argv"][1].replace(/\\/g,"/")}else{Module["thisProgram"]="unknown-program"}}Module["arguments"]=process["argv"].slice(2);if(typeof module!=="undefined"){module["exports"]=Module}process["on"]("uncaughtException",(function(ex){if(!(ex instanceof ExitStatus)){throw ex}}));Module["inspect"]=(function(){return"[Emscripten Module object]"})}else if(ENVIRONMENT_IS_SHELL){if(!Module["print"])Module["print"]=print;if(typeof printErr!="undefined")Module["printErr"]=printErr;if(typeof read!="undefined"){Module["read"]=read}else{Module["read"]=function read(){throw"no read() available (jsc?)"}}Module["readBinary"]=function readBinary(f){if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}var data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){Module["arguments"]=scriptArgs}else if(typeof arguments!="undefined"){Module["arguments"]=arguments}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){Module["read"]=function read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(typeof arguments!="undefined"){Module["arguments"]=arguments}if(typeof console!=="undefined"){if(!Module["print"])Module["print"]=function print(x){console.log(x)};if(!Module["printErr"])Module["printErr"]=function printErr(x){console.log(x)}}else{var TRY_USE_DUMP=false;if(!Module["print"])Module["print"]=TRY_USE_DUMP&&typeof dump!=="undefined"?(function(x){dump(x)}):(function(x){})}if(ENVIRONMENT_IS_WORKER){Module["load"]=importScripts}if(typeof Module["setWindowTitle"]==="undefined"){Module["setWindowTitle"]=(function(title){document.title=title})}}else{throw"Unknown runtime environment. Where are we?"}function globalEval(x){eval.call(null,x)}if(!Module["load"]&&Module["read"]){Module["load"]=function load(f){globalEval(Module["read"](f))}}if(!Module["print"]){Module["print"]=(function(){})}if(!Module["printErr"]){Module["printErr"]=Module["print"]}if(!Module["arguments"]){Module["arguments"]=[]}if(!Module["thisProgram"]){Module["thisProgram"]="./this.program"}Module.print=Module["print"];Module.printErr=Module["printErr"];Module["preRun"]=[];Module["postRun"]=[];for(var key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}var Runtime={setTempRet0:(function(value){tempRet0=value}),getTempRet0:(function(){return tempRet0}),stackSave:(function(){return STACKTOP}),stackRestore:(function(stackTop){STACKTOP=stackTop}),getNativeTypeSize:(function(type){switch(type){case"i1":case"i8":return 1;case"i16":return 2;case"i32":return 4;case"i64":return 8;case"float":return 4;case"double":return 8;default:{if(type[type.length-1]==="*"){return Runtime.QUANTUM_SIZE}else if(type[0]==="i"){var bits=parseInt(type.substr(1));assert(bits%8===0);return bits/8}else{return 0}}}}),getNativeFieldSize:(function(type){return Math.max(Runtime.getNativeTypeSize(type),Runtime.QUANTUM_SIZE)}),STACK_ALIGN:16,prepVararg:(function(ptr,type){if(type==="double"||type==="i64"){if(ptr&7){assert((ptr&7)===4);ptr+=4}}else{assert((ptr&3)===0)}return ptr}),getAlignSize:(function(type,size,vararg){if(!vararg&&(type=="i64"||type=="double"))return 8;if(!type)return Math.min(size,8);return Math.min(size||(type?Runtime.getNativeFieldSize(type):0),Runtime.QUANTUM_SIZE)}),dynCall:(function(sig,ptr,args){if(args&&args.length){if(!args.splice)args=Array.prototype.slice.call(args);args.splice(0,0,ptr);return Module["dynCall_"+sig].apply(null,args)}else{return Module["dynCall_"+sig].call(null,ptr)}}),functionPointers:[],addFunction:(function(func){for(var i=0;i<Runtime.functionPointers.length;i++){if(!Runtime.functionPointers[i]){Runtime.functionPointers[i]=func;return 2*(1+i)}}throw"Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS."}),removeFunction:(function(index){Runtime.functionPointers[(index-2)/2]=null}),warnOnce:(function(text){if(!Runtime.warnOnce.shown)Runtime.warnOnce.shown={};if(!Runtime.warnOnce.shown[text]){Runtime.warnOnce.shown[text]=1;Module.printErr(text)}}),funcWrappers:{},getFuncWrapper:(function(func,sig){assert(sig);if(!Runtime.funcWrappers[sig]){Runtime.funcWrappers[sig]={}}var sigCache=Runtime.funcWrappers[sig];if(!sigCache[func]){sigCache[func]=function dynCall_wrapper(){return Runtime.dynCall(sig,func,arguments)}}return sigCache[func]}),getCompilerSetting:(function(name){throw"You must build with -s RETAIN_COMPILER_SETTINGS=1 for Runtime.getCompilerSetting or emscripten_get_compiler_setting to work"}),stackAlloc:(function(size){var ret=STACKTOP;STACKTOP=STACKTOP+size|0;STACKTOP=STACKTOP+15&-16;return ret}),staticAlloc:(function(size){var ret=STATICTOP;STATICTOP=STATICTOP+size|0;STATICTOP=STATICTOP+15&-16;return ret}),dynamicAlloc:(function(size){var ret=DYNAMICTOP;DYNAMICTOP=DYNAMICTOP+size|0;DYNAMICTOP=DYNAMICTOP+15&-16;if(DYNAMICTOP>=TOTAL_MEMORY){var success=enlargeMemory();if(!success){DYNAMICTOP=ret;return 0}}return ret}),alignMemory:(function(size,quantum){var ret=size=Math.ceil(size/(quantum?quantum:16))*(quantum?quantum:16);return ret}),makeBigInt:(function(low,high,unsigned){var ret=unsigned?+(low>>>0)+ +(high>>>0)*+4294967296:+(low>>>0)+ +(high|0)*+4294967296;return ret}),GLOBAL_BASE:8,QUANTUM_SIZE:4,__dummy__:0};Module["Runtime"]=Runtime;var __THREW__=0;var ABORT=false;var EXITSTATUS=0;var undef=0;var tempValue,tempInt,tempBigInt,tempInt2,tempBigInt2,tempPair,tempBigIntI,tempBigIntR,tempBigIntS,tempBigIntP,tempBigIntD,tempDouble,tempFloat;var tempI64,tempI64b;var tempRet0,tempRet1,tempRet2,tempRet3,tempRet4,tempRet5,tempRet6,tempRet7,tempRet8,tempRet9;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var globalScope=this;function getCFunc(ident){var func=Module["_"+ident];if(!func){try{func=eval("_"+ident)}catch(e){}}assert(func,"Cannot call unknown function "+ident+" (perhaps LLVM optimizations or closure removed it?)");return func}var cwrap,ccall;((function(){var JSfuncs={"stackSave":(function(){Runtime.stackSave()}),"stackRestore":(function(){Runtime.stackRestore()}),"arrayToC":(function(arr){var ret=Runtime.stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}),"stringToC":(function(str){var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=Runtime.stackAlloc((str.length<<2)+1);writeStringToMemory(str,ret)}return ret})};var toC={"string":JSfuncs["stringToC"],"array":JSfuncs["arrayToC"]};ccall=function ccallFunc(ident,returnType,argTypes,args,opts){var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(stack===0)stack=Runtime.stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func.apply(null,cArgs);if(returnType==="string")ret=Pointer_stringify(ret);if(stack!==0){if(opts&&opts.async){EmterpreterAsync.asyncFinalizers.push((function(){Runtime.stackRestore(stack)}));return}Runtime.stackRestore(stack)}return ret};var sourceRegex=/^function\s*\(([^)]*)\)\s*{\s*([^*]*?)[\s;]*(?:return\s*(.*?)[;\s]*)?}$/;function parseJSFunc(jsfunc){var parsed=jsfunc.toString().match(sourceRegex).slice(1);return{arguments:parsed[0],body:parsed[1],returnValue:parsed[2]}}var JSsource={};for(var fun in JSfuncs){if(JSfuncs.hasOwnProperty(fun)){JSsource[fun]=parseJSFunc(JSfuncs[fun])}}cwrap=function cwrap(ident,returnType,argTypes){argTypes=argTypes||[];var cfunc=getCFunc(ident);var numericArgs=argTypes.every((function(type){return type==="number"}));var numericRet=returnType!=="string";if(numericRet&&numericArgs){return cfunc}var argNames=argTypes.map((function(x,i){return"$"+i}));var funcstr="(function("+argNames.join(",")+") {";var nargs=argTypes.length;if(!numericArgs){funcstr+="var stack = "+JSsource["stackSave"].body+";";for(var i=0;i<nargs;i++){var arg=argNames[i],type=argTypes[i];if(type==="number")continue;var convertCode=JSsource[type+"ToC"];funcstr+="var "+convertCode.arguments+" = "+arg+";";funcstr+=convertCode.body+";";funcstr+=arg+"="+convertCode.returnValue+";"}}var cfuncname=parseJSFunc((function(){return cfunc})).returnValue;funcstr+="var ret = "+cfuncname+"("+argNames.join(",")+");";if(!numericRet){var strgfy=parseJSFunc((function(){return Pointer_stringify})).returnValue;funcstr+="ret = "+strgfy+"(ret);"}if(!numericArgs){funcstr+=JSsource["stackRestore"].body.replace("()","(stack)")+";"}funcstr+="return ret})";return eval(funcstr)}}))();Module["ccall"]=ccall;Module["cwrap"]=cwrap;function setValue(ptr,value,type,noSafe){type=type||"i8";if(type.charAt(type.length-1)==="*")type="i32";switch(type){case"i1":HEAP8[ptr>>0]=value;break;case"i8":HEAP8[ptr>>0]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":tempI64=[value>>>0,(tempDouble=value,+Math_abs(tempDouble)>=+1?tempDouble>+0?(Math_min(+Math_floor(tempDouble/+4294967296),+4294967295)|0)>>>0:~~+Math_ceil((tempDouble- +(~~tempDouble>>>0))/+4294967296)>>>0:0)],HEAP32[ptr>>2]=tempI64[0],HEAP32[ptr+4>>2]=tempI64[1];break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;default:abort("invalid type for setValue: "+type)}}Module["setValue"]=setValue;function getValue(ptr,type,noSafe){type=type||"i8";if(type.charAt(type.length-1)==="*")type="i32";switch(type){case"i1":return HEAP8[ptr>>0];case"i8":return HEAP8[ptr>>0];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":return HEAP32[ptr>>2];case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];default:abort("invalid type for setValue: "+type)}return null}Module["getValue"]=getValue;var ALLOC_NORMAL=0;var ALLOC_STACK=1;var ALLOC_STATIC=2;var ALLOC_DYNAMIC=3;var ALLOC_NONE=4;Module["ALLOC_NORMAL"]=ALLOC_NORMAL;Module["ALLOC_STACK"]=ALLOC_STACK;Module["ALLOC_STATIC"]=ALLOC_STATIC;Module["ALLOC_DYNAMIC"]=ALLOC_DYNAMIC;Module["ALLOC_NONE"]=ALLOC_NONE;function allocate(slab,types,allocator,ptr){var zeroinit,size;if(typeof slab==="number"){zeroinit=true;size=slab}else{zeroinit=false;size=slab.length}var singleType=typeof types==="string"?types:null;var ret;if(allocator==ALLOC_NONE){ret=ptr}else{ret=[_malloc,Runtime.stackAlloc,Runtime.staticAlloc,Runtime.dynamicAlloc][allocator===undefined?ALLOC_STATIC:allocator](Math.max(size,singleType?1:types.length))}if(zeroinit){var ptr=ret,stop;assert((ret&3)==0);stop=ret+(size&~3);for(;ptr<stop;ptr+=4){HEAP32[ptr>>2]=0}stop=ret+size;while(ptr<stop){HEAP8[ptr++>>0]=0}return ret}if(singleType==="i8"){if(slab.subarray||slab.slice){HEAPU8.set(slab,ret)}else{HEAPU8.set(new Uint8Array(slab),ret)}return ret}var i=0,type,typeSize,previousType;while(i<size){var curr=slab[i];if(typeof curr==="function"){curr=Runtime.getFunctionIndex(curr)}type=singleType||types[i];if(type===0){i++;continue}if(type=="i64")type="i32";setValue(ret+i,curr,type);if(previousType!==type){typeSize=Runtime.getNativeTypeSize(type);previousType=type}i+=typeSize}return ret}Module["allocate"]=allocate;function getMemory(size){if(!staticSealed)return Runtime.staticAlloc(size);if(typeof _sbrk!=="undefined"&&!_sbrk.called||!runtimeInitialized)return Runtime.dynamicAlloc(size);return _malloc(size)}Module["getMemory"]=getMemory;function Pointer_stringify(ptr,length){if(length===0||!ptr)return"";var hasUtf=0;var t;var i=0;while(1){t=HEAPU8[ptr+i>>0];hasUtf|=t;if(t==0&&!length)break;i++;if(length&&i==length)break}if(!length)length=i;var ret="";if(hasUtf<128){var MAX_CHUNK=1024;var curr;while(length>0){curr=String.fromCharCode.apply(String,HEAPU8.subarray(ptr,ptr+Math.min(length,MAX_CHUNK)));ret=ret?ret+curr:curr;ptr+=MAX_CHUNK;length-=MAX_CHUNK}return ret}return Module["UTF8ToString"](ptr)}Module["Pointer_stringify"]=Pointer_stringify;function AsciiToString(ptr){var str="";while(1){var ch=HEAP8[ptr++>>0];if(!ch)return str;str+=String.fromCharCode(ch)}}Module["AsciiToString"]=AsciiToString;function stringToAscii(str,outPtr){return writeAsciiToMemory(str,outPtr,false)}Module["stringToAscii"]=stringToAscii;function UTF8ArrayToString(u8Array,idx){var u0,u1,u2,u3,u4,u5;var str="";while(1){u0=u8Array[idx++];if(!u0)return str;if(!(u0&128)){str+=String.fromCharCode(u0);continue}u1=u8Array[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}u2=u8Array[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u3=u8Array[idx++]&63;if((u0&248)==240){u0=(u0&7)<<18|u1<<12|u2<<6|u3}else{u4=u8Array[idx++]&63;if((u0&252)==248){u0=(u0&3)<<24|u1<<18|u2<<12|u3<<6|u4}else{u5=u8Array[idx++]&63;u0=(u0&1)<<30|u1<<24|u2<<18|u3<<12|u4<<6|u5}}}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}}Module["UTF8ArrayToString"]=UTF8ArrayToString;function UTF8ToString(ptr){return UTF8ArrayToString(HEAPU8,ptr)}Module["UTF8ToString"]=UTF8ToString;function stringToUTF8Array(str,outU8Array,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127){if(outIdx>=endIdx)break;outU8Array[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;outU8Array[outIdx++]=192|u>>6;outU8Array[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;outU8Array[outIdx++]=224|u>>12;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else if(u<=2097151){if(outIdx+3>=endIdx)break;outU8Array[outIdx++]=240|u>>18;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else if(u<=67108863){if(outIdx+4>=endIdx)break;outU8Array[outIdx++]=248|u>>24;outU8Array[outIdx++]=128|u>>18&63;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else{if(outIdx+5>=endIdx)break;outU8Array[outIdx++]=252|u>>30;outU8Array[outIdx++]=128|u>>24&63;outU8Array[outIdx++]=128|u>>18&63;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}}outU8Array[outIdx]=0;return outIdx-startIdx}Module["stringToUTF8Array"]=stringToUTF8Array;function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}Module["stringToUTF8"]=stringToUTF8;function lengthBytesUTF8(str){var len=0;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127){++len}else if(u<=2047){len+=2}else if(u<=65535){len+=3}else if(u<=2097151){len+=4}else if(u<=67108863){len+=5}else{len+=6}}return len}Module["lengthBytesUTF8"]=lengthBytesUTF8;function UTF16ToString(ptr){var i=0;var str="";while(1){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)return str;++i;str+=String.fromCharCode(codeUnit)}}Module["UTF16ToString"]=UTF16ToString;function stringToUTF16(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite<str.length*2?maxBytesToWrite/2:str.length;for(var i=0;i<numCharsToWrite;++i){var codeUnit=str.charCodeAt(i);HEAP16[outPtr>>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr}Module["stringToUTF16"]=stringToUTF16;function lengthBytesUTF16(str){return str.length*2}Module["lengthBytesUTF16"]=lengthBytesUTF16;function UTF32ToString(ptr){var i=0;var str="";while(1){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)return str;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}}Module["UTF32ToString"]=UTF32ToString;function stringToUTF32(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr}Module["stringToUTF32"]=stringToUTF32;function lengthBytesUTF32(str){var len=0;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343)++i;len+=4}return len}Module["lengthBytesUTF32"]=lengthBytesUTF32;function demangle(func){var hasLibcxxabi=!!Module["___cxa_demangle"];if(hasLibcxxabi){try{var buf=_malloc(func.length);writeStringToMemory(func.substr(1),buf);var status=_malloc(4);var ret=Module["___cxa_demangle"](buf,0,0,status);if(getValue(status,"i32")===0&&ret){return Pointer_stringify(ret)}}catch(e){}finally{if(buf)_free(buf);if(status)_free(status);if(ret)_free(ret)}}var i=3;var basicTypes={"v":"void","b":"bool","c":"char","s":"short","i":"int","l":"long","f":"float","d":"double","w":"wchar_t","a":"signed char","h":"unsigned char","t":"unsigned short","j":"unsigned int","m":"unsigned long","x":"long long","y":"unsigned long long","z":"..."};var subs=[];var first=true;function dump(x){if(x)Module.print(x);Module.print(func);var pre="";for(var a=0;a<i;a++)pre+=" ";Module.print(pre+"^")}function parseNested(){i++;if(func[i]==="K")i++;var parts=[];while(func[i]!=="E"){if(func[i]==="S"){i++;var next=func.indexOf("_",i);var num=func.substring(i,next)||0;parts.push(subs[num]||"?");i=next+1;continue}if(func[i]==="C"){parts.push(parts[parts.length-1]);i+=2;continue}var size=parseInt(func.substr(i));var pre=size.toString().length;if(!size||!pre){i--;break}var curr=func.substr(i+pre,size);parts.push(curr);subs.push(curr);i+=pre+size}i++;return parts}function parse(rawList,limit,allowVoid){limit=limit||Infinity;var ret="",list=[];function flushList(){return"("+list.join(", ")+")"}var name;if(func[i]==="N"){name=parseNested().join("::");limit--;if(limit===0)return rawList?[name]:name}else{if(func[i]==="K"||first&&func[i]==="L")i++;var size=parseInt(func.substr(i));if(size){var pre=size.toString().length;name=func.substr(i+pre,size);i+=pre+size}}first=false;if(func[i]==="I"){i++;var iList=parse(true);var iRet=parse(true,1,true);ret+=iRet[0]+" "+name+"<"+iList.join(", ")+">"}else{ret=name}paramLoop:while(i<func.length&&limit-->0){var c=func[i++];if(c in basicTypes){list.push(basicTypes[c])}else{switch(c){case"P":list.push(parse(true,1,true)[0]+"*");break;case"R":list.push(parse(true,1,true)[0]+"&");break;case"L":{i++;var end=func.indexOf("E",i);var size=end-i;list.push(func.substr(i,size));i+=size+2;break};case"A":{var size=parseInt(func.substr(i));i+=size.toString().length;if(func[i]!=="_")throw"?";i++;list.push(parse(true,1,true)[0]+" ["+size+"]");break};case"E":break paramLoop;default:ret+="?"+c;break paramLoop}}}if(!allowVoid&&list.length===1&&list[0]==="void")list=[];if(rawList){if(ret){list.push(ret+"?")}return list}else{return ret+flushList()}}var parsed=func;try{if(func=="Object._main"||func=="_main"){return"main()"}if(typeof func==="number")func=Pointer_stringify(func);if(func[0]!=="_")return func;if(func[1]!=="_")return func;if(func[2]!=="Z")return func;switch(func[3]){case"n":return"operator new()";case"d":return"operator delete()"}parsed=parse()}catch(e){parsed+="?"}if(parsed.indexOf("?")>=0&&!hasLibcxxabi){Runtime.warnOnce("warning: a problem occurred in builtin C++ name demangling; build with  -s DEMANGLE_SUPPORT=1  to link in libcxxabi demangling")}return parsed}function demangleAll(text){return text.replace(/__Z[\w\d_]+/g,(function(x){var y=demangle(x);return x===y?x:x+" ["+y+"]"}))}function jsStackTrace(){var err=new Error;if(!err.stack){try{throw new Error(0)}catch(e){err=e}if(!err.stack){return"(no stack trace available)"}}return err.stack.toString()}function stackTrace(){return demangleAll(jsStackTrace())}Module["stackTrace"]=stackTrace;var PAGE_SIZE=4096;function alignMemoryPage(x){if(x%4096>0){x+=4096-x%4096}return x}var HEAP;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;var STATIC_BASE=0,STATICTOP=0,staticSealed=false;var STACK_BASE=0,STACKTOP=0,STACK_MAX=0;var DYNAMIC_BASE=0,DYNAMICTOP=0;function abortOnCannotGrowMemory(){abort("Cannot enlarge memory arrays. Either (1) compile with  -s TOTAL_MEMORY=X  with X higher than the current value "+TOTAL_MEMORY+", (2) compile with  -s ALLOW_MEMORY_GROWTH=1  which adjusts the size at runtime but prevents some optimizations, (3) set Module.TOTAL_MEMORY to a higher value before the program runs, or if you want malloc to return NULL (0) instead of this abort, compile with  -s ABORTING_MALLOC=0 ")}function enlargeMemory(){abortOnCannotGrowMemory()}var TOTAL_STACK=Module["TOTAL_STACK"]||5242880;var TOTAL_MEMORY=Module["TOTAL_MEMORY"]||16777216;var totalMemory=64*1024;while(totalMemory<TOTAL_MEMORY||totalMemory<2*TOTAL_STACK){if(totalMemory<16*1024*1024){totalMemory*=2}else{totalMemory+=16*1024*1024}}if(totalMemory!==TOTAL_MEMORY){TOTAL_MEMORY=totalMemory}assert(typeof Int32Array!=="undefined"&&typeof Float64Array!=="undefined"&&!!(new Int32Array(1))["subarray"]&&!!(new Int32Array(1))["set"],"JS engine does not provide full typed array support");var buffer;buffer=new ArrayBuffer(TOTAL_MEMORY);HEAP8=new Int8Array(buffer);HEAP16=new Int16Array(buffer);HEAP32=new Int32Array(buffer);HEAPU8=new Uint8Array(buffer);HEAPU16=new Uint16Array(buffer);HEAPU32=new Uint32Array(buffer);HEAPF32=new Float32Array(buffer);HEAPF64=new Float64Array(buffer);HEAP32[0]=255;assert(HEAPU8[0]===255&&HEAPU8[3]===0,"Typed arrays 2 must be run on a little-endian system");Module["HEAP"]=HEAP;Module["buffer"]=buffer;Module["HEAP8"]=HEAP8;Module["HEAP16"]=HEAP16;Module["HEAP32"]=HEAP32;Module["HEAPU8"]=HEAPU8;Module["HEAPU16"]=HEAPU16;Module["HEAPU32"]=HEAPU32;Module["HEAPF32"]=HEAPF32;Module["HEAPF64"]=HEAPF64;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback();continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Runtime.dynCall("v",func)}else{Runtime.dynCall("vi",func,[callback.arg])}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATEXIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function ensureInitRuntime(){if(runtimeInitialized)return;runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){callRuntimeCallbacks(__ATEXIT__);runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}Module["addOnPreRun"]=addOnPreRun;function addOnInit(cb){__ATINIT__.unshift(cb)}Module["addOnInit"]=addOnInit;function addOnPreMain(cb){__ATMAIN__.unshift(cb)}Module["addOnPreMain"]=addOnPreMain;function addOnExit(cb){__ATEXIT__.unshift(cb)}Module["addOnExit"]=addOnExit;function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}Module["addOnPostRun"]=addOnPostRun;function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}Module["intArrayFromString"]=intArrayFromString;function intArrayToString(array){var ret=[];for(var i=0;i<array.length;i++){var chr=array[i];if(chr>255){chr&=255}ret.push(String.fromCharCode(chr))}return ret.join("")}Module["intArrayToString"]=intArrayToString;function writeStringToMemory(string,buffer,dontAddNull){var array=intArrayFromString(string,dontAddNull);var i=0;while(i<array.length){var chr=array[i];HEAP8[buffer+i>>0]=chr;i=i+1}}Module["writeStringToMemory"]=writeStringToMemory;function writeArrayToMemory(array,buffer){for(var i=0;i<array.length;i++){HEAP8[buffer++>>0]=array[i]}}Module["writeArrayToMemory"]=writeArrayToMemory;function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i<str.length;++i){HEAP8[buffer++>>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}Module["writeAsciiToMemory"]=writeAsciiToMemory;function unSign(value,bits,ignore){if(value>=0){return value}return bits<=32?2*Math.abs(1<<bits-1)+value:Math.pow(2,bits)+value}function reSign(value,bits,ignore){if(value<=0){return value}var half=bits<=32?Math.abs(1<<bits-1):Math.pow(2,bits-1);if(value>=half&&(bits<=32||value>half)){value=-2*half+value}return value}if(!Math["imul"]||Math["imul"](4294967295,5)!==-5)Math["imul"]=function imul(a,b){var ah=a>>>16;var al=a&65535;var bh=b>>>16;var bl=b&65535;return al*bl+(ah*bl+al*bh<<16)|0};Math.imul=Math["imul"];if(!Math["clz32"])Math["clz32"]=(function(x){x=x>>>0;for(var i=0;i<32;i++){if(x&1<<31-i)return i}return 32});Math.clz32=Math["clz32"];var Math_abs=Math.abs;var Math_cos=Math.cos;var Math_sin=Math.sin;var Math_tan=Math.tan;var Math_acos=Math.acos;var Math_asin=Math.asin;var Math_atan=Math.atan;var Math_atan2=Math.atan2;var Math_exp=Math.exp;var Math_log=Math.log;var Math_sqrt=Math.sqrt;var Math_ceil=Math.ceil;var Math_floor=Math.floor;var Math_pow=Math.pow;var Math_imul=Math.imul;var Math_fround=Math.fround;var Math_min=Math.min;var Math_clz32=Math.clz32;var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}Module["addRunDependency"]=addRunDependency;function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["removeRunDependency"]=removeRunDependency;Module["preloadedImages"]={};Module["preloadedAudios"]={};var memoryInitializer=null;var ASM_CONSTS=[];STATIC_BASE=8;STATICTOP=STATIC_BASE+752;__ATINIT__.push();allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,60,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,2,0,0,0,239,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,82,101,97,108,32,70,70,84,32,111,112,116,105,109,105,122,97,116,105,111,110,32,109,117,115,116,32,98,101,32,101,118,101,110,46,10,0,107,105,115,115,32,102,102,116,32,117,115,97,103,101,32,101,114,114,111,114,58,32,105,109,112,114,111,112,101,114,32,97,108,108,111,99,10,0,0,0,0,0,0,0,0,0],"i8",ALLOC_NONE,Runtime.GLOBAL_BASE);var tempDoublePtr=Runtime.alignMemory(allocate(12,"i8",ALLOC_STATIC),8);assert(tempDoublePtr%8==0);function copyTempFloat(ptr){HEAP8[tempDoublePtr]=HEAP8[ptr];HEAP8[tempDoublePtr+1]=HEAP8[ptr+1];HEAP8[tempDoublePtr+2]=HEAP8[ptr+2];HEAP8[tempDoublePtr+3]=HEAP8[ptr+3]}function copyTempDouble(ptr){HEAP8[tempDoublePtr]=HEAP8[ptr];HEAP8[tempDoublePtr+1]=HEAP8[ptr+1];HEAP8[tempDoublePtr+2]=HEAP8[ptr+2];HEAP8[tempDoublePtr+3]=HEAP8[ptr+3];HEAP8[tempDoublePtr+4]=HEAP8[ptr+4];HEAP8[tempDoublePtr+5]=HEAP8[ptr+5];HEAP8[tempDoublePtr+6]=HEAP8[ptr+6];HEAP8[tempDoublePtr+7]=HEAP8[ptr+7]}function __exit(status){Module["exit"](status)}function _exit(status){__exit(status)}function ___setErrNo(value){if(Module["___errno_location"])HEAP32[Module["___errno_location"]()>>2]=value;return value}var ERRNO_CODES={EPERM:1,ENOENT:2,ESRCH:3,EINTR:4,EIO:5,ENXIO:6,E2BIG:7,ENOEXEC:8,EBADF:9,ECHILD:10,EAGAIN:11,EWOULDBLOCK:11,ENOMEM:12,EACCES:13,EFAULT:14,ENOTBLK:15,EBUSY:16,EEXIST:17,EXDEV:18,ENODEV:19,ENOTDIR:20,EISDIR:21,EINVAL:22,ENFILE:23,EMFILE:24,ENOTTY:25,ETXTBSY:26,EFBIG:27,ENOSPC:28,ESPIPE:29,EROFS:30,EMLINK:31,EPIPE:32,EDOM:33,ERANGE:34,ENOMSG:42,EIDRM:43,ECHRNG:44,EL2NSYNC:45,EL3HLT:46,EL3RST:47,ELNRNG:48,EUNATCH:49,ENOCSI:50,EL2HLT:51,EDEADLK:35,ENOLCK:37,EBADE:52,EBADR:53,EXFULL:54,ENOANO:55,EBADRQC:56,EBADSLT:57,EDEADLOCK:35,EBFONT:59,ENOSTR:60,ENODATA:61,ETIME:62,ENOSR:63,ENONET:64,ENOPKG:65,EREMOTE:66,ENOLINK:67,EADV:68,ESRMNT:69,ECOMM:70,EPROTO:71,EMULTIHOP:72,EDOTDOT:73,EBADMSG:74,ENOTUNIQ:76,EBADFD:77,EREMCHG:78,ELIBACC:79,ELIBBAD:80,ELIBSCN:81,ELIBMAX:82,ELIBEXEC:83,ENOSYS:38,ENOTEMPTY:39,ENAMETOOLONG:36,ELOOP:40,EOPNOTSUPP:95,EPFNOSUPPORT:96,ECONNRESET:104,ENOBUFS:105,EAFNOSUPPORT:97,EPROTOTYPE:91,ENOTSOCK:88,ENOPROTOOPT:92,ESHUTDOWN:108,ECONNREFUSED:111,EADDRINUSE:98,ECONNABORTED:103,ENETUNREACH:101,ENETDOWN:100,ETIMEDOUT:110,EHOSTDOWN:112,EHOSTUNREACH:113,EINPROGRESS:115,EALREADY:114,EDESTADDRREQ:89,EMSGSIZE:90,EPROTONOSUPPORT:93,ESOCKTNOSUPPORT:94,EADDRNOTAVAIL:99,ENETRESET:102,EISCONN:106,ENOTCONN:107,ETOOMANYREFS:109,EUSERS:87,EDQUOT:122,ESTALE:116,ENOTSUP:95,ENOMEDIUM:123,EILSEQ:84,EOVERFLOW:75,ECANCELED:125,ENOTRECOVERABLE:131,EOWNERDEAD:130,ESTRPIPE:86};function _sysconf(name){switch(name){case 30:return PAGE_SIZE;case 85:return totalMemory/PAGE_SIZE;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 80:case 81:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:return 200809;case 79:return 0;case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:return-1;case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32;case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1e3;case 89:return 700;case 71:return 256;case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:{if(typeof navigator==="object")return navigator["hardwareConcurrency"]||1;return 1}}___setErrNo(ERRNO_CODES.EINVAL);return-1}Module["_memset"]=_memset;function _pthread_cleanup_push(routine,arg){__ATEXIT__.push((function(){Runtime.dynCall("vi",routine,[arg])}));_pthread_cleanup_push.level=__ATEXIT__.length}function _pthread_cleanup_pop(){assert(_pthread_cleanup_push.level==__ATEXIT__.length,"cannot pop if something else added meanwhile!");__ATEXIT__.pop();_pthread_cleanup_push.level=__ATEXIT__.length}function _abort(){Module["abort"]()}function _emscripten_memcpy_big(dest,src,num){HEAPU8.set(HEAPU8.subarray(src,src+num),dest);return dest}Module["_memcpy"]=_memcpy;var SYSCALLS={varargs:0,get:(function(varargs){SYSCALLS.varargs+=4;var ret=HEAP32[SYSCALLS.varargs-4>>2];return ret}),getStr:(function(){var ret=Pointer_stringify(SYSCALLS.get());return ret}),get64:(function(){var low=SYSCALLS.get(),high=SYSCALLS.get();if(low>=0)assert(high===0);else assert(high===-1);return low}),getZero:(function(){assert(SYSCALLS.get()===0)})};function ___syscall6(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD();FS.close(stream);return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}var _cos=Math_cos;function _sbrk(bytes){var self=_sbrk;if(!self.called){DYNAMICTOP=alignMemoryPage(DYNAMICTOP);self.called=true;assert(Runtime.dynamicAlloc);self.alloc=Runtime.dynamicAlloc;Runtime.dynamicAlloc=(function(){abort("cannot dynamically allocate, sbrk now has control")})}var ret=DYNAMICTOP;if(bytes!=0){var success=self.alloc(bytes);if(!success)return-1>>>0}return ret}var _floor=Math_floor;var _sqrt=Math_sqrt;function _time(ptr){var ret=Date.now()/1e3|0;if(ptr){HEAP32[ptr>>2]=ret}return ret}function _pthread_self(){return 0}function ___syscall140(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.getStreamFromFD(),offset_high=SYSCALLS.get(),offset_low=SYSCALLS.get(),result=SYSCALLS.get(),whence=SYSCALLS.get();var offset=offset_low;assert(offset_high===0);FS.llseek(stream,offset,whence);HEAP32[result>>2]=stream.position;if(stream.getdents&&offset===0&&whence===0)stream.getdents=null;return 0}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}function ___syscall146(which,varargs){SYSCALLS.varargs=varargs;try{var stream=SYSCALLS.get(),iov=SYSCALLS.get(),iovcnt=SYSCALLS.get();var ret=0;if(!___syscall146.buffer)___syscall146.buffer=[];var buffer=___syscall146.buffer;for(var i=0;i<iovcnt;i++){var ptr=HEAP32[iov+i*8>>2];var len=HEAP32[iov+(i*8+4)>>2];for(var j=0;j<len;j++){var curr=HEAPU8[ptr+j];if(curr===0||curr===10){Module["print"](UTF8ArrayToString(buffer,0));buffer.length=0}else{buffer.push(curr)}}ret+=len}return ret}catch(e){if(typeof FS==="undefined"||!(e instanceof FS.ErrnoError))abort(e);return-e.errno}}var _sin=Math_sin;STACK_BASE=STACKTOP=Runtime.alignMemory(STATICTOP);staticSealed=true;STACK_MAX=STACK_BASE+TOTAL_STACK;DYNAMIC_BASE=DYNAMICTOP=Runtime.alignMemory(STACK_MAX);assert(DYNAMIC_BASE<TOTAL_MEMORY,"TOTAL_MEMORY not big enough for stack");function invoke_ii(index,a1){try{return Module["dynCall_ii"](index,a1)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;asm["setThrew"](1,0)}}function invoke_iiii(index,a1,a2,a3){try{return Module["dynCall_iiii"](index,a1,a2,a3)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;asm["setThrew"](1,0)}}function invoke_vi(index,a1){try{Module["dynCall_vi"](index,a1)}catch(e){if(typeof e!=="number"&&e!=="longjmp")throw e;asm["setThrew"](1,0)}}Module.asmGlobalArg={"Math":Math,"Int8Array":Int8Array,"Int16Array":Int16Array,"Int32Array":Int32Array,"Uint8Array":Uint8Array,"Uint16Array":Uint16Array,"Uint32Array":Uint32Array,"Float32Array":Float32Array,"Float64Array":Float64Array,"NaN":NaN,"Infinity":Infinity};Module.asmLibraryArg={"abort":abort,"assert":assert,"invoke_ii":invoke_ii,"invoke_iiii":invoke_iiii,"invoke_vi":invoke_vi,"_pthread_cleanup_pop":_pthread_cleanup_pop,"_floor":_floor,"_sin":_sin,"_cos":_cos,"_sysconf":_sysconf,"_pthread_self":_pthread_self,"___syscall6":___syscall6,"___setErrNo":___setErrNo,"_abort":_abort,"_sbrk":_sbrk,"_time":_time,"_pthread_cleanup_push":_pthread_cleanup_push,"_emscripten_memcpy_big":_emscripten_memcpy_big,"_sqrt":_sqrt,"___syscall140":___syscall140,"_exit":_exit,"__exit":__exit,"___syscall146":___syscall146,"STACKTOP":STACKTOP,"STACK_MAX":STACK_MAX,"tempDoublePtr":tempDoublePtr,"ABORT":ABORT};// EMSCRIPTEN_START_ASM
 var asm=(function(global,env,buffer) {
 "use asm";var a=new global.Int8Array(buffer);var b=new global.Int16Array(buffer);var c=new global.Int32Array(buffer);var d=new global.Uint8Array(buffer);var e=new global.Uint16Array(buffer);var f=new global.Uint32Array(buffer);var g=new global.Float32Array(buffer);var h=new global.Float64Array(buffer);var i=env.STACKTOP|0;var j=env.STACK_MAX|0;var k=env.tempDoublePtr|0;var l=env.ABORT|0;var m=0;var n=0;var o=0;var p=0;var q=global.NaN,r=global.Infinity;var s=0,t=0,u=0,v=0,w=0.0,x=0,y=0,z=0,A=0.0;var B=0;var C=0;var D=0;var E=0;var F=0;var G=0;var H=0;var I=0;var J=0;var K=0;var L=global.Math.floor;var M=global.Math.abs;var N=global.Math.sqrt;var O=global.Math.pow;var P=global.Math.cos;var Q=global.Math.sin;var R=global.Math.tan;var S=global.Math.acos;var T=global.Math.asin;var U=global.Math.atan;var V=global.Math.atan2;var W=global.Math.exp;var X=global.Math.log;var Y=global.Math.ceil;var Z=global.Math.imul;var _=global.Math.min;var $=global.Math.clz32;var aa=env.abort;var ba=env.assert;var ca=env.invoke_ii;var da=env.invoke_iiii;var ea=env.invoke_vi;var fa=env._pthread_cleanup_pop;var ga=env._floor;var ha=env._sin;var ia=env._cos;var ja=env._sysconf;var ka=env._pthread_self;var la=env.___syscall6;var ma=env.___setErrNo;var na=env._abort;var oa=env._sbrk;var pa=env._time;var qa=env._pthread_cleanup_push;var ra=env._emscripten_memcpy_big;var sa=env._sqrt;var ta=env.___syscall140;var ua=env._exit;var va=env.__exit;var wa=env.___syscall146;var xa=0.0;
 // EMSCRIPTEN_START_FUNCS
-function Ba(a){a=a|0;var b=0;b=i;i=i+a|0;i=i+15&-16;return b|0}function Ca(){return i|0}function Da(a){a=a|0;i=a}function Ea(a,b){a=a|0;b=b|0;i=a;j=b}function Fa(a,b){a=a|0;b=b|0;if(!m){m=a;n=b}}function Ga(b){b=b|0;a[k>>0]=a[b>>0];a[k+1>>0]=a[b+1>>0];a[k+2>>0]=a[b+2>>0];a[k+3>>0]=a[b+3>>0]}function Ha(b){b=b|0;a[k>>0]=a[b>>0];a[k+1>>0]=a[b+1>>0];a[k+2>>0]=a[b+2>>0];a[k+3>>0]=a[b+3>>0];a[k+4>>0]=a[b+4>>0];a[k+5>>0]=a[b+5>>0];a[k+6>>0]=a[b+6>>0];a[k+7>>0]=a[b+7>>0]}function Ia(a){a=a|0;B=a}function Ja(){return B|0}function Ka(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,h=0.0,i=0,j=0.0;f=(a<<3)+264|0;if(!e)i=ab(f)|0;else{if(!d)d=0;else d=(c[e>>2]|0)>>>0<f>>>0?0:d;c[e>>2]=f;i=d}if(!i)return i|0;c[i>>2]=a;d=i+4|0;c[d>>2]=b;h=+(a|0);a:do if((a|0)>0){e=b;f=0;while(1){j=+(f|0)*-6.283185307179586/h;j=(e|0)==0?j:-j;g[i+264+(f<<3)>>2]=+P(+j);g[i+264+(f<<3)+4>>2]=+Q(+j);f=f+1|0;if((f|0)==(a|0))break a;e=c[d>>2]|0}}while(0);h=+L(+(+N(+h)));e=a;f=i+8|0;d=4;while(1){b:do if((e|0)%(d|0)|0)while(1){switch(d|0){case 4:{d=2;break}case 2:{d=3;break}default:d=d+2|0}d=+(d|0)>h?e:d;if(!((e|0)%(d|0)|0))break b}while(0);e=(e|0)/(d|0)|0;c[f>>2]=d;c[f+4>>2]=e;if((e|0)<=1)break;else f=f+8|0}return i|0}function La(a,b,d,e,f,h){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;var i=0,j=0,l=0,m=0,n=0.0,o=0.0,p=0.0,q=0.0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0.0,B=0.0,C=0.0,D=0.0,E=0.0,F=0.0,G=0.0,H=0.0,I=0.0,J=0.0,K=0.0,L=0.0,M=0.0,N=0.0,O=0;y=c[f>>2]|0;l=f+8|0;z=c[f+4>>2]|0;r=a+((Z(z,y)|0)<<3)|0;if((z|0)==1){m=Z(e,d)|0;l=a;f=b;while(1){i=f;b=c[i+4>>2]|0;e=l;c[e>>2]=c[i>>2];c[e+4>>2]=b;l=l+8|0;if((l|0)==(r|0))break;else f=f+(m<<3)|0}}else{i=Z(y,d)|0;j=Z(e,d)|0;m=a;while(1){La(m,b,i,e,l,h);m=m+(z<<3)|0;if((m|0)==(r|0))break;else b=b+(j<<3)|0}}switch(y|0){case 2:{l=a;f=z;j=a+(z<<3)|0;i=h+264|0;while(1){p=+g[j>>2];A=+g[i>>2];b=j+4|0;q=+g[b>>2];n=+g[i+4>>2];o=p*A-q*n;n=A*q+p*n;g[j>>2]=+g[l>>2]-o;e=l+4|0;g[b>>2]=+g[e>>2]-n;g[l>>2]=o+ +g[l>>2];g[e>>2]=n+ +g[e>>2];f=f+-1|0;if(!f)break;else{l=l+8|0;j=j+8|0;i=i+(d<<3)|0}}return}case 3:{b=z<<1;n=+g[h+264+((Z(z,d)|0)<<3)+4>>2];i=h+264|0;m=d<<1;f=a;l=z;j=i;while(1){r=f+(z<<3)|0;A=+g[r>>2];o=+g[j>>2];e=f+(z<<3)+4|0;D=+g[e>>2];B=+g[j+4>>2];C=A*o-D*B;B=o*D+A*B;t=f+(b<<3)|0;A=+g[t>>2];D=+g[i>>2];s=f+(b<<3)+4|0;o=+g[s>>2];p=+g[i+4>>2];q=A*D-o*p;p=D*o+A*p;A=C+q;o=B+p;g[r>>2]=+g[f>>2]-A*.5;u=f+4|0;g[e>>2]=+g[u>>2]-o*.5;q=n*(C-q);p=n*(B-p);g[f>>2]=+g[f>>2]+A;g[u>>2]=o+ +g[u>>2];g[t>>2]=p+ +g[r>>2];g[s>>2]=+g[e>>2]-q;g[r>>2]=+g[r>>2]-p;g[e>>2]=q+ +g[e>>2];l=l+-1|0;if(!l)break;else{f=f+8|0;j=j+(d<<3)|0;i=i+(m<<3)|0}}return}case 4:{r=z<<1;f=z*3|0;e=h+264|0;s=d<<1;t=d*3|0;if(!(c[h+4>>2]|0)){m=a;i=z;j=e;l=e;b=e;while(1){w=m+(z<<3)|0;D=+g[w>>2];C=+g[j>>2];v=m+(z<<3)+4|0;n=+g[v>>2];E=+g[j+4>>2];F=D*C-n*E;E=C*n+D*E;a=m+(r<<3)|0;D=+g[a>>2];n=+g[l>>2];y=m+(r<<3)+4|0;C=+g[y>>2];B=+g[l+4>>2];A=D*n-C*B;B=n*C+D*B;u=m+(f<<3)|0;D=+g[u>>2];C=+g[b>>2];e=m+(f<<3)+4|0;n=+g[e>>2];o=+g[b+4>>2];q=D*C-n*o;o=C*n+D*o;D=+g[m>>2];n=D-A;x=m+4|0;C=+g[x>>2];p=C-B;D=A+D;g[m>>2]=D;C=B+C;g[x>>2]=C;B=F+q;A=E+o;q=F-q;o=E-o;g[a>>2]=D-B;g[y>>2]=C-A;g[m>>2]=B+ +g[m>>2];g[x>>2]=A+ +g[x>>2];g[w>>2]=n+o;g[v>>2]=p-q;g[u>>2]=n-o;g[e>>2]=p+q;i=i+-1|0;if(!i)break;else{m=m+8|0;j=j+(d<<3)|0;l=l+(s<<3)|0;b=b+(t<<3)|0}}return}else{m=a;i=z;j=e;l=e;b=e;while(1){w=m+(z<<3)|0;C=+g[w>>2];B=+g[j>>2];v=m+(z<<3)+4|0;n=+g[v>>2];D=+g[j+4>>2];E=C*B-n*D;D=B*n+C*D;a=m+(r<<3)|0;C=+g[a>>2];n=+g[l>>2];y=m+(r<<3)+4|0;B=+g[y>>2];A=+g[l+4>>2];F=C*n-B*A;A=n*B+C*A;u=m+(f<<3)|0;C=+g[u>>2];B=+g[b>>2];e=m+(f<<3)+4|0;n=+g[e>>2];o=+g[b+4>>2];q=C*B-n*o;o=B*n+C*o;C=+g[m>>2];n=C-F;x=m+4|0;B=+g[x>>2];p=B-A;C=F+C;g[m>>2]=C;B=A+B;g[x>>2]=B;A=E+q;F=D+o;q=E-q;o=D-o;g[a>>2]=C-A;g[y>>2]=B-F;g[m>>2]=A+ +g[m>>2];g[x>>2]=F+ +g[x>>2];g[w>>2]=n-o;g[v>>2]=p+q;g[u>>2]=n+o;g[e>>2]=p-q;i=i+-1|0;if(!i)break;else{m=m+8|0;j=j+(d<<3)|0;l=l+(s<<3)|0;b=b+(t<<3)|0}}return}}case 5:{e=Z(z,d)|0;n=+g[h+264+(e<<3)>>2];o=+g[h+264+(e<<3)+4>>2];e=Z(z,d<<1)|0;p=+g[h+264+(e<<3)>>2];q=+g[h+264+(e<<3)+4>>2];if((z|0)<=0)return;m=d*3|0;i=a;j=a+(z<<3)|0;l=a+(z<<1<<3)|0;f=a+(z*3<<3)|0;b=a+(z<<2<<3)|0;e=0;while(1){I=+g[i>>2];v=i+4|0;G=+g[v>>2];C=+g[j>>2];w=Z(e,d)|0;A=+g[h+264+(w<<3)>>2];u=j+4|0;N=+g[u>>2];J=+g[h+264+(w<<3)+4>>2];H=C*A-N*J;J=A*N+C*J;C=+g[l>>2];w=Z(e<<1,d)|0;N=+g[h+264+(w<<3)>>2];s=l+4|0;A=+g[s>>2];M=+g[h+264+(w<<3)+4>>2];K=C*N-A*M;M=N*A+C*M;C=+g[f>>2];w=Z(m,e)|0;A=+g[h+264+(w<<3)>>2];r=f+4|0;N=+g[r>>2];E=+g[h+264+(w<<3)+4>>2];F=C*A-N*E;E=A*N+C*E;C=+g[b>>2];w=Z(e<<2,d)|0;N=+g[h+264+(w<<3)>>2];t=b+4|0;A=+g[t>>2];B=+g[h+264+(w<<3)+4>>2];D=C*N-A*B;B=N*A+C*B;C=H+D;A=J+B;D=H-D;B=J-B;J=K+F;H=M+E;F=K-F;E=M-E;g[i>>2]=I+(J+C);g[v>>2]=G+(H+A);M=p*J+(I+n*C);K=p*H+(G+n*A);N=q*E+o*B;L=-(o*D)-q*F;g[j>>2]=M-N;g[u>>2]=K-L;g[b>>2]=N+M;g[t>>2]=L+K;C=n*J+(I+p*C);A=n*H+(G+p*A);B=o*E-q*B;F=q*D-o*F;g[l>>2]=B+C;g[s>>2]=F+A;g[f>>2]=C-B;g[r>>2]=A-F;e=e+1|0;if((e|0)==(z|0))break;else{i=i+8|0;j=j+8|0;l=l+8|0;f=f+8|0;b=b+8|0}}return}default:{f=c[h>>2]|0;u=ab(y<<3)|0;if((z|0)>0){v=(y|0)>0;w=(y|0)>1;x=0;do{a:do if(v){b=x;m=0;while(1){i=a+(b<<3)|0;r=c[i+4>>2]|0;e=u+(m<<3)|0;c[e>>2]=c[i>>2];c[e+4>>2]=r;m=m+1|0;if((m|0)==(y|0))break;else b=b+z|0}l=u;t=c[l>>2]|0;l=c[l+4>>2]|0;if(!w){e=x;b=0;while(1){m=a+(e<<3)|0;c[m>>2]=t;c[m+4>>2]=l;b=b+1|0;if((b|0)==(y|0))break a;else e=e+z|0}}q=(c[k>>2]=t,+g[k>>2]);r=x;s=0;while(1){b=a+(r<<3)|0;e=b;c[e>>2]=t;c[e+4>>2]=l;e=Z(r,d)|0;m=a+(r<<3)+4|0;p=q;o=+g[m>>2];i=1;j=0;do{O=j+e|0;j=O-((O|0)<(f|0)?0:f)|0;D=+g[u+(i<<3)>>2];B=+g[h+264+(j<<3)>>2];C=+g[u+(i<<3)+4>>2];E=+g[h+264+(j<<3)+4>>2];p=p+(D*B-C*E);g[b>>2]=p;o=o+(B*C+D*E);g[m>>2]=o;i=i+1|0}while((i|0)!=(y|0));s=s+1|0;if((s|0)==(y|0))break;else r=r+z|0}}while(0);x=x+1|0}while((x|0)!=(z|0))}bb(u);return}}}function Ma(a,b,d){a=a|0;b=b|0;d=d|0;if((b|0)==(d|0)){d=ab(c[a>>2]<<3)|0;La(d,b,1,1,a+8|0,a);eb(b|0,d|0,c[a>>2]<<3|0)|0;bb(d);return}else{La(d,b,1,1,a+8|0,a);return}}function Na(a){a=a|0;bb(a);return}function Oa(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,h=0.0,j=0,k=0,l=0,m=0,n=0.0;l=i;i=i+16|0;k=l;if(a&1){Ya(668,36,1,c[13]|0)|0;e=0;i=l;return e|0}j=a>>1;Ka(j,b,0,k)|0;a=(c[k>>2]|0)+12|0;f=a+(((j*3|0)/2|0)<<3)|0;if(e){m=(c[e>>2]|0)>>>0<f>>>0;c[e>>2]=f;if(m){e=0;i=l;return e|0}}else d=ab(f)|0;if(!d){e=0;i=l;return e|0}e=d+12|0;c[d>>2]=e;f=d+a|0;c[d+4>>2]=f;a=d+8|0;c[a>>2]=f+(j<<3);Ka(j,b,e,k)|0;e=(j|0)/2|0;if((j|0)<=1){e=d;i=l;return e|0}h=+(j|0);f=c[a>>2]|0;if(!b){a=0;do{j=a;a=a+1|0;n=(+(a|0)/h+.5)*-3.141592653589793;g[f+(j<<3)>>2]=+P(+n);g[f+(j<<3)+4>>2]=+Q(+n)}while((a|0)<(e|0));i=l;return d|0}else{a=0;do{j=a;a=a+1|0;n=(+(a|0)/h+.5)*-3.141592653589793;g[f+(j<<3)>>2]=+P(+n);g[f+(j<<3)+4>>2]=+Q(+-n)}while((a|0)<(e|0));i=l;return d|0}return 0}function Pa(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,h=0,i=0,j=0.0,k=0.0,l=0.0,m=0.0,n=0.0,o=0.0,p=0.0,q=0;e=c[a>>2]|0;if(c[e+4>>2]|0){Ya(705,37,1,c[13]|0)|0;ua(1)}i=c[e>>2]|0;h=a+4|0;Ma(e,b,c[h>>2]|0);h=c[h>>2]|0;k=+g[h>>2];j=+g[h+4>>2];g[d>>2]=k+j;g[d+(i<<3)>>2]=k-j;g[d+4>>2]=0.0;g[d+(i<<3)+4>>2]=0.0;f=(i|0)/2|0;if((i|0)<2)return;b=c[a+8>>2]|0;e=1;while(1){k=+g[h+(e<<3)>>2];o=+g[h+(e<<3)+4>>2];a=i-e|0;n=+g[h+(a<<3)>>2];p=+g[h+(a<<3)+4>>2];m=k+n;j=o-p;n=k-n;p=o+p;q=e+-1|0;o=+g[b+(q<<3)>>2];k=+g[b+(q<<3)+4>>2];l=n*o-p*k;k=p*o+n*k;g[d+(e<<3)>>2]=(m+l)*.5;g[d+(e<<3)+4>>2]=(j+k)*.5;g[d+(a<<3)>>2]=(m-l)*.5;g[d+(a<<3)+4>>2]=(k-j)*.5;if((e|0)<(f|0))e=e+1|0;else break}return}function Qa(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,h=0,i=0,j=0,k=0,l=0.0,m=0.0,n=0.0,o=0.0,p=0.0,q=0.0,r=0.0,s=0;f=c[a>>2]|0;if(!(c[f+4>>2]|0)){Ya(705,37,1,c[13]|0)|0;ua(1)}j=c[f>>2]|0;i=b+(j<<3)|0;h=c[a+4>>2]|0;g[h>>2]=+g[b>>2]+ +g[i>>2];g[h+4>>2]=+g[b>>2]-+g[i>>2];i=(j|0)/2|0;if((j|0)<2){Ma(f,h,d);return}a=c[a+8>>2]|0;e=1;while(1){l=+g[b+(e<<3)>>2];q=+g[b+(e<<3)+4>>2];k=j-e|0;p=+g[b+(k<<3)>>2];r=+g[b+(k<<3)+4>>2];o=l+p;m=q-r;p=l-p;r=q+r;s=e+-1|0;q=+g[a+(s<<3)>>2];l=+g[a+(s<<3)+4>>2];n=p*q-r*l;l=r*q+p*l;g[h+(e<<3)>>2]=o+n;g[h+(e<<3)+4>>2]=m+l;g[h+(k<<3)>>2]=o-n;g[h+(k<<3)+4>>2]=-(m-l);if((e|0)<(i|0))e=e+1|0;else break}Ma(f,h,d);return}function Ra(){var a=0;if(!(c[2]|0))a=56;else a=c[(ka()|0)+60>>2]|0;return a|0}function Sa(a){a=a|0;if(a>>>0>4294963200){c[(Ra()|0)>>2]=0-a;a=-1}return a|0}function Ta(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;q=i;i=i+48|0;n=q+16|0;m=q;e=q+32|0;o=a+28|0;f=c[o>>2]|0;c[e>>2]=f;p=a+20|0;f=(c[p>>2]|0)-f|0;c[e+4>>2]=f;c[e+8>>2]=b;c[e+12>>2]=d;k=a+60|0;l=a+44|0;b=2;f=f+d|0;while(1){if(!(c[2]|0)){c[n>>2]=c[k>>2];c[n+4>>2]=e;c[n+8>>2]=b;h=Sa(wa(146,n|0)|0)|0}else{qa(1,a|0);c[m>>2]=c[k>>2];c[m+4>>2]=e;c[m+8>>2]=b;h=Sa(wa(146,m|0)|0)|0;fa(0)}if((f|0)==(h|0)){f=6;break}if((h|0)<0){f=8;break}f=f-h|0;g=c[e+4>>2]|0;if(h>>>0<=g>>>0)if((b|0)==2){c[o>>2]=(c[o>>2]|0)+h;j=g;b=2}else j=g;else{j=c[l>>2]|0;c[o>>2]=j;c[p>>2]=j;j=c[e+12>>2]|0;h=h-g|0;e=e+8|0;b=b+-1|0}c[e>>2]=(c[e>>2]|0)+h;c[e+4>>2]=j-h}if((f|0)==6){m=c[l>>2]|0;c[a+16>>2]=m+(c[a+48>>2]|0);c[o>>2]=m;c[p>>2]=m}else if((f|0)==8){c[a+16>>2]=0;c[o>>2]=0;c[p>>2]=0;c[a>>2]=c[a>>2]|32;if((b|0)==2)d=0;else d=d-(c[e+4>>2]|0)|0}i=q;return d|0}function Ua(a){a=a|0;var b=0,d=0;b=i;i=i+16|0;d=b;c[d>>2]=c[a+60>>2];a=Sa(la(6,d|0)|0)|0;i=b;return a|0}function Va(b){b=b|0;var d=0,e=0;d=b+74|0;e=a[d>>0]|0;a[d>>0]=e+255|e;d=c[b>>2]|0;if(!(d&8)){c[b+8>>2]=0;c[b+4>>2]=0;d=c[b+44>>2]|0;c[b+28>>2]=d;c[b+20>>2]=d;c[b+16>>2]=d+(c[b+48>>2]|0);d=0}else{c[b>>2]=d|32;d=-1}return d|0}function Wa(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0;f=i;i=i+32|0;g=f;e=f+20|0;c[g>>2]=c[a+60>>2];c[g+4>>2]=0;c[g+8>>2]=b;c[g+12>>2]=e;c[g+16>>2]=d;if((Sa(ta(140,g|0)|0)|0)<0){c[e>>2]=-1;a=-1}else a=c[e>>2]|0;i=f;return a|0}function Xa(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0;f=e+16|0;g=c[f>>2]|0;if(!g)if(!(Va(e)|0)){g=c[f>>2]|0;h=4}else f=0;else h=4;a:do if((h|0)==4){i=e+20|0;h=c[i>>2]|0;if((g-h|0)>>>0<d>>>0){f=za[c[e+36>>2]&3](e,b,d)|0;break}b:do if((a[e+75>>0]|0)>-1){f=d;while(1){if(!f){g=h;f=0;break b}g=f+-1|0;if((a[b+g>>0]|0)==10)break;else f=g}if((za[c[e+36>>2]&3](e,b,f)|0)>>>0<f>>>0)break a;d=d-f|0;b=b+f|0;g=c[i>>2]|0}else{g=h;f=0}while(0);eb(g|0,b|0,d|0)|0;c[i>>2]=(c[i>>2]|0)+d;f=f+d|0}while(0);return f|0}function Ya(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0;f=Z(d,b)|0;if((c[e+76>>2]|0)>-1){g=(Za(e)|0)==0;a=Xa(a,f,e)|0;if(!g)_a(e)}else a=Xa(a,f,e)|0;if((a|0)!=(f|0))d=(a>>>0)/(b>>>0)|0;return d|0}function Za(a){a=a|0;return 0}function _a(a){a=a|0;return}function $a(a){a=a|0;if(!(c[a+68>>2]|0))_a(a);return}function ab(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0,M=0,N=0;do if(a>>>0<245){q=a>>>0<11?16:a+11&-8;a=q>>>3;l=c[43]|0;j=l>>>a;if(j&3){e=(j&1^1)+a|0;f=e<<1;b=212+(f<<2)|0;f=212+(f+2<<2)|0;g=c[f>>2]|0;h=g+8|0;i=c[h>>2]|0;do if((b|0)!=(i|0)){if(i>>>0<(c[47]|0)>>>0)na();d=i+12|0;if((c[d>>2]|0)==(g|0)){c[d>>2]=b;c[f>>2]=i;break}else na()}else c[43]=l&~(1<<e);while(0);w=e<<3;c[g+4>>2]=w|3;w=g+(w|4)|0;c[w>>2]=c[w>>2]|1;w=h;return w|0}f=c[45]|0;if(q>>>0>f>>>0){if(j){b=2<<a;b=j<<a&(b|0-b);b=(b&0-b)+-1|0;a=b>>>12&16;b=b>>>a;d=b>>>5&8;b=b>>>d;e=b>>>2&4;b=b>>>e;g=b>>>1&2;b=b>>>g;h=b>>>1&1;h=(d|a|e|g|h)+(b>>>h)|0;b=h<<1;g=212+(b<<2)|0;b=212+(b+2<<2)|0;e=c[b>>2]|0;a=e+8|0;d=c[a>>2]|0;do if((g|0)!=(d|0)){if(d>>>0<(c[47]|0)>>>0)na();i=d+12|0;if((c[i>>2]|0)==(e|0)){c[i>>2]=g;c[b>>2]=d;k=c[45]|0;break}else na()}else{c[43]=l&~(1<<h);k=f}while(0);w=h<<3;f=w-q|0;c[e+4>>2]=q|3;j=e+q|0;c[e+(q|4)>>2]=f|1;c[e+w>>2]=f;if(k){d=c[48]|0;g=k>>>3;i=g<<1;b=212+(i<<2)|0;h=c[43]|0;g=1<<g;if(h&g){h=212+(i+2<<2)|0;i=c[h>>2]|0;if(i>>>0<(c[47]|0)>>>0)na();else{m=h;n=i}}else{c[43]=h|g;m=212+(i+2<<2)|0;n=b}c[m>>2]=d;c[n+12>>2]=d;c[d+8>>2]=n;c[d+12>>2]=b}c[45]=f;c[48]=j;w=a;return w|0}a=c[44]|0;if(a){h=(a&0-a)+-1|0;v=h>>>12&16;h=h>>>v;u=h>>>5&8;h=h>>>u;w=h>>>2&4;h=h>>>w;i=h>>>1&2;h=h>>>i;g=h>>>1&1;g=c[476+((u|v|w|i|g)+(h>>>g)<<2)>>2]|0;h=(c[g+4>>2]&-8)-q|0;i=g;while(1){d=c[i+16>>2]|0;if(!d){d=c[i+20>>2]|0;if(!d){l=h;k=g;break}}i=(c[d+4>>2]&-8)-q|0;w=i>>>0<h>>>0;h=w?i:h;i=d;g=w?d:g}a=c[47]|0;if(k>>>0<a>>>0)na();f=k+q|0;if(k>>>0>=f>>>0)na();j=c[k+24>>2]|0;g=c[k+12>>2]|0;do if((g|0)==(k|0)){h=k+20|0;i=c[h>>2]|0;if(!i){h=k+16|0;i=c[h>>2]|0;if(!i){e=0;break}}while(1){g=i+20|0;b=c[g>>2]|0;if(b){i=b;h=g;continue}g=i+16|0;b=c[g>>2]|0;if(!b)break;else{i=b;h=g}}if(h>>>0<a>>>0)na();else{c[h>>2]=0;e=i;break}}else{b=c[k+8>>2]|0;if(b>>>0<a>>>0)na();i=b+12|0;if((c[i>>2]|0)!=(k|0))na();h=g+8|0;if((c[h>>2]|0)==(k|0)){c[i>>2]=g;c[h>>2]=b;e=g;break}else na()}while(0);do if(j){i=c[k+28>>2]|0;h=476+(i<<2)|0;if((k|0)==(c[h>>2]|0)){c[h>>2]=e;if(!e){c[44]=c[44]&~(1<<i);break}}else{if(j>>>0<(c[47]|0)>>>0)na();i=j+16|0;if((c[i>>2]|0)==(k|0))c[i>>2]=e;else c[j+20>>2]=e;if(!e)break}h=c[47]|0;if(e>>>0<h>>>0)na();c[e+24>>2]=j;i=c[k+16>>2]|0;do if(i)if(i>>>0<h>>>0)na();else{c[e+16>>2]=i;c[i+24>>2]=e;break}while(0);i=c[k+20>>2]|0;if(i)if(i>>>0<(c[47]|0)>>>0)na();else{c[e+20>>2]=i;c[i+24>>2]=e;break}}while(0);if(l>>>0<16){w=l+q|0;c[k+4>>2]=w|3;w=k+(w+4)|0;c[w>>2]=c[w>>2]|1}else{c[k+4>>2]=q|3;c[k+(q|4)>>2]=l|1;c[k+(l+q)>>2]=l;d=c[45]|0;if(d){e=c[48]|0;g=d>>>3;i=g<<1;b=212+(i<<2)|0;h=c[43]|0;g=1<<g;if(h&g){i=212+(i+2<<2)|0;h=c[i>>2]|0;if(h>>>0<(c[47]|0)>>>0)na();else{p=i;o=h}}else{c[43]=h|g;p=212+(i+2<<2)|0;o=b}c[p>>2]=e;c[o+12>>2]=e;c[e+8>>2]=o;c[e+12>>2]=b}c[45]=l;c[48]=f}w=k+8|0;return w|0}else z=q}else z=q}else if(a>>>0<=4294967231){a=a+11|0;p=a&-8;k=c[44]|0;if(k){j=0-p|0;a=a>>>8;if(a)if(p>>>0>16777215)l=31;else{q=(a+1048320|0)>>>16&8;w=a<<q;o=(w+520192|0)>>>16&4;w=w<<o;l=(w+245760|0)>>>16&2;l=14-(o|q|l)+(w<<l>>>15)|0;l=p>>>(l+7|0)&1|l<<1}else l=0;a=c[476+(l<<2)>>2]|0;a:do if(!a){h=0;a=0;w=86}else{d=j;h=0;e=p<<((l|0)==31?0:25-(l>>>1)|0);f=a;a=0;while(1){g=c[f+4>>2]&-8;j=g-p|0;if(j>>>0<d>>>0)if((g|0)==(p|0)){g=f;a=f;w=90;break a}else a=f;else j=d;w=c[f+20>>2]|0;f=c[f+16+(e>>>31<<2)>>2]|0;h=(w|0)==0|(w|0)==(f|0)?h:w;if(!f){w=86;break}else{d=j;e=e<<1}}}while(0);if((w|0)==86){if((h|0)==0&(a|0)==0){a=2<<l;a=k&(a|0-a);if(!a){z=p;break}a=(a&0-a)+-1|0;n=a>>>12&16;a=a>>>n;m=a>>>5&8;a=a>>>m;o=a>>>2&4;a=a>>>o;q=a>>>1&2;a=a>>>q;h=a>>>1&1;h=c[476+((m|n|o|q|h)+(a>>>h)<<2)>>2]|0;a=0}if(!h){n=j;q=a}else{g=h;w=90}}if((w|0)==90)while(1){w=0;q=(c[g+4>>2]&-8)-p|0;h=q>>>0<j>>>0;j=h?q:j;a=h?g:a;h=c[g+16>>2]|0;if(h){g=h;w=90;continue}g=c[g+20>>2]|0;if(!g){n=j;q=a;break}else w=90}if((q|0)!=0?n>>>0<((c[45]|0)-p|0)>>>0:0){a=c[47]|0;if(q>>>0<a>>>0)na();m=q+p|0;if(q>>>0>=m>>>0)na();j=c[q+24>>2]|0;g=c[q+12>>2]|0;do if((g|0)==(q|0)){h=q+20|0;i=c[h>>2]|0;if(!i){h=q+16|0;i=c[h>>2]|0;if(!i){s=0;break}}while(1){g=i+20|0;b=c[g>>2]|0;if(b){i=b;h=g;continue}g=i+16|0;b=c[g>>2]|0;if(!b)break;else{i=b;h=g}}if(h>>>0<a>>>0)na();else{c[h>>2]=0;s=i;break}}else{b=c[q+8>>2]|0;if(b>>>0<a>>>0)na();i=b+12|0;if((c[i>>2]|0)!=(q|0))na();h=g+8|0;if((c[h>>2]|0)==(q|0)){c[i>>2]=g;c[h>>2]=b;s=g;break}else na()}while(0);do if(j){i=c[q+28>>2]|0;h=476+(i<<2)|0;if((q|0)==(c[h>>2]|0)){c[h>>2]=s;if(!s){c[44]=c[44]&~(1<<i);break}}else{if(j>>>0<(c[47]|0)>>>0)na();i=j+16|0;if((c[i>>2]|0)==(q|0))c[i>>2]=s;else c[j+20>>2]=s;if(!s)break}h=c[47]|0;if(s>>>0<h>>>0)na();c[s+24>>2]=j;i=c[q+16>>2]|0;do if(i)if(i>>>0<h>>>0)na();else{c[s+16>>2]=i;c[i+24>>2]=s;break}while(0);i=c[q+20>>2]|0;if(i)if(i>>>0<(c[47]|0)>>>0)na();else{c[s+20>>2]=i;c[i+24>>2]=s;break}}while(0);b:do if(n>>>0>=16){c[q+4>>2]=p|3;c[q+(p|4)>>2]=n|1;c[q+(n+p)>>2]=n;i=n>>>3;if(n>>>0<256){h=i<<1;b=212+(h<<2)|0;g=c[43]|0;i=1<<i;if(g&i){i=212+(h+2<<2)|0;h=c[i>>2]|0;if(h>>>0<(c[47]|0)>>>0)na();else{t=i;u=h}}else{c[43]=g|i;t=212+(h+2<<2)|0;u=b}c[t>>2]=m;c[u+12>>2]=m;c[q+(p+8)>>2]=u;c[q+(p+12)>>2]=b;break}d=n>>>8;if(d)if(n>>>0>16777215)b=31;else{v=(d+1048320|0)>>>16&8;w=d<<v;u=(w+520192|0)>>>16&4;w=w<<u;b=(w+245760|0)>>>16&2;b=14-(u|v|b)+(w<<b>>>15)|0;b=n>>>(b+7|0)&1|b<<1}else b=0;i=476+(b<<2)|0;c[q+(p+28)>>2]=b;c[q+(p+20)>>2]=0;c[q+(p+16)>>2]=0;h=c[44]|0;g=1<<b;if(!(h&g)){c[44]=h|g;c[i>>2]=m;c[q+(p+24)>>2]=i;c[q+(p+12)>>2]=m;c[q+(p+8)>>2]=m;break}d=c[i>>2]|0;c:do if((c[d+4>>2]&-8|0)!=(n|0)){h=n<<((b|0)==31?0:25-(b>>>1)|0);while(1){b=d+16+(h>>>31<<2)|0;i=c[b>>2]|0;if(!i)break;if((c[i+4>>2]&-8|0)==(n|0)){z=i;break c}else{h=h<<1;d=i}}if(b>>>0<(c[47]|0)>>>0)na();else{c[b>>2]=m;c[q+(p+24)>>2]=d;c[q+(p+12)>>2]=m;c[q+(p+8)>>2]=m;break b}}else z=d;while(0);d=z+8|0;b=c[d>>2]|0;w=c[47]|0;if(b>>>0>=w>>>0&z>>>0>=w>>>0){c[b+12>>2]=m;c[d>>2]=m;c[q+(p+8)>>2]=b;c[q+(p+12)>>2]=z;c[q+(p+24)>>2]=0;break}else na()}else{w=n+p|0;c[q+4>>2]=w|3;w=q+(w+4)|0;c[w>>2]=c[w>>2]|1}while(0);w=q+8|0;return w|0}else z=p}else z=p}else z=-1;while(0);a=c[45]|0;if(a>>>0>=z>>>0){b=a-z|0;d=c[48]|0;if(b>>>0>15){c[48]=d+z;c[45]=b;c[d+(z+4)>>2]=b|1;c[d+a>>2]=b;c[d+4>>2]=z|3}else{c[45]=0;c[48]=0;c[d+4>>2]=a|3;w=d+(a+4)|0;c[w>>2]=c[w>>2]|1}w=d+8|0;return w|0}a=c[46]|0;if(a>>>0>z>>>0){v=a-z|0;c[46]=v;w=c[49]|0;c[49]=w+z;c[w+(z+4)>>2]=v|1;c[w+4>>2]=z|3;w=w+8|0;return w|0}do if(!(c[161]|0)){a=ja(30)|0;if(!(a+-1&a)){c[163]=a;c[162]=a;c[164]=-1;c[165]=-1;c[166]=0;c[154]=0;c[161]=(pa(0)|0)&-16^1431655768;break}else na()}while(0);l=z+48|0;e=c[163]|0;f=z+47|0;d=e+f|0;e=0-e|0;m=d&e;if(m>>>0<=z>>>0){w=0;return w|0}a=c[153]|0;if((a|0)!=0?(t=c[151]|0,u=t+m|0,u>>>0<=t>>>0|u>>>0>a>>>0):0){w=0;return w|0}d:do if(!(c[154]&4)){a=c[49]|0;e:do if(a){h=620;while(1){j=c[h>>2]|0;if(j>>>0<=a>>>0?(r=h+4|0,(j+(c[r>>2]|0)|0)>>>0>a>>>0):0){g=h;a=r;break}h=c[h+8>>2]|0;if(!h){w=174;break e}}j=d-(c[46]|0)&e;if(j>>>0<2147483647){h=oa(j|0)|0;u=(h|0)==((c[g>>2]|0)+(c[a>>2]|0)|0);a=u?j:0;if(u){if((h|0)!=(-1|0)){x=h;w=194;break d}}else w=184}else a=0}else w=174;while(0);do if((w|0)==174){g=oa(0)|0;if((g|0)!=(-1|0)){a=g;j=c[162]|0;h=j+-1|0;if(!(h&a))j=m;else j=m-a+(h+a&0-j)|0;a=c[151]|0;h=a+j|0;if(j>>>0>z>>>0&j>>>0<2147483647){u=c[153]|0;if((u|0)!=0?h>>>0<=a>>>0|h>>>0>u>>>0:0){a=0;break}h=oa(j|0)|0;w=(h|0)==(g|0);a=w?j:0;if(w){x=g;w=194;break d}else w=184}else a=0}else a=0}while(0);f:do if((w|0)==184){g=0-j|0;do if(l>>>0>j>>>0&(j>>>0<2147483647&(h|0)!=(-1|0))?(v=c[163]|0,v=f-j+v&0-v,v>>>0<2147483647):0)if((oa(v|0)|0)==(-1|0)){oa(g|0)|0;break f}else{j=v+j|0;break}while(0);if((h|0)!=(-1|0)){x=h;a=j;w=194;break d}}while(0);c[154]=c[154]|4;w=191}else{a=0;w=191}while(0);if((((w|0)==191?m>>>0<2147483647:0)?(x=oa(m|0)|0,y=oa(0)|0,x>>>0<y>>>0&((x|0)!=(-1|0)&(y|0)!=(-1|0))):0)?(A=y-x|0,B=A>>>0>(z+40|0)>>>0,B):0){a=B?A:a;w=194}if((w|0)==194){j=(c[151]|0)+a|0;c[151]=j;if(j>>>0>(c[152]|0)>>>0)c[152]=j;n=c[49]|0;g:do if(n){d=620;do{j=c[d>>2]|0;h=d+4|0;g=c[h>>2]|0;if((x|0)==(j+g|0)){C=j;D=h;E=g;F=d;w=204;break}d=c[d+8>>2]|0}while((d|0)!=0);if(((w|0)==204?(c[F+12>>2]&8|0)==0:0)?n>>>0<x>>>0&n>>>0>=C>>>0:0){c[D>>2]=E+a;w=(c[46]|0)+a|0;v=n+8|0;v=(v&7|0)==0?0:0-v&7;u=w-v|0;c[49]=n+v;c[46]=u;c[n+(v+4)>>2]=u|1;c[n+(w+4)>>2]=40;c[50]=c[165];break}j=c[47]|0;if(x>>>0<j>>>0){c[47]=x;j=x}h=x+a|0;d=620;while(1){if((c[d>>2]|0)==(h|0)){g=d;h=d;w=212;break}d=c[d+8>>2]|0;if(!d){g=620;break}}if((w|0)==212)if(!(c[h+12>>2]&8)){c[g>>2]=x;p=h+4|0;c[p>>2]=(c[p>>2]|0)+a;p=x+8|0;p=(p&7|0)==0?0:0-p&7;k=x+(a+8)|0;k=(k&7|0)==0?0:0-k&7;i=x+(k+a)|0;o=p+z|0;q=x+o|0;m=i-(x+p)-z|0;c[x+(p+4)>>2]=z|3;h:do if((i|0)!=(n|0)){if((i|0)==(c[48]|0)){w=(c[45]|0)+m|0;c[45]=w;c[48]=q;c[x+(o+4)>>2]=w|1;c[x+(w+o)>>2]=w;break}l=a+4|0;h=c[x+(l+k)>>2]|0;if((h&3|0)==1){f=h&-8;d=h>>>3;i:do if(h>>>0>=256){e=c[x+((k|24)+a)>>2]|0;g=c[x+(a+12+k)>>2]|0;do if((g|0)==(i|0)){b=k|16;g=x+(l+b)|0;h=c[g>>2]|0;if(!h){g=x+(b+a)|0;h=c[g>>2]|0;if(!h){K=0;break}}while(1){b=h+20|0;d=c[b>>2]|0;if(d){h=d;g=b;continue}b=h+16|0;d=c[b>>2]|0;if(!d)break;else{h=d;g=b}}if(g>>>0<j>>>0)na();else{c[g>>2]=0;K=h;break}}else{b=c[x+((k|8)+a)>>2]|0;if(b>>>0<j>>>0)na();j=b+12|0;if((c[j>>2]|0)!=(i|0))na();h=g+8|0;if((c[h>>2]|0)==(i|0)){c[j>>2]=g;c[h>>2]=b;K=g;break}else na()}while(0);if(!e)break;j=c[x+(a+28+k)>>2]|0;h=476+(j<<2)|0;do if((i|0)!=(c[h>>2]|0)){if(e>>>0<(c[47]|0)>>>0)na();j=e+16|0;if((c[j>>2]|0)==(i|0))c[j>>2]=K;else c[e+20>>2]=K;if(!K)break i}else{c[h>>2]=K;if(K)break;c[44]=c[44]&~(1<<j);break i}while(0);h=c[47]|0;if(K>>>0<h>>>0)na();c[K+24>>2]=e;j=k|16;i=c[x+(j+a)>>2]|0;do if(i)if(i>>>0<h>>>0)na();else{c[K+16>>2]=i;c[i+24>>2]=K;break}while(0);i=c[x+(l+j)>>2]|0;if(!i)break;if(i>>>0<(c[47]|0)>>>0)na();else{c[K+20>>2]=i;c[i+24>>2]=K;break}}else{g=c[x+((k|8)+a)>>2]|0;b=c[x+(a+12+k)>>2]|0;h=212+(d<<1<<2)|0;do if((g|0)!=(h|0)){if(g>>>0<j>>>0)na();if((c[g+12>>2]|0)==(i|0))break;na()}while(0);if((b|0)==(g|0)){c[43]=c[43]&~(1<<d);break}do if((b|0)==(h|0))G=b+8|0;else{if(b>>>0<j>>>0)na();j=b+8|0;if((c[j>>2]|0)==(i|0)){G=j;break}na()}while(0);c[g+12>>2]=b;c[G>>2]=g}while(0);i=x+((f|k)+a)|0;j=f+m|0}else j=m;i=i+4|0;c[i>>2]=c[i>>2]&-2;c[x+(o+4)>>2]=j|1;c[x+(j+o)>>2]=j;i=j>>>3;if(j>>>0<256){h=i<<1;b=212+(h<<2)|0;g=c[43]|0;i=1<<i;do if(!(g&i)){c[43]=g|i;L=212+(h+2<<2)|0;M=b}else{i=212+(h+2<<2)|0;h=c[i>>2]|0;if(h>>>0>=(c[47]|0)>>>0){L=i;M=h;break}na()}while(0);c[L>>2]=q;c[M+12>>2]=q;c[x+(o+8)>>2]=M;c[x+(o+12)>>2]=b;break}d=j>>>8;do if(!d)b=0;else{if(j>>>0>16777215){b=31;break}v=(d+1048320|0)>>>16&8;w=d<<v;u=(w+520192|0)>>>16&4;w=w<<u;b=(w+245760|0)>>>16&2;b=14-(u|v|b)+(w<<b>>>15)|0;b=j>>>(b+7|0)&1|b<<1}while(0);i=476+(b<<2)|0;c[x+(o+28)>>2]=b;c[x+(o+20)>>2]=0;c[x+(o+16)>>2]=0;h=c[44]|0;g=1<<b;if(!(h&g)){c[44]=h|g;c[i>>2]=q;c[x+(o+24)>>2]=i;c[x+(o+12)>>2]=q;c[x+(o+8)>>2]=q;break}d=c[i>>2]|0;j:do if((c[d+4>>2]&-8|0)!=(j|0)){h=j<<((b|0)==31?0:25-(b>>>1)|0);while(1){b=d+16+(h>>>31<<2)|0;i=c[b>>2]|0;if(!i)break;if((c[i+4>>2]&-8|0)==(j|0)){N=i;break j}else{h=h<<1;d=i}}if(b>>>0<(c[47]|0)>>>0)na();else{c[b>>2]=q;c[x+(o+24)>>2]=d;c[x+(o+12)>>2]=q;c[x+(o+8)>>2]=q;break h}}else N=d;while(0);d=N+8|0;b=c[d>>2]|0;w=c[47]|0;if(b>>>0>=w>>>0&N>>>0>=w>>>0){c[b+12>>2]=q;c[d>>2]=q;c[x+(o+8)>>2]=b;c[x+(o+12)>>2]=N;c[x+(o+24)>>2]=0;break}else na()}else{w=(c[46]|0)+m|0;c[46]=w;c[49]=q;c[x+(o+4)>>2]=w|1}while(0);w=x+(p|8)|0;return w|0}else g=620;while(1){h=c[g>>2]|0;if(h>>>0<=n>>>0?(i=c[g+4>>2]|0,b=h+i|0,b>>>0>n>>>0):0)break;g=c[g+8>>2]|0}j=h+(i+-39)|0;h=h+(i+-47+((j&7|0)==0?0:0-j&7))|0;j=n+16|0;h=h>>>0<j>>>0?n:h;i=h+8|0;g=x+8|0;g=(g&7|0)==0?0:0-g&7;w=a+-40-g|0;c[49]=x+g;c[46]=w;c[x+(g+4)>>2]=w|1;c[x+(a+-36)>>2]=40;c[50]=c[165];g=h+4|0;c[g>>2]=27;c[i>>2]=c[155];c[i+4>>2]=c[156];c[i+8>>2]=c[157];c[i+12>>2]=c[158];c[155]=x;c[156]=a;c[158]=0;c[157]=i;i=h+28|0;c[i>>2]=7;if((h+32|0)>>>0<b>>>0)do{w=i;i=i+4|0;c[i>>2]=7}while((w+8|0)>>>0<b>>>0);if((h|0)!=(n|0)){f=h-n|0;c[g>>2]=c[g>>2]&-2;c[n+4>>2]=f|1;c[h>>2]=f;i=f>>>3;if(f>>>0<256){h=i<<1;e=212+(h<<2)|0;g=c[43]|0;i=1<<i;if(g&i){d=212+(h+2<<2)|0;b=c[d>>2]|0;if(b>>>0<(c[47]|0)>>>0)na();else{H=d;I=b}}else{c[43]=g|i;H=212+(h+2<<2)|0;I=e}c[H>>2]=n;c[I+12>>2]=n;c[n+8>>2]=I;c[n+12>>2]=e;break}d=f>>>8;if(d)if(f>>>0>16777215)h=31;else{v=(d+1048320|0)>>>16&8;w=d<<v;u=(w+520192|0)>>>16&4;w=w<<u;h=(w+245760|0)>>>16&2;h=14-(u|v|h)+(w<<h>>>15)|0;h=f>>>(h+7|0)&1|h<<1}else h=0;i=476+(h<<2)|0;c[n+28>>2]=h;c[n+20>>2]=0;c[j>>2]=0;d=c[44]|0;b=1<<h;if(!(d&b)){c[44]=d|b;c[i>>2]=n;c[n+24>>2]=i;c[n+12>>2]=n;c[n+8>>2]=n;break}d=c[i>>2]|0;k:do if((c[d+4>>2]&-8|0)!=(f|0)){i=f<<((h|0)==31?0:25-(h>>>1)|0);while(1){b=d+16+(i>>>31<<2)|0;e=c[b>>2]|0;if(!e)break;if((c[e+4>>2]&-8|0)==(f|0)){J=e;break k}else{i=i<<1;d=e}}if(b>>>0<(c[47]|0)>>>0)na();else{c[b>>2]=n;c[n+24>>2]=d;c[n+12>>2]=n;c[n+8>>2]=n;break g}}else J=d;while(0);d=J+8|0;b=c[d>>2]|0;w=c[47]|0;if(b>>>0>=w>>>0&J>>>0>=w>>>0){c[b+12>>2]=n;c[d>>2]=n;c[n+8>>2]=b;c[n+12>>2]=J;c[n+24>>2]=0;break}else na()}}else{w=c[47]|0;if((w|0)==0|x>>>0<w>>>0)c[47]=x;c[155]=x;c[156]=a;c[158]=0;c[52]=c[161];c[51]=-1;d=0;do{w=d<<1;v=212+(w<<2)|0;c[212+(w+3<<2)>>2]=v;c[212+(w+2<<2)>>2]=v;d=d+1|0}while((d|0)!=32);w=x+8|0;w=(w&7|0)==0?0:0-w&7;v=a+-40-w|0;c[49]=x+w;c[46]=v;c[x+(w+4)>>2]=v|1;c[x+(a+-36)>>2]=40;c[50]=c[165]}while(0);b=c[46]|0;if(b>>>0>z>>>0){v=b-z|0;c[46]=v;w=c[49]|0;c[49]=w+z;c[w+(z+4)>>2]=v|1;c[w+4>>2]=z|3;w=w+8|0;return w|0}}c[(Ra()|0)>>2]=12;w=0;return w|0}function bb(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0;if(!a)return;g=a+-8|0;h=c[47]|0;if(g>>>0<h>>>0)na();b=c[a+-4>>2]|0;d=b&3;if((d|0)==1)na();o=b&-8;q=a+(o+-8)|0;do if(!(b&1)){g=c[g>>2]|0;if(!d)return;i=-8-g|0;l=a+i|0;m=g+o|0;if(l>>>0<h>>>0)na();if((l|0)==(c[48]|0)){g=a+(o+-4)|0;b=c[g>>2]|0;if((b&3|0)!=3){u=l;k=m;break}c[45]=m;c[g>>2]=b&-2;c[a+(i+4)>>2]=m|1;c[q>>2]=m;return}e=g>>>3;if(g>>>0<256){d=c[a+(i+8)>>2]|0;b=c[a+(i+12)>>2]|0;g=212+(e<<1<<2)|0;if((d|0)!=(g|0)){if(d>>>0<h>>>0)na();if((c[d+12>>2]|0)!=(l|0))na()}if((b|0)==(d|0)){c[43]=c[43]&~(1<<e);u=l;k=m;break}if((b|0)!=(g|0)){if(b>>>0<h>>>0)na();g=b+8|0;if((c[g>>2]|0)==(l|0))f=g;else na()}else f=b+8|0;c[d+12>>2]=b;c[f>>2]=d;u=l;k=m;break}f=c[a+(i+24)>>2]|0;d=c[a+(i+12)>>2]|0;do if((d|0)==(l|0)){b=a+(i+20)|0;g=c[b>>2]|0;if(!g){b=a+(i+16)|0;g=c[b>>2]|0;if(!g){j=0;break}}while(1){d=g+20|0;e=c[d>>2]|0;if(e){g=e;b=d;continue}d=g+16|0;e=c[d>>2]|0;if(!e)break;else{g=e;b=d}}if(b>>>0<h>>>0)na();else{c[b>>2]=0;j=g;break}}else{e=c[a+(i+8)>>2]|0;if(e>>>0<h>>>0)na();g=e+12|0;if((c[g>>2]|0)!=(l|0))na();b=d+8|0;if((c[b>>2]|0)==(l|0)){c[g>>2]=d;c[b>>2]=e;j=d;break}else na()}while(0);if(f){g=c[a+(i+28)>>2]|0;b=476+(g<<2)|0;if((l|0)==(c[b>>2]|0)){c[b>>2]=j;if(!j){c[44]=c[44]&~(1<<g);u=l;k=m;break}}else{if(f>>>0<(c[47]|0)>>>0)na();g=f+16|0;if((c[g>>2]|0)==(l|0))c[g>>2]=j;else c[f+20>>2]=j;if(!j){u=l;k=m;break}}b=c[47]|0;if(j>>>0<b>>>0)na();c[j+24>>2]=f;g=c[a+(i+16)>>2]|0;do if(g)if(g>>>0<b>>>0)na();else{c[j+16>>2]=g;c[g+24>>2]=j;break}while(0);g=c[a+(i+20)>>2]|0;if(g)if(g>>>0<(c[47]|0)>>>0)na();else{c[j+20>>2]=g;c[g+24>>2]=j;u=l;k=m;break}else{u=l;k=m}}else{u=l;k=m}}else{u=g;k=o}while(0);if(u>>>0>=q>>>0)na();g=a+(o+-4)|0;b=c[g>>2]|0;if(!(b&1))na();if(!(b&2)){if((q|0)==(c[49]|0)){l=(c[46]|0)+k|0;c[46]=l;c[49]=u;c[u+4>>2]=l|1;if((u|0)!=(c[48]|0))return;c[48]=0;c[45]=0;return}if((q|0)==(c[48]|0)){l=(c[45]|0)+k|0;c[45]=l;c[48]=u;c[u+4>>2]=l|1;c[u+l>>2]=l;return}h=(b&-8)+k|0;e=b>>>3;do if(b>>>0>=256){f=c[a+(o+16)>>2]|0;g=c[a+(o|4)>>2]|0;do if((g|0)==(q|0)){b=a+(o+12)|0;g=c[b>>2]|0;if(!g){b=a+(o+8)|0;g=c[b>>2]|0;if(!g){p=0;break}}while(1){d=g+20|0;e=c[d>>2]|0;if(e){g=e;b=d;continue}d=g+16|0;e=c[d>>2]|0;if(!e)break;else{g=e;b=d}}if(b>>>0<(c[47]|0)>>>0)na();else{c[b>>2]=0;p=g;break}}else{b=c[a+o>>2]|0;if(b>>>0<(c[47]|0)>>>0)na();d=b+12|0;if((c[d>>2]|0)!=(q|0))na();e=g+8|0;if((c[e>>2]|0)==(q|0)){c[d>>2]=g;c[e>>2]=b;p=g;break}else na()}while(0);if(f){g=c[a+(o+20)>>2]|0;b=476+(g<<2)|0;if((q|0)==(c[b>>2]|0)){c[b>>2]=p;if(!p){c[44]=c[44]&~(1<<g);break}}else{if(f>>>0<(c[47]|0)>>>0)na();g=f+16|0;if((c[g>>2]|0)==(q|0))c[g>>2]=p;else c[f+20>>2]=p;if(!p)break}g=c[47]|0;if(p>>>0<g>>>0)na();c[p+24>>2]=f;f=c[a+(o+8)>>2]|0;do if(f)if(f>>>0<g>>>0)na();else{c[p+16>>2]=f;c[f+24>>2]=p;break}while(0);d=c[a+(o+12)>>2]|0;if(d)if(d>>>0<(c[47]|0)>>>0)na();else{c[p+20>>2]=d;c[d+24>>2]=p;break}}}else{d=c[a+o>>2]|0;b=c[a+(o|4)>>2]|0;g=212+(e<<1<<2)|0;if((d|0)!=(g|0)){if(d>>>0<(c[47]|0)>>>0)na();if((c[d+12>>2]|0)!=(q|0))na()}if((b|0)==(d|0)){c[43]=c[43]&~(1<<e);break}if((b|0)!=(g|0)){if(b>>>0<(c[47]|0)>>>0)na();f=b+8|0;if((c[f>>2]|0)==(q|0))n=f;else na()}else n=b+8|0;c[d+12>>2]=b;c[n>>2]=d}while(0);c[u+4>>2]=h|1;c[u+h>>2]=h;if((u|0)==(c[48]|0)){c[45]=h;return}else g=h}else{c[g>>2]=b&-2;c[u+4>>2]=k|1;c[u+k>>2]=k;g=k}f=g>>>3;if(g>>>0<256){e=f<<1;g=212+(e<<2)|0;b=c[43]|0;d=1<<f;if(b&d){d=212+(e+2<<2)|0;b=c[d>>2]|0;if(b>>>0<(c[47]|0)>>>0)na();else{r=d;s=b}}else{c[43]=b|d;r=212+(e+2<<2)|0;s=g}c[r>>2]=u;c[s+12>>2]=u;c[u+8>>2]=s;c[u+12>>2]=g;return}b=g>>>8;if(b)if(g>>>0>16777215)f=31;else{k=(b+1048320|0)>>>16&8;l=b<<k;j=(l+520192|0)>>>16&4;l=l<<j;f=(l+245760|0)>>>16&2;f=14-(j|k|f)+(l<<f>>>15)|0;f=g>>>(f+7|0)&1|f<<1}else f=0;d=476+(f<<2)|0;c[u+28>>2]=f;c[u+20>>2]=0;c[u+16>>2]=0;b=c[44]|0;e=1<<f;a:do if(b&e){d=c[d>>2]|0;b:do if((c[d+4>>2]&-8|0)!=(g|0)){f=g<<((f|0)==31?0:25-(f>>>1)|0);while(1){b=d+16+(f>>>31<<2)|0;e=c[b>>2]|0;if(!e)break;if((c[e+4>>2]&-8|0)==(g|0)){t=e;break b}else{f=f<<1;d=e}}if(b>>>0<(c[47]|0)>>>0)na();else{c[b>>2]=u;c[u+24>>2]=d;c[u+12>>2]=u;c[u+8>>2]=u;break a}}else t=d;while(0);b=t+8|0;d=c[b>>2]|0;l=c[47]|0;if(d>>>0>=l>>>0&t>>>0>=l>>>0){c[d+12>>2]=u;c[b>>2]=u;c[u+8>>2]=d;c[u+12>>2]=t;c[u+24>>2]=0;break}else na()}else{c[44]=b|e;c[d>>2]=u;c[u+24>>2]=d;c[u+12>>2]=u;c[u+8>>2]=u}while(0);l=(c[51]|0)+-1|0;c[51]=l;if(!l)b=628;else return;while(1){b=c[b>>2]|0;if(!b)break;else b=b+8|0}c[51]=-1;return}function cb(){}function db(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0;f=b+e|0;if((e|0)>=20){d=d&255;h=b&3;i=d|d<<8|d<<16|d<<24;g=f&~3;if(h){h=b+4-h|0;while((b|0)<(h|0)){a[b>>0]=d;b=b+1|0}}while((b|0)<(g|0)){c[b>>2]=i;b=b+4|0}}while((b|0)<(f|0)){a[b>>0]=d;b=b+1|0}return b-e|0}function eb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0;if((e|0)>=4096)return ra(b|0,d|0,e|0)|0;f=b|0;if((b&3)==(d&3)){while(b&3){if(!e)return f|0;a[b>>0]=a[d>>0]|0;b=b+1|0;d=d+1|0;e=e-1|0}while((e|0)>=4){c[b>>2]=c[d>>2];b=b+4|0;d=d+4|0;e=e-4|0}}while((e|0)>0){a[b>>0]=a[d>>0]|0;b=b+1|0;d=d+1|0;e=e-1|0}return f|0}function fb(a,b){a=a|0;b=b|0;return ya[a&1](b|0)|0}function gb(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return za[a&3](b|0,c|0,d|0)|0}function hb(a,b){a=a|0;b=b|0;Aa[a&1](b|0)}function ib(a){a=a|0;aa(0);return 0}function jb(a,b,c){a=a|0;b=b|0;c=c|0;aa(1);return 0}function kb(a){a=a|0;aa(2)}
+function Ba(a){a=a|0;var b=0;b=i;i=i+a|0;i=i+15&-16;return b|0}function Ca(){return i|0}function Da(a){a=a|0;i=a}function Ea(a,b){a=a|0;b=b|0;i=a;j=b}function Fa(a,b){a=a|0;b=b|0;if(!m){m=a;n=b}}function Ga(b){b=b|0;a[k>>0]=a[b>>0];a[k+1>>0]=a[b+1>>0];a[k+2>>0]=a[b+2>>0];a[k+3>>0]=a[b+3>>0]}function Ha(b){b=b|0;a[k>>0]=a[b>>0];a[k+1>>0]=a[b+1>>0];a[k+2>>0]=a[b+2>>0];a[k+3>>0]=a[b+3>>0];a[k+4>>0]=a[b+4>>0];a[k+5>>0]=a[b+5>>0];a[k+6>>0]=a[b+6>>0];a[k+7>>0]=a[b+7>>0]}function Ia(a){a=a|0;B=a}function Ja(){return B|0}function Ka(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,h=0.0,i=0,j=0.0;f=(a<<3)+264|0;if(!e)i=ab(f)|0;else{if(!d)d=0;else d=(c[e>>2]|0)>>>0<f>>>0?0:d;c[e>>2]=f;i=d}if(!i)return i|0;c[i>>2]=a;e=i+4|0;c[e>>2]=b;h=+(a|0);a:do if((a|0)>0){f=b;d=0;while(1){j=+(d|0)*-6.283185307179586/h;j=(f|0)==0?j:-j;g[i+264+(d<<3)>>2]=+P(+j);g[i+264+(d<<3)+4>>2]=+Q(+j);d=d+1|0;if((d|0)==(a|0))break a;f=c[e>>2]|0}}while(0);h=+L(+(+N(+h)));f=a;e=i+8|0;d=4;while(1){b:do if((f|0)%(d|0)|0)while(1){switch(d|0){case 4:{d=2;break}case 2:{d=3;break}default:d=d+2|0}d=+(d|0)>h?f:d;if(!((f|0)%(d|0)|0))break b}while(0);f=(f|0)/(d|0)|0;c[e>>2]=d;c[e+4>>2]=f;if((f|0)<=1)break;else e=e+8|0}return i|0}function La(a,b,d,e,f,h){a=a|0;b=b|0;d=d|0;e=e|0;f=f|0;h=h|0;var i=0,j=0,l=0,m=0,n=0.0,o=0.0,p=0.0,q=0.0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0.0,z=0.0,A=0.0,B=0.0,C=0,D=0.0,E=0.0,F=0.0,G=0.0,H=0.0,I=0.0,J=0.0,K=0.0,L=0.0,M=0.0;w=c[f>>2]|0;m=f+8|0;x=c[f+4>>2]|0;r=a+((Z(x,w)|0)<<3)|0;if((x|0)==1){j=Z(e,d)|0;i=a;f=b;while(1){t=f;u=c[t+4>>2]|0;v=i;c[v>>2]=c[t>>2];c[v+4>>2]=u;i=i+8|0;if((i|0)==(r|0))break;else f=f+(j<<3)|0}}else{j=Z(w,d)|0;l=Z(e,d)|0;i=a;f=b;while(1){La(i,f,j,e,m,h);i=i+(x<<3)|0;if((i|0)==(r|0))break;else f=f+(l<<3)|0}}switch(w|0){case 2:{j=a;l=x;i=a+(x<<3)|0;f=h+264|0;while(1){o=+g[i>>2];y=+g[f>>2];a=i+4|0;n=+g[a>>2];q=+g[f+4>>2];p=o*y-n*q;q=y*n+o*q;g[i>>2]=+g[j>>2]-p;x=j+4|0;g[a>>2]=+g[x>>2]-q;g[j>>2]=p+ +g[j>>2];g[x>>2]=q+ +g[x>>2];l=l+-1|0;if(!l)break;else{j=j+8|0;i=i+8|0;f=f+(d<<3)|0}}return}case 3:{e=x<<1;n=+g[h+264+((Z(x,d)|0)<<3)+4>>2];l=h+264|0;m=d<<1;f=a;i=x;j=l;while(1){h=f+(x<<3)|0;o=+g[h>>2];p=+g[j>>2];a=f+(x<<3)+4|0;B=+g[a>>2];z=+g[j+4>>2];A=o*p-B*z;z=p*B+o*z;v=f+(e<<3)|0;o=+g[v>>2];B=+g[l>>2];w=f+(e<<3)+4|0;p=+g[w>>2];q=+g[l+4>>2];y=o*B-p*q;q=B*p+o*q;o=A+y;p=z+q;g[h>>2]=+g[f>>2]-o*.5;u=f+4|0;g[a>>2]=+g[u>>2]-p*.5;y=n*(A-y);q=n*(z-q);g[f>>2]=+g[f>>2]+o;g[u>>2]=p+ +g[u>>2];g[v>>2]=q+ +g[h>>2];g[w>>2]=+g[a>>2]-y;g[h>>2]=+g[h>>2]-q;g[a>>2]=y+ +g[a>>2];i=i+-1|0;if(!i)break;else{f=f+8|0;j=j+(d<<3)|0;l=l+(m<<3)|0}}return}case 4:{e=x<<1;b=x*3|0;f=h+264|0;r=d<<1;s=d*3|0;if(!(c[h+4>>2]|0)){i=a;j=x;l=f;m=f;while(1){v=i+(x<<3)|0;n=+g[v>>2];o=+g[l>>2];w=i+(x<<3)+4|0;y=+g[w>>2];D=+g[l+4>>2];E=n*o-y*D;D=o*y+n*D;C=i+(e<<3)|0;n=+g[C>>2];y=+g[m>>2];t=i+(e<<3)+4|0;o=+g[t>>2];p=+g[m+4>>2];q=n*y-o*p;p=y*o+n*p;h=i+(b<<3)|0;n=+g[h>>2];o=+g[f>>2];a=i+(b<<3)+4|0;y=+g[a>>2];z=+g[f+4>>2];B=n*o-y*z;z=o*y+n*z;n=+g[i>>2];y=n-q;u=i+4|0;o=+g[u>>2];A=o-p;n=q+n;g[i>>2]=n;o=p+o;g[u>>2]=o;p=E+B;q=D+z;B=E-B;z=D-z;g[C>>2]=n-p;g[t>>2]=o-q;g[i>>2]=p+ +g[i>>2];g[u>>2]=q+ +g[u>>2];g[v>>2]=y+z;g[w>>2]=A-B;g[h>>2]=y-z;g[a>>2]=A+B;j=j+-1|0;if(!j)break;else{i=i+8|0;l=l+(d<<3)|0;m=m+(r<<3)|0;f=f+(s<<3)|0}}return}else{i=a;j=x;l=f;m=f;while(1){w=i+(x<<3)|0;p=+g[w>>2];q=+g[l>>2];h=i+(x<<3)+4|0;A=+g[h>>2];o=+g[l+4>>2];n=p*q-A*o;o=q*A+p*o;t=i+(e<<3)|0;p=+g[t>>2];A=+g[m>>2];u=i+(e<<3)+4|0;q=+g[u>>2];y=+g[m+4>>2];z=p*A-q*y;y=A*q+p*y;a=i+(b<<3)|0;p=+g[a>>2];q=+g[f>>2];C=i+(b<<3)+4|0;A=+g[C>>2];B=+g[f+4>>2];E=p*q-A*B;B=q*A+p*B;p=+g[i>>2];A=p-z;v=i+4|0;q=+g[v>>2];D=q-y;p=z+p;g[i>>2]=p;q=y+q;g[v>>2]=q;y=n+E;z=o+B;E=n-E;B=o-B;g[t>>2]=p-y;g[u>>2]=q-z;g[i>>2]=y+ +g[i>>2];g[v>>2]=z+ +g[v>>2];g[w>>2]=A-B;g[h>>2]=D+E;g[a>>2]=A+B;g[C>>2]=D-E;j=j+-1|0;if(!j)break;else{i=i+8|0;l=l+(d<<3)|0;m=m+(r<<3)|0;f=f+(s<<3)|0}}return}}case 5:{C=Z(x,d)|0;n=+g[h+264+(C<<3)>>2];o=+g[h+264+(C<<3)+4>>2];C=Z(x,d<<1)|0;p=+g[h+264+(C<<3)>>2];q=+g[h+264+(C<<3)+4>>2];if((x|0)<=0)return;j=d*3|0;l=a;m=a+(x<<3)|0;e=a+(x<<1<<3)|0;b=a+(x*3<<3)|0;f=a+(x<<2<<3)|0;i=0;while(1){H=+g[l>>2];u=l+4|0;F=+g[u>>2];A=+g[m>>2];t=Z(i,d)|0;D=+g[h+264+(t<<3)>>2];v=m+4|0;M=+g[v>>2];I=+g[h+264+(t<<3)+4>>2];G=A*D-M*I;I=D*M+A*I;A=+g[e>>2];t=Z(i<<1,d)|0;M=+g[h+264+(t<<3)>>2];a=e+4|0;D=+g[a>>2];L=+g[h+264+(t<<3)+4>>2];J=A*M-D*L;L=M*D+A*L;A=+g[b>>2];t=Z(j,i)|0;D=+g[h+264+(t<<3)>>2];C=b+4|0;M=+g[C>>2];y=+g[h+264+(t<<3)+4>>2];E=A*D-M*y;y=D*M+A*y;A=+g[f>>2];t=Z(i<<2,d)|0;M=+g[h+264+(t<<3)>>2];w=f+4|0;D=+g[w>>2];B=+g[h+264+(t<<3)+4>>2];z=A*M-D*B;B=M*D+A*B;A=G+z;D=I+B;z=G-z;B=I-B;I=J+E;G=L+y;E=J-E;y=L-y;g[l>>2]=H+(I+A);g[u>>2]=F+(G+D);L=p*I+(H+n*A);J=p*G+(F+n*D);M=q*y+o*B;K=-(o*z)-q*E;g[m>>2]=L-M;g[v>>2]=J-K;g[f>>2]=M+L;g[w>>2]=K+J;A=n*I+(H+p*A);D=n*G+(F+p*D);B=o*y-q*B;E=q*z-o*E;g[e>>2]=B+A;g[a>>2]=E+D;g[b>>2]=A-B;g[C>>2]=D-E;i=i+1|0;if((i|0)==(x|0))break;else{l=l+8|0;m=m+8|0;e=e+8|0;b=b+8|0;f=f+8|0}}return}default:{t=c[h>>2]|0;v=ab(w<<3)|0;a:do if((x|0)>0?(w|0)>0:0){if((w|0)>1)u=0;else{m=0;while(1){f=m;i=0;while(1){h=a+(f<<3)|0;d=c[h+4>>2]|0;C=v+(i<<3)|0;c[C>>2]=c[h>>2];c[C+4>>2]=d;i=i+1|0;if((i|0)==(w|0))break;else f=f+x|0}i=v;f=c[i>>2]|0;i=c[i+4>>2]|0;j=m;l=0;while(1){C=a+(j<<3)|0;c[C>>2]=f;c[C+4>>2]=i;l=l+1|0;if((l|0)==(w|0))break;else j=j+x|0}m=m+1|0;if((m|0)==(x|0))break a}}do{f=u;i=0;while(1){r=a+(f<<3)|0;s=c[r+4>>2]|0;C=v+(i<<3)|0;c[C>>2]=c[r>>2];c[C+4>>2]=s;i=i+1|0;if((i|0)==(w|0))break;else f=f+x|0}i=v;f=c[i>>2]|0;i=c[i+4>>2]|0;n=(c[k>>2]=f,+g[k>>2]);e=u;r=0;while(1){j=a+(e<<3)|0;l=j;c[l>>2]=f;c[l+4>>2]=i;l=Z(e,d)|0;m=a+(e<<3)+4|0;o=n;p=+g[m>>2];b=1;s=0;do{C=s+l|0;s=C-((C|0)<(t|0)?0:t)|0;L=+g[v+(b<<3)>>2];J=+g[h+264+(s<<3)>>2];K=+g[v+(b<<3)+4>>2];M=+g[h+264+(s<<3)+4>>2];o=o+(L*J-K*M);g[j>>2]=o;p=p+(J*K+L*M);g[m>>2]=p;b=b+1|0}while((b|0)!=(w|0));r=r+1|0;if((r|0)==(w|0))break;else e=e+x|0}u=u+1|0}while((u|0)!=(x|0))}while(0);bb(v);return}}}function Ma(a,b,d){a=a|0;b=b|0;d=d|0;if((b|0)==(d|0)){d=ab(c[a>>2]<<3)|0;La(d,b,1,1,a+8|0,a);eb(b|0,d|0,c[a>>2]<<3|0)|0;bb(d);return}else{La(d,b,1,1,a+8|0,a);return}}function Na(a){a=a|0;bb(a);return}function Oa(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,h=0.0,j=0,k=0,l=0,m=0,n=0.0;l=i;i=i+16|0;k=l;if(a&1){Ya(668,36,1,c[13]|0)|0;b=0;i=l;return b|0}j=a>>1;Ka(j,b,0,k)|0;f=c[k>>2]|0;a=f+12+(((j*3|0)/2|0)<<3)|0;if(e){m=(c[e>>2]|0)>>>0<a>>>0;c[e>>2]=a;if(m){m=0;i=l;return m|0}}else d=ab(a)|0;if(!d){m=0;i=l;return m|0}e=d+12|0;c[d>>2]=e;m=e+f|0;c[d+4>>2]=m;a=d+8|0;c[a>>2]=m+(j<<3);Ka(j,b,e,k)|0;e=(j|0)/2|0;if((j|0)<=1){m=d;i=l;return m|0}h=+(j|0);f=c[a>>2]|0;if(!b){a=0;do{m=a;a=a+1|0;n=(+(a|0)/h+.5)*-3.141592653589793;g[f+(m<<3)>>2]=+P(+n);g[f+(m<<3)+4>>2]=+Q(+n)}while((a|0)<(e|0));i=l;return d|0}else{a=0;do{m=a;a=a+1|0;n=(+(a|0)/h+.5)*-3.141592653589793;g[f+(m<<3)>>2]=+P(+n);g[f+(m<<3)+4>>2]=+Q(+-n)}while((a|0)<(e|0));i=l;return d|0}return 0}function Pa(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,h=0,i=0,j=0.0,k=0.0,l=0.0,m=0.0,n=0.0,o=0.0,p=0.0,q=0;e=c[a>>2]|0;if(c[e+4>>2]|0){Ya(705,37,1,c[13]|0)|0;ua(1)}i=c[e>>2]|0;h=a+4|0;Ma(e,b,c[h>>2]|0);h=c[h>>2]|0;k=+g[h>>2];j=+g[h+4>>2];g[d>>2]=k+j;g[d+(i<<3)>>2]=k-j;g[d+4>>2]=0.0;g[d+(i<<3)+4>>2]=0.0;f=(i|0)/2|0;if((i|0)<2)return;e=c[a+8>>2]|0;b=1;while(1){j=+g[h+(b<<3)>>2];o=+g[h+(b<<3)+4>>2];a=i-b|0;n=+g[h+(a<<3)>>2];p=+g[h+(a<<3)+4>>2];m=j+n;k=o-p;n=j-n;p=o+p;q=b+-1|0;o=+g[e+(q<<3)>>2];j=+g[e+(q<<3)+4>>2];l=n*o-p*j;j=p*o+n*j;g[d+(b<<3)>>2]=(m+l)*.5;g[d+(b<<3)+4>>2]=(k+j)*.5;g[d+(a<<3)>>2]=(m-l)*.5;g[d+(a<<3)+4>>2]=(j-k)*.5;if((b|0)<(f|0))b=b+1|0;else break}return}function Qa(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,h=0,i=0,j=0,k=0,l=0.0,m=0.0,n=0.0,o=0.0,p=0.0,q=0.0,r=0.0,s=0;f=c[a>>2]|0;if(!(c[f+4>>2]|0)){Ya(705,37,1,c[13]|0)|0;ua(1)}j=c[f>>2]|0;i=b+(j<<3)|0;h=c[a+4>>2]|0;g[h>>2]=+g[b>>2]+ +g[i>>2];g[h+4>>2]=+g[b>>2]-+g[i>>2];i=(j|0)/2|0;if((j|0)<2){Ma(f,h,d);return}a=c[a+8>>2]|0;e=1;while(1){l=+g[b+(e<<3)>>2];q=+g[b+(e<<3)+4>>2];k=j-e|0;p=+g[b+(k<<3)>>2];r=+g[b+(k<<3)+4>>2];o=l+p;m=q-r;p=l-p;r=q+r;s=e+-1|0;q=+g[a+(s<<3)>>2];l=+g[a+(s<<3)+4>>2];n=p*q-r*l;l=r*q+p*l;g[h+(e<<3)>>2]=o+n;g[h+(e<<3)+4>>2]=m+l;g[h+(k<<3)>>2]=o-n;g[h+(k<<3)+4>>2]=-(m-l);if((e|0)<(i|0))e=e+1|0;else break}Ma(f,h,d);return}function Ra(){var a=0;if(!(c[2]|0))a=56;else a=c[(ka()|0)+60>>2]|0;return a|0}function Sa(a){a=a|0;if(a>>>0>4294963200){c[(Ra()|0)>>2]=0-a;a=-1}return a|0}function Ta(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0,h=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;q=i;i=i+48|0;n=q+16|0;m=q;e=q+32|0;o=a+28|0;f=c[o>>2]|0;c[e>>2]=f;p=a+20|0;f=(c[p>>2]|0)-f|0;c[e+4>>2]=f;c[e+8>>2]=b;c[e+12>>2]=d;k=a+60|0;l=a+44|0;b=2;f=f+d|0;while(1){if(!(c[2]|0)){c[n>>2]=c[k>>2];c[n+4>>2]=e;c[n+8>>2]=b;h=Sa(wa(146,n|0)|0)|0}else{qa(1,a|0);c[m>>2]=c[k>>2];c[m+4>>2]=e;c[m+8>>2]=b;h=Sa(wa(146,m|0)|0)|0;fa(0)}if((f|0)==(h|0)){f=6;break}if((h|0)<0){f=8;break}f=f-h|0;g=c[e+4>>2]|0;if(h>>>0<=g>>>0)if((b|0)==2){c[o>>2]=(c[o>>2]|0)+h;j=g;b=2}else j=g;else{j=c[l>>2]|0;c[o>>2]=j;c[p>>2]=j;j=c[e+12>>2]|0;h=h-g|0;e=e+8|0;b=b+-1|0}c[e>>2]=(c[e>>2]|0)+h;c[e+4>>2]=j-h}if((f|0)==6){n=c[l>>2]|0;c[a+16>>2]=n+(c[a+48>>2]|0);a=n;c[o>>2]=a;c[p>>2]=a}else if((f|0)==8){c[a+16>>2]=0;c[o>>2]=0;c[p>>2]=0;c[a>>2]=c[a>>2]|32;if((b|0)==2)d=0;else d=d-(c[e+4>>2]|0)|0}i=q;return d|0}function Ua(a){a=a|0;var b=0,d=0;b=i;i=i+16|0;d=b;c[d>>2]=c[a+60>>2];a=Sa(la(6,d|0)|0)|0;i=b;return a|0}function Va(b){b=b|0;var d=0,e=0;d=b+74|0;e=a[d>>0]|0;a[d>>0]=e+255|e;d=c[b>>2]|0;if(!(d&8)){c[b+8>>2]=0;c[b+4>>2]=0;d=c[b+44>>2]|0;c[b+28>>2]=d;c[b+20>>2]=d;c[b+16>>2]=d+(c[b+48>>2]|0);d=0}else{c[b>>2]=d|32;d=-1}return d|0}function Wa(a,b,d){a=a|0;b=b|0;d=d|0;var e=0,f=0,g=0;f=i;i=i+32|0;g=f;e=f+20|0;c[g>>2]=c[a+60>>2];c[g+4>>2]=0;c[g+8>>2]=b;c[g+12>>2]=e;c[g+16>>2]=d;if((Sa(ta(140,g|0)|0)|0)<0){c[e>>2]=-1;a=-1}else a=c[e>>2]|0;i=f;return a|0}function Xa(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0;f=e+16|0;g=c[f>>2]|0;if(!g)if(!(Va(e)|0)){g=c[f>>2]|0;h=5}else f=0;else h=5;a:do if((h|0)==5){i=e+20|0;f=c[i>>2]|0;h=f;if((g-f|0)>>>0<d>>>0){f=za[c[e+36>>2]&3](e,b,d)|0;break}b:do if((a[e+75>>0]|0)>-1){f=d;while(1){if(!f){g=h;f=0;break b}g=f+-1|0;if((a[b+g>>0]|0)==10)break;else f=g}if((za[c[e+36>>2]&3](e,b,f)|0)>>>0<f>>>0)break a;d=d-f|0;b=b+f|0;g=c[i>>2]|0}else{g=h;f=0}while(0);eb(g|0,b|0,d|0)|0;c[i>>2]=(c[i>>2]|0)+d;f=f+d|0}while(0);return f|0}function Ya(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0;f=Z(d,b)|0;if((c[e+76>>2]|0)>-1){g=(Za(e)|0)==0;a=Xa(a,f,e)|0;if(!g)_a(e)}else a=Xa(a,f,e)|0;if((a|0)!=(f|0))d=(a>>>0)/(b>>>0)|0;return d|0}function Za(a){a=a|0;return 0}function _a(a){a=a|0;return}function $a(a){a=a|0;if(!(c[a+68>>2]|0))_a(a);return}function ab(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0;do if(a>>>0<245){o=a>>>0<11?16:a+11&-8;a=o>>>3;j=c[43]|0;b=j>>>a;if(b&3){b=(b&1^1)+a|0;d=212+(b<<1<<2)|0;e=d+8|0;f=c[e>>2]|0;g=f+8|0;h=c[g>>2]|0;do if((d|0)!=(h|0)){if(h>>>0<(c[47]|0)>>>0)na();a=h+12|0;if((c[a>>2]|0)==(f|0)){c[a>>2]=d;c[e>>2]=h;break}else na()}else c[43]=j&~(1<<b);while(0);L=b<<3;c[f+4>>2]=L|3;L=f+L+4|0;c[L>>2]=c[L>>2]|1;L=g;return L|0}h=c[45]|0;if(o>>>0>h>>>0){if(b){d=2<<a;d=b<<a&(d|0-d);d=(d&0-d)+-1|0;i=d>>>12&16;d=d>>>i;f=d>>>5&8;d=d>>>f;g=d>>>2&4;d=d>>>g;e=d>>>1&2;d=d>>>e;b=d>>>1&1;b=(f|i|g|e|b)+(d>>>b)|0;d=212+(b<<1<<2)|0;e=d+8|0;g=c[e>>2]|0;i=g+8|0;f=c[i>>2]|0;do if((d|0)!=(f|0)){if(f>>>0<(c[47]|0)>>>0)na();a=f+12|0;if((c[a>>2]|0)==(g|0)){c[a>>2]=d;c[e>>2]=f;k=c[45]|0;break}else na()}else{c[43]=j&~(1<<b);k=h}while(0);h=(b<<3)-o|0;c[g+4>>2]=o|3;e=g+o|0;c[e+4>>2]=h|1;c[e+h>>2]=h;if(k){f=c[48]|0;b=k>>>3;d=212+(b<<1<<2)|0;a=c[43]|0;b=1<<b;if(a&b){a=d+8|0;b=c[a>>2]|0;if(b>>>0<(c[47]|0)>>>0)na();else{l=a;m=b}}else{c[43]=a|b;l=d+8|0;m=d}c[l>>2]=f;c[m+12>>2]=f;c[f+8>>2]=m;c[f+12>>2]=d}c[45]=h;c[48]=e;L=i;return L|0}a=c[44]|0;if(a){d=(a&0-a)+-1|0;K=d>>>12&16;d=d>>>K;J=d>>>5&8;d=d>>>J;L=d>>>2&4;d=d>>>L;b=d>>>1&2;d=d>>>b;e=d>>>1&1;e=c[476+((J|K|L|b|e)+(d>>>e)<<2)>>2]|0;d=(c[e+4>>2]&-8)-o|0;b=e;while(1){a=c[b+16>>2]|0;if(!a){a=c[b+20>>2]|0;if(!a){j=e;break}}b=(c[a+4>>2]&-8)-o|0;L=b>>>0<d>>>0;d=L?b:d;b=a;e=L?a:e}g=c[47]|0;if(j>>>0<g>>>0)na();i=j+o|0;if(j>>>0>=i>>>0)na();h=c[j+24>>2]|0;e=c[j+12>>2]|0;do if((e|0)==(j|0)){b=j+20|0;a=c[b>>2]|0;if(!a){b=j+16|0;a=c[b>>2]|0;if(!a){n=0;break}}while(1){e=a+20|0;f=c[e>>2]|0;if(f){a=f;b=e;continue}e=a+16|0;f=c[e>>2]|0;if(!f)break;else{a=f;b=e}}if(b>>>0<g>>>0)na();else{c[b>>2]=0;n=a;break}}else{f=c[j+8>>2]|0;if(f>>>0<g>>>0)na();a=f+12|0;if((c[a>>2]|0)!=(j|0))na();b=e+8|0;if((c[b>>2]|0)==(j|0)){c[a>>2]=e;c[b>>2]=f;n=e;break}else na()}while(0);do if(h){a=c[j+28>>2]|0;b=476+(a<<2)|0;if((j|0)==(c[b>>2]|0)){c[b>>2]=n;if(!n){c[44]=c[44]&~(1<<a);break}}else{if(h>>>0<(c[47]|0)>>>0)na();a=h+16|0;if((c[a>>2]|0)==(j|0))c[a>>2]=n;else c[h+20>>2]=n;if(!n)break}b=c[47]|0;if(n>>>0<b>>>0)na();c[n+24>>2]=h;a=c[j+16>>2]|0;do if(a)if(a>>>0<b>>>0)na();else{c[n+16>>2]=a;c[a+24>>2]=n;break}while(0);a=c[j+20>>2]|0;if(a)if(a>>>0<(c[47]|0)>>>0)na();else{c[n+20>>2]=a;c[a+24>>2]=n;break}}while(0);if(d>>>0<16){L=d+o|0;c[j+4>>2]=L|3;L=j+L+4|0;c[L>>2]=c[L>>2]|1}else{c[j+4>>2]=o|3;c[i+4>>2]=d|1;c[i+d>>2]=d;a=c[45]|0;if(a){f=c[48]|0;b=a>>>3;e=212+(b<<1<<2)|0;a=c[43]|0;b=1<<b;if(a&b){a=e+8|0;b=c[a>>2]|0;if(b>>>0<(c[47]|0)>>>0)na();else{p=a;q=b}}else{c[43]=a|b;p=e+8|0;q=e}c[p>>2]=f;c[q+12>>2]=f;c[f+8>>2]=q;c[f+12>>2]=e}c[45]=d;c[48]=i}L=j+8|0;return L|0}}}else if(a>>>0<=4294967231){a=a+11|0;o=a&-8;j=c[44]|0;if(j){d=0-o|0;a=a>>>8;if(a)if(o>>>0>16777215)i=31;else{q=(a+1048320|0)>>>16&8;E=a<<q;p=(E+520192|0)>>>16&4;E=E<<p;i=(E+245760|0)>>>16&2;i=14-(p|q|i)+(E<<i>>>15)|0;i=o>>>(i+7|0)&1|i<<1}else i=0;b=c[476+(i<<2)>>2]|0;a:do if(!b){a=0;b=0;E=86}else{f=d;a=0;g=o<<((i|0)==31?0:25-(i>>>1)|0);h=b;b=0;while(1){e=c[h+4>>2]&-8;d=e-o|0;if(d>>>0<f>>>0)if((e|0)==(o|0)){a=h;b=h;E=90;break a}else b=h;else d=f;e=c[h+20>>2]|0;h=c[h+16+(g>>>31<<2)>>2]|0;a=(e|0)==0|(e|0)==(h|0)?a:e;e=(h|0)==0;if(e){E=86;break}else{f=d;g=g<<(e&1^1)}}}while(0);if((E|0)==86){if((a|0)==0&(b|0)==0){a=2<<i;a=j&(a|0-a);if(!a)break;q=(a&0-a)+-1|0;m=q>>>12&16;q=q>>>m;l=q>>>5&8;q=q>>>l;n=q>>>2&4;q=q>>>n;p=q>>>1&2;q=q>>>p;a=q>>>1&1;a=c[476+((l|m|n|p|a)+(q>>>a)<<2)>>2]|0}if(!a){i=d;j=b}else E=90}if((E|0)==90)while(1){E=0;q=(c[a+4>>2]&-8)-o|0;e=q>>>0<d>>>0;d=e?q:d;b=e?a:b;e=c[a+16>>2]|0;if(e){a=e;E=90;continue}a=c[a+20>>2]|0;if(!a){i=d;j=b;break}else E=90}if((j|0)!=0?i>>>0<((c[45]|0)-o|0)>>>0:0){f=c[47]|0;if(j>>>0<f>>>0)na();h=j+o|0;if(j>>>0>=h>>>0)na();g=c[j+24>>2]|0;d=c[j+12>>2]|0;do if((d|0)==(j|0)){b=j+20|0;a=c[b>>2]|0;if(!a){b=j+16|0;a=c[b>>2]|0;if(!a){s=0;break}}while(1){d=a+20|0;e=c[d>>2]|0;if(e){a=e;b=d;continue}d=a+16|0;e=c[d>>2]|0;if(!e)break;else{a=e;b=d}}if(b>>>0<f>>>0)na();else{c[b>>2]=0;s=a;break}}else{e=c[j+8>>2]|0;if(e>>>0<f>>>0)na();a=e+12|0;if((c[a>>2]|0)!=(j|0))na();b=d+8|0;if((c[b>>2]|0)==(j|0)){c[a>>2]=d;c[b>>2]=e;s=d;break}else na()}while(0);do if(g){a=c[j+28>>2]|0;b=476+(a<<2)|0;if((j|0)==(c[b>>2]|0)){c[b>>2]=s;if(!s){c[44]=c[44]&~(1<<a);break}}else{if(g>>>0<(c[47]|0)>>>0)na();a=g+16|0;if((c[a>>2]|0)==(j|0))c[a>>2]=s;else c[g+20>>2]=s;if(!s)break}b=c[47]|0;if(s>>>0<b>>>0)na();c[s+24>>2]=g;a=c[j+16>>2]|0;do if(a)if(a>>>0<b>>>0)na();else{c[s+16>>2]=a;c[a+24>>2]=s;break}while(0);a=c[j+20>>2]|0;if(a)if(a>>>0<(c[47]|0)>>>0)na();else{c[s+20>>2]=a;c[a+24>>2]=s;break}}while(0);do if(i>>>0>=16){c[j+4>>2]=o|3;c[h+4>>2]=i|1;c[h+i>>2]=i;a=i>>>3;if(i>>>0<256){d=212+(a<<1<<2)|0;b=c[43]|0;a=1<<a;if(b&a){a=d+8|0;b=c[a>>2]|0;if(b>>>0<(c[47]|0)>>>0)na();else{u=a;v=b}}else{c[43]=b|a;u=d+8|0;v=d}c[u>>2]=h;c[v+12>>2]=h;c[h+8>>2]=v;c[h+12>>2]=d;break}a=i>>>8;if(a)if(i>>>0>16777215)d=31;else{K=(a+1048320|0)>>>16&8;L=a<<K;J=(L+520192|0)>>>16&4;L=L<<J;d=(L+245760|0)>>>16&2;d=14-(J|K|d)+(L<<d>>>15)|0;d=i>>>(d+7|0)&1|d<<1}else d=0;e=476+(d<<2)|0;c[h+28>>2]=d;a=h+16|0;c[a+4>>2]=0;c[a>>2]=0;a=c[44]|0;b=1<<d;if(!(a&b)){c[44]=a|b;c[e>>2]=h;c[h+24>>2]=e;c[h+12>>2]=h;c[h+8>>2]=h;break}f=i<<((d|0)==31?0:25-(d>>>1)|0);a=c[e>>2]|0;while(1){if((c[a+4>>2]&-8|0)==(i|0)){d=a;E=148;break}b=a+16+(f>>>31<<2)|0;d=c[b>>2]|0;if(!d){E=145;break}else{f=f<<1;a=d}}if((E|0)==145)if(b>>>0<(c[47]|0)>>>0)na();else{c[b>>2]=h;c[h+24>>2]=a;c[h+12>>2]=h;c[h+8>>2]=h;break}else if((E|0)==148){a=d+8|0;b=c[a>>2]|0;L=c[47]|0;if(b>>>0>=L>>>0&d>>>0>=L>>>0){c[b+12>>2]=h;c[a>>2]=h;c[h+8>>2]=b;c[h+12>>2]=d;c[h+24>>2]=0;break}else na()}}else{L=i+o|0;c[j+4>>2]=L|3;L=j+L+4|0;c[L>>2]=c[L>>2]|1}while(0);L=j+8|0;return L|0}}}else o=-1;while(0);d=c[45]|0;if(d>>>0>=o>>>0){a=d-o|0;b=c[48]|0;if(a>>>0>15){L=b+o|0;c[48]=L;c[45]=a;c[L+4>>2]=a|1;c[L+a>>2]=a;c[b+4>>2]=o|3}else{c[45]=0;c[48]=0;c[b+4>>2]=d|3;L=b+d+4|0;c[L>>2]=c[L>>2]|1}L=b+8|0;return L|0}a=c[46]|0;if(a>>>0>o>>>0){J=a-o|0;c[46]=J;L=c[49]|0;K=L+o|0;c[49]=K;c[K+4>>2]=J|1;c[L+4>>2]=o|3;L=L+8|0;return L|0}do if(!(c[161]|0)){a=ja(30)|0;if(!(a+-1&a)){c[163]=a;c[162]=a;c[164]=-1;c[165]=-1;c[166]=0;c[154]=0;c[161]=(pa(0)|0)&-16^1431655768;break}else na()}while(0);h=o+48|0;g=c[163]|0;i=o+47|0;f=g+i|0;g=0-g|0;j=f&g;if(j>>>0<=o>>>0){L=0;return L|0}a=c[153]|0;if((a|0)!=0?(u=c[151]|0,v=u+j|0,v>>>0<=u>>>0|v>>>0>a>>>0):0){L=0;return L|0}b:do if(!(c[154]&4)){a=c[49]|0;c:do if(a){d=620;while(1){b=c[d>>2]|0;if(b>>>0<=a>>>0?(r=d+4|0,(b+(c[r>>2]|0)|0)>>>0>a>>>0):0){e=d;d=r;break}d=c[d+8>>2]|0;if(!d){E=173;break c}}a=f-(c[46]|0)&g;if(a>>>0<2147483647){b=oa(a|0)|0;if((b|0)==((c[e>>2]|0)+(c[d>>2]|0)|0)){if((b|0)!=(-1|0)){h=b;f=a;E=193;break b}}else E=183}}else E=173;while(0);do if((E|0)==173?(t=oa(0)|0,(t|0)!=(-1|0)):0){a=t;b=c[162]|0;d=b+-1|0;if(!(d&a))a=j;else a=j-a+(d+a&0-b)|0;b=c[151]|0;d=b+a|0;if(a>>>0>o>>>0&a>>>0<2147483647){v=c[153]|0;if((v|0)!=0?d>>>0<=b>>>0|d>>>0>v>>>0:0)break;b=oa(a|0)|0;if((b|0)==(t|0)){h=t;f=a;E=193;break b}else E=183}}while(0);d:do if((E|0)==183){d=0-a|0;do if(h>>>0>a>>>0&(a>>>0<2147483647&(b|0)!=(-1|0))?(w=c[163]|0,w=i-a+w&0-w,w>>>0<2147483647):0)if((oa(w|0)|0)==(-1|0)){oa(d|0)|0;break d}else{a=w+a|0;break}while(0);if((b|0)!=(-1|0)){h=b;f=a;E=193;break b}}while(0);c[154]=c[154]|4;E=190}else E=190;while(0);if((((E|0)==190?j>>>0<2147483647:0)?(x=oa(j|0)|0,y=oa(0)|0,x>>>0<y>>>0&((x|0)!=(-1|0)&(y|0)!=(-1|0))):0)?(z=y-x|0,z>>>0>(o+40|0)>>>0):0){h=x;f=z;E=193}if((E|0)==193){a=(c[151]|0)+f|0;c[151]=a;if(a>>>0>(c[152]|0)>>>0)c[152]=a;i=c[49]|0;do if(i){e=620;do{a=c[e>>2]|0;b=e+4|0;d=c[b>>2]|0;if((h|0)==(a+d|0)){A=a;B=b;C=d;D=e;E=203;break}e=c[e+8>>2]|0}while((e|0)!=0);if(((E|0)==203?(c[D+12>>2]&8|0)==0:0)?i>>>0<h>>>0&i>>>0>=A>>>0:0){c[B>>2]=C+f;L=i+8|0;L=(L&7|0)==0?0:0-L&7;K=i+L|0;L=f-L+(c[46]|0)|0;c[49]=K;c[46]=L;c[K+4>>2]=L|1;c[K+L+4>>2]=40;c[50]=c[165];break}a=c[47]|0;if(h>>>0<a>>>0){c[47]=h;j=h}else j=a;d=h+f|0;a=620;while(1){if((c[a>>2]|0)==(d|0)){b=a;E=211;break}a=c[a+8>>2]|0;if(!a){b=620;break}}if((E|0)==211)if(!(c[a+12>>2]&8)){c[b>>2]=h;l=a+4|0;c[l>>2]=(c[l>>2]|0)+f;l=h+8|0;l=h+((l&7|0)==0?0:0-l&7)|0;a=d+8|0;a=d+((a&7|0)==0?0:0-a&7)|0;k=l+o|0;g=a-l-o|0;c[l+4>>2]=o|3;do if((a|0)!=(i|0)){if((a|0)==(c[48]|0)){L=(c[45]|0)+g|0;c[45]=L;c[48]=k;c[k+4>>2]=L|1;c[k+L>>2]=L;break}b=c[a+4>>2]|0;if((b&3|0)==1){i=b&-8;f=b>>>3;e:do if(b>>>0>=256){h=c[a+24>>2]|0;e=c[a+12>>2]|0;do if((e|0)==(a|0)){d=a+16|0;e=d+4|0;b=c[e>>2]|0;if(!b){b=c[d>>2]|0;if(!b){J=0;break}}else d=e;while(1){e=b+20|0;f=c[e>>2]|0;if(f){b=f;d=e;continue}e=b+16|0;f=c[e>>2]|0;if(!f)break;else{b=f;d=e}}if(d>>>0<j>>>0)na();else{c[d>>2]=0;J=b;break}}else{f=c[a+8>>2]|0;if(f>>>0<j>>>0)na();b=f+12|0;if((c[b>>2]|0)!=(a|0))na();d=e+8|0;if((c[d>>2]|0)==(a|0)){c[b>>2]=e;c[d>>2]=f;J=e;break}else na()}while(0);if(!h)break;b=c[a+28>>2]|0;d=476+(b<<2)|0;do if((a|0)!=(c[d>>2]|0)){if(h>>>0<(c[47]|0)>>>0)na();b=h+16|0;if((c[b>>2]|0)==(a|0))c[b>>2]=J;else c[h+20>>2]=J;if(!J)break e}else{c[d>>2]=J;if(J)break;c[44]=c[44]&~(1<<b);break e}while(0);e=c[47]|0;if(J>>>0<e>>>0)na();c[J+24>>2]=h;b=a+16|0;d=c[b>>2]|0;do if(d)if(d>>>0<e>>>0)na();else{c[J+16>>2]=d;c[d+24>>2]=J;break}while(0);b=c[b+4>>2]|0;if(!b)break;if(b>>>0<(c[47]|0)>>>0)na();else{c[J+20>>2]=b;c[b+24>>2]=J;break}}else{d=c[a+8>>2]|0;e=c[a+12>>2]|0;b=212+(f<<1<<2)|0;do if((d|0)!=(b|0)){if(d>>>0<j>>>0)na();if((c[d+12>>2]|0)==(a|0))break;na()}while(0);if((e|0)==(d|0)){c[43]=c[43]&~(1<<f);break}do if((e|0)==(b|0))G=e+8|0;else{if(e>>>0<j>>>0)na();b=e+8|0;if((c[b>>2]|0)==(a|0)){G=b;break}na()}while(0);c[d+12>>2]=e;c[G>>2]=d}while(0);a=a+i|0;g=i+g|0}a=a+4|0;c[a>>2]=c[a>>2]&-2;c[k+4>>2]=g|1;c[k+g>>2]=g;a=g>>>3;if(g>>>0<256){d=212+(a<<1<<2)|0;b=c[43]|0;a=1<<a;do if(!(b&a)){c[43]=b|a;K=d+8|0;L=d}else{a=d+8|0;b=c[a>>2]|0;if(b>>>0>=(c[47]|0)>>>0){K=a;L=b;break}na()}while(0);c[K>>2]=k;c[L+12>>2]=k;c[k+8>>2]=L;c[k+12>>2]=d;break}a=g>>>8;do if(!a)d=0;else{if(g>>>0>16777215){d=31;break}K=(a+1048320|0)>>>16&8;L=a<<K;J=(L+520192|0)>>>16&4;L=L<<J;d=(L+245760|0)>>>16&2;d=14-(J|K|d)+(L<<d>>>15)|0;d=g>>>(d+7|0)&1|d<<1}while(0);e=476+(d<<2)|0;c[k+28>>2]=d;a=k+16|0;c[a+4>>2]=0;c[a>>2]=0;a=c[44]|0;b=1<<d;if(!(a&b)){c[44]=a|b;c[e>>2]=k;c[k+24>>2]=e;c[k+12>>2]=k;c[k+8>>2]=k;break}f=g<<((d|0)==31?0:25-(d>>>1)|0);a=c[e>>2]|0;while(1){if((c[a+4>>2]&-8|0)==(g|0)){d=a;E=281;break}b=a+16+(f>>>31<<2)|0;d=c[b>>2]|0;if(!d){E=278;break}else{f=f<<1;a=d}}if((E|0)==278)if(b>>>0<(c[47]|0)>>>0)na();else{c[b>>2]=k;c[k+24>>2]=a;c[k+12>>2]=k;c[k+8>>2]=k;break}else if((E|0)==281){a=d+8|0;b=c[a>>2]|0;L=c[47]|0;if(b>>>0>=L>>>0&d>>>0>=L>>>0){c[b+12>>2]=k;c[a>>2]=k;c[k+8>>2]=b;c[k+12>>2]=d;c[k+24>>2]=0;break}else na()}}else{L=(c[46]|0)+g|0;c[46]=L;c[49]=k;c[k+4>>2]=L|1}while(0);L=l+8|0;return L|0}else b=620;while(1){a=c[b>>2]|0;if(a>>>0<=i>>>0?(F=a+(c[b+4>>2]|0)|0,F>>>0>i>>>0):0){b=F;break}b=c[b+8>>2]|0}g=b+-47|0;d=g+8|0;d=g+((d&7|0)==0?0:0-d&7)|0;g=i+16|0;d=d>>>0<g>>>0?i:d;a=d+8|0;e=h+8|0;e=(e&7|0)==0?0:0-e&7;L=h+e|0;e=f+-40-e|0;c[49]=L;c[46]=e;c[L+4>>2]=e|1;c[L+e+4>>2]=40;c[50]=c[165];e=d+4|0;c[e>>2]=27;c[a>>2]=c[155];c[a+4>>2]=c[156];c[a+8>>2]=c[157];c[a+12>>2]=c[158];c[155]=h;c[156]=f;c[158]=0;c[157]=a;a=d+24|0;do{a=a+4|0;c[a>>2]=7}while((a+4|0)>>>0<b>>>0);if((d|0)!=(i|0)){h=d-i|0;c[e>>2]=c[e>>2]&-2;c[i+4>>2]=h|1;c[d>>2]=h;a=h>>>3;if(h>>>0<256){d=212+(a<<1<<2)|0;b=c[43]|0;a=1<<a;if(b&a){a=d+8|0;b=c[a>>2]|0;if(b>>>0<(c[47]|0)>>>0)na();else{H=a;I=b}}else{c[43]=b|a;H=d+8|0;I=d}c[H>>2]=i;c[I+12>>2]=i;c[i+8>>2]=I;c[i+12>>2]=d;break}a=h>>>8;if(a)if(h>>>0>16777215)d=31;else{K=(a+1048320|0)>>>16&8;L=a<<K;J=(L+520192|0)>>>16&4;L=L<<J;d=(L+245760|0)>>>16&2;d=14-(J|K|d)+(L<<d>>>15)|0;d=h>>>(d+7|0)&1|d<<1}else d=0;f=476+(d<<2)|0;c[i+28>>2]=d;c[i+20>>2]=0;c[g>>2]=0;a=c[44]|0;b=1<<d;if(!(a&b)){c[44]=a|b;c[f>>2]=i;c[i+24>>2]=f;c[i+12>>2]=i;c[i+8>>2]=i;break}e=h<<((d|0)==31?0:25-(d>>>1)|0);a=c[f>>2]|0;while(1){if((c[a+4>>2]&-8|0)==(h|0)){d=a;E=307;break}b=a+16+(e>>>31<<2)|0;d=c[b>>2]|0;if(!d){E=304;break}else{e=e<<1;a=d}}if((E|0)==304)if(b>>>0<(c[47]|0)>>>0)na();else{c[b>>2]=i;c[i+24>>2]=a;c[i+12>>2]=i;c[i+8>>2]=i;break}else if((E|0)==307){a=d+8|0;b=c[a>>2]|0;L=c[47]|0;if(b>>>0>=L>>>0&d>>>0>=L>>>0){c[b+12>>2]=i;c[a>>2]=i;c[i+8>>2]=b;c[i+12>>2]=d;c[i+24>>2]=0;break}else na()}}}else{L=c[47]|0;if((L|0)==0|h>>>0<L>>>0)c[47]=h;c[155]=h;c[156]=f;c[158]=0;c[52]=c[161];c[51]=-1;a=0;do{L=212+(a<<1<<2)|0;c[L+12>>2]=L;c[L+8>>2]=L;a=a+1|0}while((a|0)!=32);L=h+8|0;L=(L&7|0)==0?0:0-L&7;K=h+L|0;L=f+-40-L|0;c[49]=K;c[46]=L;c[K+4>>2]=L|1;c[K+L+4>>2]=40;c[50]=c[165]}while(0);a=c[46]|0;if(a>>>0>o>>>0){J=a-o|0;c[46]=J;L=c[49]|0;K=L+o|0;c[49]=K;c[K+4>>2]=J|1;c[L+4>>2]=o|3;L=L+8|0;return L|0}}c[(Ra()|0)>>2]=12;L=0;return L|0}function bb(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;if(!a)return;d=a+-8|0;h=c[47]|0;if(d>>>0<h>>>0)na();a=c[a+-4>>2]|0;b=a&3;if((b|0)==1)na();e=a&-8;m=d+e|0;do if(!(a&1)){a=c[d>>2]|0;if(!b)return;k=d+(0-a)|0;j=a+e|0;if(k>>>0<h>>>0)na();if((k|0)==(c[48]|0)){a=m+4|0;b=c[a>>2]|0;if((b&3|0)!=3){q=k;g=j;break}c[45]=j;c[a>>2]=b&-2;c[k+4>>2]=j|1;c[k+j>>2]=j;return}e=a>>>3;if(a>>>0<256){b=c[k+8>>2]|0;d=c[k+12>>2]|0;a=212+(e<<1<<2)|0;if((b|0)!=(a|0)){if(b>>>0<h>>>0)na();if((c[b+12>>2]|0)!=(k|0))na()}if((d|0)==(b|0)){c[43]=c[43]&~(1<<e);q=k;g=j;break}if((d|0)!=(a|0)){if(d>>>0<h>>>0)na();a=d+8|0;if((c[a>>2]|0)==(k|0))f=a;else na()}else f=d+8|0;c[b+12>>2]=d;c[f>>2]=b;q=k;g=j;break}f=c[k+24>>2]|0;d=c[k+12>>2]|0;do if((d|0)==(k|0)){b=k+16|0;d=b+4|0;a=c[d>>2]|0;if(!a){a=c[b>>2]|0;if(!a){i=0;break}}else b=d;while(1){d=a+20|0;e=c[d>>2]|0;if(e){a=e;b=d;continue}d=a+16|0;e=c[d>>2]|0;if(!e)break;else{a=e;b=d}}if(b>>>0<h>>>0)na();else{c[b>>2]=0;i=a;break}}else{e=c[k+8>>2]|0;if(e>>>0<h>>>0)na();a=e+12|0;if((c[a>>2]|0)!=(k|0))na();b=d+8|0;if((c[b>>2]|0)==(k|0)){c[a>>2]=d;c[b>>2]=e;i=d;break}else na()}while(0);if(f){a=c[k+28>>2]|0;b=476+(a<<2)|0;if((k|0)==(c[b>>2]|0)){c[b>>2]=i;if(!i){c[44]=c[44]&~(1<<a);q=k;g=j;break}}else{if(f>>>0<(c[47]|0)>>>0)na();a=f+16|0;if((c[a>>2]|0)==(k|0))c[a>>2]=i;else c[f+20>>2]=i;if(!i){q=k;g=j;break}}d=c[47]|0;if(i>>>0<d>>>0)na();c[i+24>>2]=f;a=k+16|0;b=c[a>>2]|0;do if(b)if(b>>>0<d>>>0)na();else{c[i+16>>2]=b;c[b+24>>2]=i;break}while(0);a=c[a+4>>2]|0;if(a)if(a>>>0<(c[47]|0)>>>0)na();else{c[i+20>>2]=a;c[a+24>>2]=i;q=k;g=j;break}else{q=k;g=j}}else{q=k;g=j}}else{q=d;g=e}while(0);if(q>>>0>=m>>>0)na();a=m+4|0;b=c[a>>2]|0;if(!(b&1))na();if(!(b&2)){if((m|0)==(c[49]|0)){p=(c[46]|0)+g|0;c[46]=p;c[49]=q;c[q+4>>2]=p|1;if((q|0)!=(c[48]|0))return;c[48]=0;c[45]=0;return}if((m|0)==(c[48]|0)){p=(c[45]|0)+g|0;c[45]=p;c[48]=q;c[q+4>>2]=p|1;c[q+p>>2]=p;return}g=(b&-8)+g|0;e=b>>>3;do if(b>>>0>=256){f=c[m+24>>2]|0;a=c[m+12>>2]|0;do if((a|0)==(m|0)){b=m+16|0;d=b+4|0;a=c[d>>2]|0;if(!a){a=c[b>>2]|0;if(!a){n=0;break}}else b=d;while(1){d=a+20|0;e=c[d>>2]|0;if(e){a=e;b=d;continue}d=a+16|0;e=c[d>>2]|0;if(!e)break;else{a=e;b=d}}if(b>>>0<(c[47]|0)>>>0)na();else{c[b>>2]=0;n=a;break}}else{b=c[m+8>>2]|0;if(b>>>0<(c[47]|0)>>>0)na();d=b+12|0;if((c[d>>2]|0)!=(m|0))na();e=a+8|0;if((c[e>>2]|0)==(m|0)){c[d>>2]=a;c[e>>2]=b;n=a;break}else na()}while(0);if(f){a=c[m+28>>2]|0;b=476+(a<<2)|0;if((m|0)==(c[b>>2]|0)){c[b>>2]=n;if(!n){c[44]=c[44]&~(1<<a);break}}else{if(f>>>0<(c[47]|0)>>>0)na();a=f+16|0;if((c[a>>2]|0)==(m|0))c[a>>2]=n;else c[f+20>>2]=n;if(!n)break}d=c[47]|0;if(n>>>0<d>>>0)na();c[n+24>>2]=f;a=m+16|0;b=c[a>>2]|0;do if(b)if(b>>>0<d>>>0)na();else{c[n+16>>2]=b;c[b+24>>2]=n;break}while(0);a=c[a+4>>2]|0;if(a)if(a>>>0<(c[47]|0)>>>0)na();else{c[n+20>>2]=a;c[a+24>>2]=n;break}}}else{b=c[m+8>>2]|0;d=c[m+12>>2]|0;a=212+(e<<1<<2)|0;if((b|0)!=(a|0)){if(b>>>0<(c[47]|0)>>>0)na();if((c[b+12>>2]|0)!=(m|0))na()}if((d|0)==(b|0)){c[43]=c[43]&~(1<<e);break}if((d|0)!=(a|0)){if(d>>>0<(c[47]|0)>>>0)na();a=d+8|0;if((c[a>>2]|0)==(m|0))l=a;else na()}else l=d+8|0;c[b+12>>2]=d;c[l>>2]=b}while(0);c[q+4>>2]=g|1;c[q+g>>2]=g;if((q|0)==(c[48]|0)){c[45]=g;return}}else{c[a>>2]=b&-2;c[q+4>>2]=g|1;c[q+g>>2]=g}a=g>>>3;if(g>>>0<256){d=212+(a<<1<<2)|0;b=c[43]|0;a=1<<a;if(b&a){a=d+8|0;b=c[a>>2]|0;if(b>>>0<(c[47]|0)>>>0)na();else{o=a;p=b}}else{c[43]=b|a;o=d+8|0;p=d}c[o>>2]=q;c[p+12>>2]=q;c[q+8>>2]=p;c[q+12>>2]=d;return}a=g>>>8;if(a)if(g>>>0>16777215)d=31;else{o=(a+1048320|0)>>>16&8;p=a<<o;n=(p+520192|0)>>>16&4;p=p<<n;d=(p+245760|0)>>>16&2;d=14-(n|o|d)+(p<<d>>>15)|0;d=g>>>(d+7|0)&1|d<<1}else d=0;e=476+(d<<2)|0;c[q+28>>2]=d;c[q+20>>2]=0;c[q+16>>2]=0;a=c[44]|0;b=1<<d;do if(a&b){f=g<<((d|0)==31?0:25-(d>>>1)|0);a=c[e>>2]|0;while(1){if((c[a+4>>2]&-8|0)==(g|0)){d=a;e=130;break}b=a+16+(f>>>31<<2)|0;d=c[b>>2]|0;if(!d){e=127;break}else{f=f<<1;a=d}}if((e|0)==127)if(b>>>0<(c[47]|0)>>>0)na();else{c[b>>2]=q;c[q+24>>2]=a;c[q+12>>2]=q;c[q+8>>2]=q;break}else if((e|0)==130){a=d+8|0;b=c[a>>2]|0;p=c[47]|0;if(b>>>0>=p>>>0&d>>>0>=p>>>0){c[b+12>>2]=q;c[a>>2]=q;c[q+8>>2]=b;c[q+12>>2]=d;c[q+24>>2]=0;break}else na()}}else{c[44]=a|b;c[e>>2]=q;c[q+24>>2]=e;c[q+12>>2]=q;c[q+8>>2]=q}while(0);q=(c[51]|0)+-1|0;c[51]=q;if(!q)a=628;else return;while(1){a=c[a>>2]|0;if(!a)break;else a=a+8|0}c[51]=-1;return}function cb(){}function db(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0;f=b+e|0;if((e|0)>=20){d=d&255;h=b&3;i=d|d<<8|d<<16|d<<24;g=f&~3;if(h){h=b+4-h|0;while((b|0)<(h|0)){a[b>>0]=d;b=b+1|0}}while((b|0)<(g|0)){c[b>>2]=i;b=b+4|0}}while((b|0)<(f|0)){a[b>>0]=d;b=b+1|0}return b-e|0}function eb(b,d,e){b=b|0;d=d|0;e=e|0;var f=0;if((e|0)>=4096)return ra(b|0,d|0,e|0)|0;f=b|0;if((b&3)==(d&3)){while(b&3){if(!e)return f|0;a[b>>0]=a[d>>0]|0;b=b+1|0;d=d+1|0;e=e-1|0}while((e|0)>=4){c[b>>2]=c[d>>2];b=b+4|0;d=d+4|0;e=e-4|0}}while((e|0)>0){a[b>>0]=a[d>>0]|0;b=b+1|0;d=d+1|0;e=e-1|0}return f|0}function fb(a,b){a=a|0;b=b|0;return ya[a&1](b|0)|0}function gb(a,b,c,d){a=a|0;b=b|0;c=c|0;d=d|0;return za[a&3](b|0,c|0,d|0)|0}function hb(a,b){a=a|0;b=b|0;Aa[a&1](b|0)}function ib(a){a=a|0;aa(0);return 0}function jb(a,b,c){a=a|0;b=b|0;c=c|0;aa(1);return 0}function kb(a){a=a|0;aa(2)}
 
 // EMSCRIPTEN_END_FUNCS
 var ya=[ib,Ua];var za=[jb,Ta,Wa,jb];var Aa=[kb,$a];return{_malloc:ab,_free:bb,_kiss_fftr_alloc:Oa,_kiss_fftr_free:Na,_kiss_fftri:Qa,_memset:db,_kiss_fftr:Pa,_memcpy:eb,runPostSets:cb,stackAlloc:Ba,stackSave:Ca,stackRestore:Da,establishStackSpace:Ea,setThrew:Fa,setTempRet0:Ia,getTempRet0:Ja,dynCall_ii:fb,dynCall_iiii:gb,dynCall_vi:hb}})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/kissfft/_kiss_fft_guts.h	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,164 @@
+/*
+Copyright (c) 2003-2010, Mark Borgerding
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+    * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+/* kiss_fft.h
+   defines kiss_fft_scalar as either short or a float type
+   and defines
+   typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */
+#include "kiss_fft.h"
+#include <limits.h>
+
+#define MAXFACTORS 32
+/* e.g. an fft of length 128 has 4 factors 
+ as far as kissfft is concerned
+ 4*4*4*2
+ */
+
+struct kiss_fft_state{
+    int nfft;
+    int inverse;
+    int factors[2*MAXFACTORS];
+    kiss_fft_cpx twiddles[1];
+};
+
+/*
+  Explanation of macros dealing with complex math:
+
+   C_MUL(m,a,b)         : m = a*b
+   C_FIXDIV( c , div )  : if a fixed point impl., c /= div. noop otherwise
+   C_SUB( res, a,b)     : res = a - b
+   C_SUBFROM( res , a)  : res -= a
+   C_ADDTO( res , a)    : res += a
+ * */
+#ifdef FIXED_POINT
+#if (FIXED_POINT==32)
+# define FRACBITS 31
+# define SAMPPROD int64_t
+#define SAMP_MAX 2147483647
+#else
+# define FRACBITS 15
+# define SAMPPROD int32_t 
+#define SAMP_MAX 32767
+#endif
+
+#define SAMP_MIN -SAMP_MAX
+
+#if defined(CHECK_OVERFLOW)
+#  define CHECK_OVERFLOW_OP(a,op,b)  \
+	if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \
+		fprintf(stderr,"WARNING:overflow @ " __FILE__ "(%d): (%d " #op" %d) = %ld\n",__LINE__,(a),(b),(SAMPPROD)(a) op (SAMPPROD)(b) );  }
+#endif
+
+
+#   define smul(a,b) ( (SAMPPROD)(a)*(b) )
+#   define sround( x )  (kiss_fft_scalar)( ( (x) + (1<<(FRACBITS-1)) ) >> FRACBITS )
+
+#   define S_MUL(a,b) sround( smul(a,b) )
+
+#   define C_MUL(m,a,b) \
+      do{ (m).r = sround( smul((a).r,(b).r) - smul((a).i,(b).i) ); \
+          (m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0)
+
+#   define DIVSCALAR(x,k) \
+	(x) = sround( smul(  x, SAMP_MAX/k ) )
+
+#   define C_FIXDIV(c,div) \
+	do {    DIVSCALAR( (c).r , div);  \
+		DIVSCALAR( (c).i  , div); }while (0)
+
+#   define C_MULBYSCALAR( c, s ) \
+    do{ (c).r =  sround( smul( (c).r , s ) ) ;\
+        (c).i =  sround( smul( (c).i , s ) ) ; }while(0)
+
+#else  /* not FIXED_POINT*/
+
+#   define S_MUL(a,b) ( (a)*(b) )
+#define C_MUL(m,a,b) \
+    do{ (m).r = (a).r*(b).r - (a).i*(b).i;\
+        (m).i = (a).r*(b).i + (a).i*(b).r; }while(0)
+#   define C_FIXDIV(c,div) /* NOOP */
+#   define C_MULBYSCALAR( c, s ) \
+    do{ (c).r *= (s);\
+        (c).i *= (s); }while(0)
+#endif
+
+#ifndef CHECK_OVERFLOW_OP
+#  define CHECK_OVERFLOW_OP(a,op,b) /* noop */
+#endif
+
+#define  C_ADD( res, a,b)\
+    do { \
+	    CHECK_OVERFLOW_OP((a).r,+,(b).r)\
+	    CHECK_OVERFLOW_OP((a).i,+,(b).i)\
+	    (res).r=(a).r+(b).r;  (res).i=(a).i+(b).i; \
+    }while(0)
+#define  C_SUB( res, a,b)\
+    do { \
+	    CHECK_OVERFLOW_OP((a).r,-,(b).r)\
+	    CHECK_OVERFLOW_OP((a).i,-,(b).i)\
+	    (res).r=(a).r-(b).r;  (res).i=(a).i-(b).i; \
+    }while(0)
+#define C_ADDTO( res , a)\
+    do { \
+	    CHECK_OVERFLOW_OP((res).r,+,(a).r)\
+	    CHECK_OVERFLOW_OP((res).i,+,(a).i)\
+	    (res).r += (a).r;  (res).i += (a).i;\
+    }while(0)
+
+#define C_SUBFROM( res , a)\
+    do {\
+	    CHECK_OVERFLOW_OP((res).r,-,(a).r)\
+	    CHECK_OVERFLOW_OP((res).i,-,(a).i)\
+	    (res).r -= (a).r;  (res).i -= (a).i; \
+    }while(0)
+
+
+#ifdef FIXED_POINT
+#  define KISS_FFT_COS(phase)  floor(.5+SAMP_MAX * cos (phase))
+#  define KISS_FFT_SIN(phase)  floor(.5+SAMP_MAX * sin (phase))
+#  define HALF_OF(x) ((x)>>1)
+#elif defined(USE_SIMD)
+#  define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) )
+#  define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) )
+#  define HALF_OF(x) ((x)*_mm_set1_ps(.5))
+#else
+#  define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase)
+#  define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase)
+#  define HALF_OF(x) ((x)*.5)
+#endif
+
+#define  kf_cexp(x,phase) \
+	do{ \
+		(x)->r = KISS_FFT_COS(phase);\
+		(x)->i = KISS_FFT_SIN(phase);\
+	}while(0)
+
+
+/* a debugging function */
+#define pcpx(c)\
+    fprintf(stderr,"%g + %gi\n",(double)((c)->r),(double)((c)->i) )
+
+
+#ifdef KISS_FFT_USE_ALLOCA
+// define this to allow use of alloca instead of malloc for temporary buffers
+// Temporary buffers are used in two case: 
+// 1. FFT sizes that have "bad" factors. i.e. not 2,3 and 5
+// 2. "in-place" FFTs.  Notice the quotes, since kissfft does not really do an in-place transform.
+#include <alloca.h>
+#define  KISS_FFT_TMP_ALLOC(nbytes) alloca(nbytes)
+#define  KISS_FFT_TMP_FREE(ptr) 
+#else
+#define  KISS_FFT_TMP_ALLOC(nbytes) KISS_FFT_MALLOC(nbytes)
+#define  KISS_FFT_TMP_FREE(ptr) KISS_FFT_FREE(ptr)
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/kissfft/kiss_fft.c	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,408 @@
+/*
+Copyright (c) 2003-2010, Mark Borgerding
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+    * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+
+#include "_kiss_fft_guts.h"
+/* The guts header contains all the multiplication and addition macros that are defined for
+ fixed or floating point complex numbers.  It also delares the kf_ internal functions.
+ */
+
+static void kf_bfly2(
+        kiss_fft_cpx * Fout,
+        const size_t fstride,
+        const kiss_fft_cfg st,
+        int m
+        )
+{
+    kiss_fft_cpx * Fout2;
+    kiss_fft_cpx * tw1 = st->twiddles;
+    kiss_fft_cpx t;
+    Fout2 = Fout + m;
+    do{
+        C_FIXDIV(*Fout,2); C_FIXDIV(*Fout2,2);
+
+        C_MUL (t,  *Fout2 , *tw1);
+        tw1 += fstride;
+        C_SUB( *Fout2 ,  *Fout , t );
+        C_ADDTO( *Fout ,  t );
+        ++Fout2;
+        ++Fout;
+    }while (--m);
+}
+
+static void kf_bfly4(
+        kiss_fft_cpx * Fout,
+        const size_t fstride,
+        const kiss_fft_cfg st,
+        const size_t m
+        )
+{
+    kiss_fft_cpx *tw1,*tw2,*tw3;
+    kiss_fft_cpx scratch[6];
+    size_t k=m;
+    const size_t m2=2*m;
+    const size_t m3=3*m;
+
+
+    tw3 = tw2 = tw1 = st->twiddles;
+
+    do {
+        C_FIXDIV(*Fout,4); C_FIXDIV(Fout[m],4); C_FIXDIV(Fout[m2],4); C_FIXDIV(Fout[m3],4);
+
+        C_MUL(scratch[0],Fout[m] , *tw1 );
+        C_MUL(scratch[1],Fout[m2] , *tw2 );
+        C_MUL(scratch[2],Fout[m3] , *tw3 );
+
+        C_SUB( scratch[5] , *Fout, scratch[1] );
+        C_ADDTO(*Fout, scratch[1]);
+        C_ADD( scratch[3] , scratch[0] , scratch[2] );
+        C_SUB( scratch[4] , scratch[0] , scratch[2] );
+        C_SUB( Fout[m2], *Fout, scratch[3] );
+        tw1 += fstride;
+        tw2 += fstride*2;
+        tw3 += fstride*3;
+        C_ADDTO( *Fout , scratch[3] );
+
+        if(st->inverse) {
+            Fout[m].r = scratch[5].r - scratch[4].i;
+            Fout[m].i = scratch[5].i + scratch[4].r;
+            Fout[m3].r = scratch[5].r + scratch[4].i;
+            Fout[m3].i = scratch[5].i - scratch[4].r;
+        }else{
+            Fout[m].r = scratch[5].r + scratch[4].i;
+            Fout[m].i = scratch[5].i - scratch[4].r;
+            Fout[m3].r = scratch[5].r - scratch[4].i;
+            Fout[m3].i = scratch[5].i + scratch[4].r;
+        }
+        ++Fout;
+    }while(--k);
+}
+
+static void kf_bfly3(
+         kiss_fft_cpx * Fout,
+         const size_t fstride,
+         const kiss_fft_cfg st,
+         size_t m
+         )
+{
+     size_t k=m;
+     const size_t m2 = 2*m;
+     kiss_fft_cpx *tw1,*tw2;
+     kiss_fft_cpx scratch[5];
+     kiss_fft_cpx epi3;
+     epi3 = st->twiddles[fstride*m];
+
+     tw1=tw2=st->twiddles;
+
+     do{
+         C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3);
+
+         C_MUL(scratch[1],Fout[m] , *tw1);
+         C_MUL(scratch[2],Fout[m2] , *tw2);
+
+         C_ADD(scratch[3],scratch[1],scratch[2]);
+         C_SUB(scratch[0],scratch[1],scratch[2]);
+         tw1 += fstride;
+         tw2 += fstride*2;
+
+         Fout[m].r = Fout->r - HALF_OF(scratch[3].r);
+         Fout[m].i = Fout->i - HALF_OF(scratch[3].i);
+
+         C_MULBYSCALAR( scratch[0] , epi3.i );
+
+         C_ADDTO(*Fout,scratch[3]);
+
+         Fout[m2].r = Fout[m].r + scratch[0].i;
+         Fout[m2].i = Fout[m].i - scratch[0].r;
+
+         Fout[m].r -= scratch[0].i;
+         Fout[m].i += scratch[0].r;
+
+         ++Fout;
+     }while(--k);
+}
+
+static void kf_bfly5(
+        kiss_fft_cpx * Fout,
+        const size_t fstride,
+        const kiss_fft_cfg st,
+        int m
+        )
+{
+    kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4;
+    int u;
+    kiss_fft_cpx scratch[13];
+    kiss_fft_cpx * twiddles = st->twiddles;
+    kiss_fft_cpx *tw;
+    kiss_fft_cpx ya,yb;
+    ya = twiddles[fstride*m];
+    yb = twiddles[fstride*2*m];
+
+    Fout0=Fout;
+    Fout1=Fout0+m;
+    Fout2=Fout0+2*m;
+    Fout3=Fout0+3*m;
+    Fout4=Fout0+4*m;
+
+    tw=st->twiddles;
+    for ( u=0; u<m; ++u ) {
+        C_FIXDIV( *Fout0,5); C_FIXDIV( *Fout1,5); C_FIXDIV( *Fout2,5); C_FIXDIV( *Fout3,5); C_FIXDIV( *Fout4,5);
+        scratch[0] = *Fout0;
+
+        C_MUL(scratch[1] ,*Fout1, tw[u*fstride]);
+        C_MUL(scratch[2] ,*Fout2, tw[2*u*fstride]);
+        C_MUL(scratch[3] ,*Fout3, tw[3*u*fstride]);
+        C_MUL(scratch[4] ,*Fout4, tw[4*u*fstride]);
+
+        C_ADD( scratch[7],scratch[1],scratch[4]);
+        C_SUB( scratch[10],scratch[1],scratch[4]);
+        C_ADD( scratch[8],scratch[2],scratch[3]);
+        C_SUB( scratch[9],scratch[2],scratch[3]);
+
+        Fout0->r += scratch[7].r + scratch[8].r;
+        Fout0->i += scratch[7].i + scratch[8].i;
+
+        scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r);
+        scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r);
+
+        scratch[6].r =  S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i);
+        scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i);
+
+        C_SUB(*Fout1,scratch[5],scratch[6]);
+        C_ADD(*Fout4,scratch[5],scratch[6]);
+
+        scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r);
+        scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r);
+        scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i);
+        scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i);
+
+        C_ADD(*Fout2,scratch[11],scratch[12]);
+        C_SUB(*Fout3,scratch[11],scratch[12]);
+
+        ++Fout0;++Fout1;++Fout2;++Fout3;++Fout4;
+    }
+}
+
+/* perform the butterfly for one stage of a mixed radix FFT */
+static void kf_bfly_generic(
+        kiss_fft_cpx * Fout,
+        const size_t fstride,
+        const kiss_fft_cfg st,
+        int m,
+        int p
+        )
+{
+    int u,k,q1,q;
+    kiss_fft_cpx * twiddles = st->twiddles;
+    kiss_fft_cpx t;
+    int Norig = st->nfft;
+
+    kiss_fft_cpx * scratch = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC(sizeof(kiss_fft_cpx)*p);
+
+    for ( u=0; u<m; ++u ) {
+        k=u;
+        for ( q1=0 ; q1<p ; ++q1 ) {
+            scratch[q1] = Fout[ k  ];
+            C_FIXDIV(scratch[q1],p);
+            k += m;
+        }
+
+        k=u;
+        for ( q1=0 ; q1<p ; ++q1 ) {
+            int twidx=0;
+            Fout[ k ] = scratch[0];
+            for (q=1;q<p;++q ) {
+                twidx += fstride * k;
+                if (twidx>=Norig) twidx-=Norig;
+                C_MUL(t,scratch[q] , twiddles[twidx] );
+                C_ADDTO( Fout[ k ] ,t);
+            }
+            k += m;
+        }
+    }
+    KISS_FFT_TMP_FREE(scratch);
+}
+
+static
+void kf_work(
+        kiss_fft_cpx * Fout,
+        const kiss_fft_cpx * f,
+        const size_t fstride,
+        int in_stride,
+        int * factors,
+        const kiss_fft_cfg st
+        )
+{
+    kiss_fft_cpx * Fout_beg=Fout;
+    const int p=*factors++; /* the radix  */
+    const int m=*factors++; /* stage's fft length/p */
+    const kiss_fft_cpx * Fout_end = Fout + p*m;
+
+#ifdef _OPENMP
+    // use openmp extensions at the 
+    // top-level (not recursive)
+    if (fstride==1 && p<=5)
+    {
+        int k;
+
+        // execute the p different work units in different threads
+#       pragma omp parallel for
+        for (k=0;k<p;++k) 
+            kf_work( Fout +k*m, f+ fstride*in_stride*k,fstride*p,in_stride,factors,st);
+        // all threads have joined by this point
+
+        switch (p) {
+            case 2: kf_bfly2(Fout,fstride,st,m); break;
+            case 3: kf_bfly3(Fout,fstride,st,m); break; 
+            case 4: kf_bfly4(Fout,fstride,st,m); break;
+            case 5: kf_bfly5(Fout,fstride,st,m); break; 
+            default: kf_bfly_generic(Fout,fstride,st,m,p); break;
+        }
+        return;
+    }
+#endif
+
+    if (m==1) {
+        do{
+            *Fout = *f;
+            f += fstride*in_stride;
+        }while(++Fout != Fout_end );
+    }else{
+        do{
+            // recursive call:
+            // DFT of size m*p performed by doing
+            // p instances of smaller DFTs of size m, 
+            // each one takes a decimated version of the input
+            kf_work( Fout , f, fstride*p, in_stride, factors,st);
+            f += fstride*in_stride;
+        }while( (Fout += m) != Fout_end );
+    }
+
+    Fout=Fout_beg;
+
+    // recombine the p smaller DFTs 
+    switch (p) {
+        case 2: kf_bfly2(Fout,fstride,st,m); break;
+        case 3: kf_bfly3(Fout,fstride,st,m); break; 
+        case 4: kf_bfly4(Fout,fstride,st,m); break;
+        case 5: kf_bfly5(Fout,fstride,st,m); break; 
+        default: kf_bfly_generic(Fout,fstride,st,m,p); break;
+    }
+}
+
+/*  facbuf is populated by p1,m1,p2,m2, ...
+    where 
+    p[i] * m[i] = m[i-1]
+    m0 = n                  */
+static 
+void kf_factor(int n,int * facbuf)
+{
+    int p=4;
+    double floor_sqrt;
+    floor_sqrt = floor( sqrt((double)n) );
+
+    /*factor out powers of 4, powers of 2, then any remaining primes */
+    do {
+        while (n % p) {
+            switch (p) {
+                case 4: p = 2; break;
+                case 2: p = 3; break;
+                default: p += 2; break;
+            }
+            if (p > floor_sqrt)
+                p = n;          /* no more factors, skip to end */
+        }
+        n /= p;
+        *facbuf++ = p;
+        *facbuf++ = n;
+    } while (n > 1);
+}
+
+/*
+ *
+ * User-callable function to allocate all necessary storage space for the fft.
+ *
+ * The return value is a contiguous block of memory, allocated with malloc.  As such,
+ * It can be freed with free(), rather than a kiss_fft-specific function.
+ * */
+kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem )
+{
+    kiss_fft_cfg st=NULL;
+    size_t memneeded = sizeof(struct kiss_fft_state)
+        + sizeof(kiss_fft_cpx)*(nfft-1); /* twiddle factors*/
+
+    if ( lenmem==NULL ) {
+        st = ( kiss_fft_cfg)KISS_FFT_MALLOC( memneeded );
+    }else{
+        if (mem != NULL && *lenmem >= memneeded)
+            st = (kiss_fft_cfg)mem;
+        *lenmem = memneeded;
+    }
+    if (st) {
+        int i;
+        st->nfft=nfft;
+        st->inverse = inverse_fft;
+
+        for (i=0;i<nfft;++i) {
+            const double pi=3.141592653589793238462643383279502884197169399375105820974944;
+            double phase = -2*pi*i / nfft;
+            if (st->inverse)
+                phase *= -1;
+            kf_cexp(st->twiddles+i, phase );
+        }
+
+        kf_factor(nfft,st->factors);
+    }
+    return st;
+}
+
+
+void kiss_fft_stride(kiss_fft_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int in_stride)
+{
+    if (fin == fout) {
+        //NOTE: this is not really an in-place FFT algorithm.
+        //It just performs an out-of-place FFT into a temp buffer
+        kiss_fft_cpx * tmpbuf = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC( sizeof(kiss_fft_cpx)*st->nfft);
+        kf_work(tmpbuf,fin,1,in_stride, st->factors,st);
+        memcpy(fout,tmpbuf,sizeof(kiss_fft_cpx)*st->nfft);
+        KISS_FFT_TMP_FREE(tmpbuf);
+    }else{
+        kf_work( fout, fin, 1,in_stride, st->factors,st );
+    }
+}
+
+void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout)
+{
+    kiss_fft_stride(cfg,fin,fout,1);
+}
+
+
+void kiss_fft_cleanup(void)
+{
+    // nothing needed any more
+}
+
+int kiss_fft_next_fast_size(int n)
+{
+    while(1) {
+        int m=n;
+        while ( (m%2) == 0 ) m/=2;
+        while ( (m%3) == 0 ) m/=3;
+        while ( (m%5) == 0 ) m/=5;
+        if (m<=1)
+            break; /* n is completely factorable by twos, threes, and fives */
+        n++;
+    }
+    return n;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/kissfft/kiss_fft.h	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,124 @@
+#ifndef KISS_FFT_H
+#define KISS_FFT_H
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ ATTENTION!
+ If you would like a :
+ -- a utility that will handle the caching of fft objects
+ -- real-only (no imaginary time component ) FFT
+ -- a multi-dimensional FFT
+ -- a command-line utility to perform ffts
+ -- a command-line utility to perform fast-convolution filtering
+
+ Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c
+  in the tools/ directory.
+*/
+
+#ifdef USE_SIMD
+# include <xmmintrin.h>
+# define kiss_fft_scalar __m128
+#define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16)
+#define KISS_FFT_FREE _mm_free
+#else	
+#define KISS_FFT_MALLOC malloc
+#define KISS_FFT_FREE free
+#endif	
+
+
+#ifdef FIXED_POINT
+#include <sys/types.h>	
+# if (FIXED_POINT == 32)
+#  define kiss_fft_scalar int32_t
+# else	
+#  define kiss_fft_scalar int16_t
+# endif
+#else
+# ifndef kiss_fft_scalar
+/*  default is float */
+#   define kiss_fft_scalar float
+# endif
+#endif
+
+typedef struct {
+    kiss_fft_scalar r;
+    kiss_fft_scalar i;
+}kiss_fft_cpx;
+
+typedef struct kiss_fft_state* kiss_fft_cfg;
+
+/* 
+ *  kiss_fft_alloc
+ *  
+ *  Initialize a FFT (or IFFT) algorithm's cfg/state buffer.
+ *
+ *  typical usage:      kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL);
+ *
+ *  The return value from fft_alloc is a cfg buffer used internally
+ *  by the fft routine or NULL.
+ *
+ *  If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc.
+ *  The returned value should be free()d when done to avoid memory leaks.
+ *  
+ *  The state can be placed in a user supplied buffer 'mem':
+ *  If lenmem is not NULL and mem is not NULL and *lenmem is large enough,
+ *      then the function places the cfg in mem and the size used in *lenmem
+ *      and returns mem.
+ *  
+ *  If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough),
+ *      then the function returns NULL and places the minimum cfg 
+ *      buffer size in *lenmem.
+ * */
+
+kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem); 
+
+/*
+ * kiss_fft(cfg,in_out_buf)
+ *
+ * Perform an FFT on a complex input buffer.
+ * for a forward FFT,
+ * fin should be  f[0] , f[1] , ... ,f[nfft-1]
+ * fout will be   F[0] , F[1] , ... ,F[nfft-1]
+ * Note that each element is complex and can be accessed like
+    f[k].r and f[k].i
+ * */
+void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout);
+
+/*
+ A more generic version of the above function. It reads its input from every Nth sample.
+ * */
+void kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride);
+
+/* If kiss_fft_alloc allocated a buffer, it is one contiguous 
+   buffer and can be simply free()d when no longer needed*/
+#define kiss_fft_free free
+
+/*
+ Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up 
+ your compiler output to call this before you exit.
+*/
+void kiss_fft_cleanup(void);
+	
+
+/*
+ * Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5)
+ */
+int kiss_fft_next_fast_size(int n);
+
+/* for real ffts, we need an even size */
+#define kiss_fftr_next_fast_size_real(n) \
+        (kiss_fft_next_fast_size( ((n)+1)>>1)<<1)
+
+#ifdef __cplusplus
+} 
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/kissfft/tools/kiss_fftr.c	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,163 @@
+/*
+Copyright (c) 2003-2004, Mark Borgerding
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+    * Neither the author nor the names of any contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#include "kiss_fftr.h"
+#include "_kiss_fft_guts.h"
+
+struct kiss_fftr_state{
+    kiss_fft_cfg substate;
+    kiss_fft_cpx * tmpbuf;
+    kiss_fft_cpx * super_twiddles;
+#ifdef USE_SIMD
+    void * pad;
+#endif
+};
+
+void kiss_fftr_free(struct kiss_fftr_state *state) {
+    free(state);
+}
+
+kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem)
+{
+    int i;
+    kiss_fftr_cfg st = NULL;
+    size_t subsize, memneeded;
+
+    if (nfft & 1) {
+        fprintf(stderr,"Real FFT optimization must be even.\n");
+        return NULL;
+    }
+    nfft >>= 1;
+
+    kiss_fft_alloc (nfft, inverse_fft, NULL, &subsize);
+    memneeded = sizeof(struct kiss_fftr_state) + subsize + sizeof(kiss_fft_cpx) * ( nfft * 3 / 2);
+
+    if (lenmem == NULL) {
+        st = (kiss_fftr_cfg) KISS_FFT_MALLOC (memneeded);
+    } else {
+        if (*lenmem >= memneeded)
+            st = (kiss_fftr_cfg) mem;
+        *lenmem = memneeded;
+    }
+    if (!st)
+        return NULL;
+
+    st->substate = (kiss_fft_cfg) (st + 1); /*just beyond kiss_fftr_state struct */
+    st->tmpbuf = (kiss_fft_cpx *) (((char *) st->substate) + subsize);
+    st->super_twiddles = st->tmpbuf + nfft;
+    kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize);
+
+    for (i = 0; i < nfft/2; ++i) {
+        double phase =
+            -3.14159265358979323846264338327 * ((double) (i+1) / nfft + .5);
+        if (inverse_fft)
+            phase *= -1;
+        kf_cexp (st->super_twiddles+i,phase);
+    }
+    return st;
+}
+
+void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata)
+{
+    /* input buffer timedata is stored row-wise */
+    int k,ncfft;
+    kiss_fft_cpx fpnk,fpk,f1k,f2k,tw,tdc;
+
+    if ( st->substate->inverse) {
+        fprintf(stderr,"kiss fft usage error: improper alloc\n");
+        exit(1);
+    }
+
+    ncfft = st->substate->nfft;
+
+    /*perform the parallel fft of two real signals packed in real,imag*/
+    kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf );
+    /* The real part of the DC element of the frequency spectrum in st->tmpbuf
+     * contains the sum of the even-numbered elements of the input time sequence
+     * The imag part is the sum of the odd-numbered elements
+     *
+     * The sum of tdc.r and tdc.i is the sum of the input time sequence. 
+     *      yielding DC of input time sequence
+     * The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1... 
+     *      yielding Nyquist bin of input time sequence
+     */
+ 
+    tdc.r = st->tmpbuf[0].r;
+    tdc.i = st->tmpbuf[0].i;
+    C_FIXDIV(tdc,2);
+    CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i);
+    CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i);
+    freqdata[0].r = tdc.r + tdc.i;
+    freqdata[ncfft].r = tdc.r - tdc.i;
+#ifdef USE_SIMD    
+    freqdata[ncfft].i = freqdata[0].i = _mm_set1_ps(0);
+#else
+    freqdata[ncfft].i = freqdata[0].i = 0;
+#endif
+
+    for ( k=1;k <= ncfft/2 ; ++k ) {
+        fpk    = st->tmpbuf[k]; 
+        fpnk.r =   st->tmpbuf[ncfft-k].r;
+        fpnk.i = - st->tmpbuf[ncfft-k].i;
+        C_FIXDIV(fpk,2);
+        C_FIXDIV(fpnk,2);
+
+        C_ADD( f1k, fpk , fpnk );
+        C_SUB( f2k, fpk , fpnk );
+        C_MUL( tw , f2k , st->super_twiddles[k-1]);
+
+        freqdata[k].r = HALF_OF(f1k.r + tw.r);
+        freqdata[k].i = HALF_OF(f1k.i + tw.i);
+        freqdata[ncfft-k].r = HALF_OF(f1k.r - tw.r);
+        freqdata[ncfft-k].i = HALF_OF(tw.i - f1k.i);
+    }
+}
+
+void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata)
+{
+    /* input buffer timedata is stored row-wise */
+    int k, ncfft;
+
+    if (st->substate->inverse == 0) {
+        fprintf (stderr, "kiss fft usage error: improper alloc\n");
+        exit (1);
+    }
+
+    ncfft = st->substate->nfft;
+
+    st->tmpbuf[0].r = freqdata[0].r + freqdata[ncfft].r;
+    st->tmpbuf[0].i = freqdata[0].r - freqdata[ncfft].r;
+    C_FIXDIV(st->tmpbuf[0],2);
+
+    for (k = 1; k <= ncfft / 2; ++k) {
+        kiss_fft_cpx fk, fnkc, fek, fok, tmp;
+        fk = freqdata[k];
+        fnkc.r = freqdata[ncfft - k].r;
+        fnkc.i = -freqdata[ncfft - k].i;
+        C_FIXDIV( fk , 2 );
+        C_FIXDIV( fnkc , 2 );
+
+        C_ADD (fek, fk, fnkc);
+        C_SUB (tmp, fk, fnkc);
+        C_MUL (fok, tmp, st->super_twiddles[k-1]);
+        C_ADD (st->tmpbuf[k],     fek, fok);
+        C_SUB (st->tmpbuf[ncfft - k], fek, fok);
+#ifdef USE_SIMD        
+        st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0);
+#else
+        st->tmpbuf[ncfft - k].i *= -1;
+#endif
+    }
+    kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/kissfft/tools/kiss_fftr.h	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,46 @@
+#ifndef KISS_FTR_H
+#define KISS_FTR_H
+
+#include "kiss_fft.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+    
+/* 
+ 
+ Real optimized version can save about 45% cpu time vs. complex fft of a real seq.
+
+ 
+ 
+ */
+
+typedef struct kiss_fftr_state *kiss_fftr_cfg;
+
+
+kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem);
+/*
+ nfft must be even
+
+ If you don't care to allocate space, use mem = lenmem = NULL 
+*/
+
+
+void kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata);
+/*
+ input timedata has nfft scalar points
+ output freqdata has nfft/2+1 complex points
+*/
+
+void kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata);
+/*
+ input freqdata has  nfft/2+1 complex points
+ output timedata has nfft scalar points
+*/
+
+void kiss_fftr_free(kiss_fftr_cfg);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
--- a/fft/native.cpp	Fri Oct 16 17:18:45 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-
-#include <bqfft/FFT.h>
-
-#include <vector>
-#include <iostream>
-#include <cmath>
-#include <chrono>
-
-using namespace std;
-using namespace breakfastquay;
-
-int main(int argc, char **argv)
-{
-    vector<int> sizes { 512, 2048 };
-
-    int iterations = 2000;
-    int times = 100;
-
-    for (auto size: sizes) {
-
-	FFT fft(size);
-	double total = 0.0;
-
-	auto start = chrono::high_resolution_clock::now();
-
-	for (int ti = 0; ti < times; ++ti) {
-	    
-	    total = 0.0;
-
-	    for (int i = 0; i < iterations; ++i) {
-
-		vector<double> ri(size), ro(size/2+1), io(size/2+1);
-		for (int j = 0; j < size; ++j) {
-		    ri[j] = (j % 2) / 4.0;
-		}
-
-		fft.forward(ri.data(), ro.data(), io.data());
-
-		for (int j = 0; j <= size/2; ++j) {
-		    total += sqrt(ro[j] * ro[j] + io[j] * io[j]);
-		}
-
-		// synthesise the conjugate half
-		for (int j = 1; j < size/2; ++j) {
-		    total += sqrt(ro[j] * ro[j] + io[j] * io[j]);
-		}
-	    }
-	}
-
-	auto end = chrono::high_resolution_clock::now();
-
-	double ms = chrono::duration<double, milli>(end - start).count() / times;
-	
-	cerr << "for " << iterations << " * size " << size << ": total = "
-	     << total << ", time = " << ms
-	     << " ms (" << (iterations / (ms / 1000.0)) << " itr/sec)" << endl;
-    }
-}
-
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/Makefile.osx	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,6 @@
+
+OBJECTS	:= bqfft/src/FFT.o native.o
+CXXFLAGS   := -O3 -ffast-math -DMALLOC_IS_ALIGNED -DHAVE_VDSP -Ibqfft -Ibqvec -std=c++11
+
+native:	   $(OBJECTS)
+	   $(CXX) -o $@ $^ -framework Accelerate
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqfft/COPYING	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,26 @@
+
+    Copyright 2007-2015 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqfft/Makefile	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,90 @@
+
+# Add to FFT_DEFINES the relevant options for your desired third-party
+# library support.
+#
+# Available options are
+#
+#  -DHAVE_IPP         Intel's Integrated Performance Primitives are available
+#  -DHAVE_VDSP        Apple's Accelerate framework is available
+#  -DHAVE_FFTW3       The FFTW library is available
+#  -DHAVE_KISSFFT     The KissFFT library is available
+#  -DHAVE_MEDIALIB    The Medialib library (from Sun) is available
+#  -DHAVE_OPENMAX     The OpenMAX signal processing library is available
+#  -DUSE_BUILTIN_FFT  Compile the built-in FFT code (which is very slow)
+#
+# You may define more than one of these. If you define
+# USE_BUILTIN_FFT, the code will be compiled in but will only be used
+# if no other option is available. The default, if no flags are
+# supplied, is for the code to refuse to compile.
+# 
+# Add any relevant -I flags for include paths as well.
+#
+# Note that you must supply the same flags when including bqfft
+# headers later as you are using now when compiling the library. (You
+# may find it simplest to just add the bqfft source files to your
+# application's build system and not build a bqfft library at all.)
+
+# WARNING! The default option here is VERY SLOW! Read above for better
+# alternatives!
+FFT_DEFINES	:= -DHAVE_VDSP
+
+
+# Add to ALLOCATOR_DEFINES options relating to aligned malloc.
+#
+# Available options are
+#
+#  -DHAVE_POSIX_MEMALIGN     The posix_memalign call is available in sys/mman.h
+#  -DLACK_POSIX_MEMALIGN     The posix_memalign call is not available
+#
+#  -DMALLOC_IS_ALIGNED       The malloc call already returns aligned memory
+#  -DMALLOC_IS_NOT_ALIGNED   The malloc call does not return aligned memory
+#
+#  -DUSE_OWN_ALIGNED_MALLOC  No aligned malloc is available, roll your own
+#
+#  -DLACK_BAD_ALLOC          The C++ library lacks the std::bad_alloc exception
+#
+# Here "aligned" is assumed to mean "aligned enough for whatever
+# vector stuff the space will be used for" which most likely means
+# 16-byte alignment.
+#
+# The default is to use _aligned_malloc when building with Visual C++,
+# system malloc when building on OS/X, and posix_memalign otherwise.
+#
+# Note that you must supply the same flags when including bqfft
+# headers later as you are using now when compiling the library. (You
+# may find it simplest to just add the bqfft source files to your
+# application's build system and not build a bqfft library at all.)
+
+ALLOCATOR_DEFINES := -DMALLOC_IS_ALIGNED
+
+SRC_DIR	:= src
+HEADER_DIR := bqfft
+
+SOURCES	:= $(wildcard $(SRC_DIR)/*.cpp)
+HEADERS	:= $(wildcard $(HEADER_DIR)/*.h) $(wildcard $(SRC_DIR)/*.h)
+
+OBJECTS	:= $(SOURCES:.cpp=.o)
+OBJECTS	:= $(OBJECTS:.c=.o)
+
+CXXFLAGS := $(FFT_DEFINES) $(ALLOCATOR_DEFINES) -O3 -ffast-math -I. -I../bqvec -fpic
+
+LIBRARY	:= libbqfft.a
+
+all:	$(LIBRARY)
+
+$(LIBRARY):	$(OBJECTS)
+	$(AR) rc $@ $^
+
+clean:		
+	rm -f $(OBJECTS)
+
+distclean:	clean
+	rm -f $(LIBRARY)
+
+depend:
+	makedepend -Y -fMakefile $(SOURCES) $(HEADERS)
+
+
+# DO NOT DELETE
+
+src/FFT.o: bqfft/FFT.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqfft/README.txt	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,20 @@
+
+bqfft
+=====
+
+A small library wrapping various FFT implementations for some common
+audio processing use cases. Note this is not a general FFT interface,
+as it handles only power-of-two FFT sizes and real inputs.
+
+Requires the bqvec library.
+
+C++ standard required: C++98 (does not use C++11)
+
+Copyright 2007-2015 Particular Programs Ltd.
+
+This code originated as part of the Rubber Band Library written by the
+same authors (see https://bitbucket.org/breakfastquay/rubberband/).
+It has been pulled out into a separate library and relicensed under a
+more permissive licence: a BSD/MIT-style licence, as opposed to the
+GPL used for Rubber Band.  See the file COPYING for details.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqfft/bqfft/FFT.h	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,145 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    bqfft
+
+    A small library wrapping various FFT implementations for some
+    common audio processing use cases.
+
+    Copyright 2007-2015 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#ifndef BQFFT_FFT_H
+#define BQFFT_FFT_H
+
+#include <bqvec/Restrict.h>
+
+#include <string>
+#include <set>
+
+namespace breakfastquay {
+
+class FFTImpl;
+
+/**
+ * Provide basic FFT computations using one of a set of candidate FFT
+ * implementations (depending on compile flags).
+ *
+ * Implements real->complex FFTs of power-of-two sizes only.  Note
+ * that only the first half of the output signal is returned (the
+ * complex conjugates half is omitted), so the "complex" arrays need
+ * room for size/2+1 elements.
+ *
+ * The "interleaved" functions use the format sometimes called CCS --
+ * size/2+1 real+imaginary pairs.  So, the array elements at indices 1
+ * and size+1 will always be zero (since the signal is real).
+ *
+ * All pointer arguments must point to valid data. A NullArgument
+ * exception is thrown if any argument is NULL.
+ *
+ * Neither forward nor inverse transform is scaled.
+ *
+ * This class is reentrant but not thread safe: use a separate
+ * instance per thread, or use a mutex.
+ */
+class FFT
+{
+public:
+    enum Exception {
+        NullArgument, InvalidSize, InvalidImplementation, InternalError
+    };
+
+    FFT(int size, int debugLevel = 0); // may throw InvalidSize
+    ~FFT();
+
+    void forward(const double *BQ_R__ realIn, double *BQ_R__ realOut, double *BQ_R__ imagOut);
+    void forwardInterleaved(const double *BQ_R__ realIn, double *BQ_R__ complexOut);
+    void forwardPolar(const double *BQ_R__ realIn, double *BQ_R__ magOut, double *BQ_R__ phaseOut);
+    void forwardMagnitude(const double *BQ_R__ realIn, double *BQ_R__ magOut);
+
+    void forward(const float *BQ_R__ realIn, float *BQ_R__ realOut, float *BQ_R__ imagOut);
+    void forwardInterleaved(const float *BQ_R__ realIn, float *BQ_R__ complexOut);
+    void forwardPolar(const float *BQ_R__ realIn, float *BQ_R__ magOut, float *BQ_R__ phaseOut);
+    void forwardMagnitude(const float *BQ_R__ realIn, float *BQ_R__ magOut);
+
+    void inverse(const double *BQ_R__ realIn, const double *BQ_R__ imagIn, double *BQ_R__ realOut);
+    void inverseInterleaved(const double *BQ_R__ complexIn, double *BQ_R__ realOut);
+    void inversePolar(const double *BQ_R__ magIn, const double *BQ_R__ phaseIn, double *BQ_R__ realOut);
+    void inverseCepstral(const double *BQ_R__ magIn, double *BQ_R__ cepOut);
+
+    void inverse(const float *BQ_R__ realIn, const float *BQ_R__ imagIn, float *BQ_R__ realOut);
+    void inverseInterleaved(const float *BQ_R__ complexIn, float *BQ_R__ realOut);
+    void inversePolar(const float *BQ_R__ magIn, const float *BQ_R__ phaseIn, float *BQ_R__ realOut);
+    void inverseCepstral(const float *BQ_R__ magIn, float *BQ_R__ cepOut);
+
+    // Calling one or both of these is optional -- if neither is
+    // called, the first call to a forward or inverse method will call
+    // init().  You only need call these if you don't want to risk
+    // expensive allocations etc happening in forward or inverse.
+    void initFloat();
+    void initDouble();
+
+    enum Precision {
+        SinglePrecision = 0x1,
+        DoublePrecision = 0x2
+    };
+    typedef int Precisions;
+
+    /**
+     * Return the OR of all precisions supported by this
+     * implementation. All of the functions (float and double) are
+     * available regardless of the supported implementations, but they
+     * will be calculated at the proper precision only if it is
+     * available. (So float functions will be calculated using doubles
+     * and then truncated if single-precision is unavailable, and
+     * double functions will use single-precision arithmetic if double
+     * is unavailable.)
+     */
+    Precisions getSupportedPrecisions() const;
+
+    static std::set<std::string> getImplementations();
+    static std::string getDefaultImplementation();
+    static void setDefaultImplementation(std::string);
+
+#ifdef FFT_MEASUREMENT
+    static std::string tune();
+#endif
+
+protected:
+    FFTImpl *d;
+    static std::string m_implementation;
+    static void pickDefaultImplementation();
+
+private:
+    FFT(const FFT &); // not provided
+    FFT &operator=(const FFT &); // not provided
+};
+
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqfft/src/FFT.cpp	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,3585 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    bqfft
+
+    A small library wrapping various FFT implementations for some
+    common audio processing use cases.
+
+    Copyright 2007-2015 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#include "bqfft/FFT.h"
+
+//#include "system/Thread.h"
+
+#include <bqvec/Allocators.h>
+#include <bqvec/VectorOps.h>
+#include <bqvec/VectorOpsComplex.h>
+
+//#define FFT_MEASUREMENT 1
+
+#ifdef FFT_MEASUREMENT
+#include <sstream>
+#endif
+
+#ifdef HAVE_IPP
+#include <ipps.h>
+#endif
+
+#ifdef HAVE_FFTW3
+#include <fftw3.h>
+#endif
+
+#ifdef HAVE_VDSP
+#include <Accelerate/Accelerate.h>
+#endif
+
+#ifdef HAVE_MEDIALIB
+#include <mlib_signal.h>
+#endif
+
+#ifdef HAVE_OPENMAX
+#include <omxSP.h>
+#endif
+
+#ifdef HAVE_SFFT
+extern "C" {
+#include <sfft.h>
+}
+#endif
+
+#ifdef HAVE_KISSFFT
+#include "kissfft/kiss_fftr.h"
+#endif
+
+#ifndef HAVE_IPP
+#ifndef HAVE_FFTW3
+#ifndef HAVE_KISSFFT
+#ifndef USE_BUILTIN_FFT
+#ifndef HAVE_VDSP
+#ifndef HAVE_MEDIALIB
+#ifndef HAVE_OPENMAX
+#ifndef HAVE_SFFT
+#error No FFT implementation selected!
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+#endif
+
+#include <cmath>
+#include <iostream>
+#include <map>
+#include <cstdio>
+#include <cstdlib>
+#include <vector>
+
+#ifdef FFT_MEASUREMENT
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+#endif
+
+namespace breakfastquay {
+
+class FFTImpl
+{
+public:
+    virtual ~FFTImpl() { }
+
+    virtual FFT::Precisions getSupportedPrecisions() const = 0;
+
+    virtual void initFloat() = 0;
+    virtual void initDouble() = 0;
+
+    virtual void forward(const double *BQ_R__ realIn, double *BQ_R__ realOut, double *BQ_R__ imagOut) = 0;
+    virtual void forwardInterleaved(const double *BQ_R__ realIn, double *BQ_R__ complexOut) = 0;
+    virtual void forwardPolar(const double *BQ_R__ realIn, double *BQ_R__ magOut, double *BQ_R__ phaseOut) = 0;
+    virtual void forwardMagnitude(const double *BQ_R__ realIn, double *BQ_R__ magOut) = 0;
+
+    virtual void forward(const float *BQ_R__ realIn, float *BQ_R__ realOut, float *BQ_R__ imagOut) = 0;
+    virtual void forwardInterleaved(const float *BQ_R__ realIn, float *BQ_R__ complexOut) = 0;
+    virtual void forwardPolar(const float *BQ_R__ realIn, float *BQ_R__ magOut, float *BQ_R__ phaseOut) = 0;
+    virtual void forwardMagnitude(const float *BQ_R__ realIn, float *BQ_R__ magOut) = 0;
+
+    virtual void inverse(const double *BQ_R__ realIn, const double *BQ_R__ imagIn, double *BQ_R__ realOut) = 0;
+    virtual void inverseInterleaved(const double *BQ_R__ complexIn, double *BQ_R__ realOut) = 0;
+    virtual void inversePolar(const double *BQ_R__ magIn, const double *BQ_R__ phaseIn, double *BQ_R__ realOut) = 0;
+    virtual void inverseCepstral(const double *BQ_R__ magIn, double *BQ_R__ cepOut) = 0;
+
+    virtual void inverse(const float *BQ_R__ realIn, const float *BQ_R__ imagIn, float *BQ_R__ realOut) = 0;
+    virtual void inverseInterleaved(const float *BQ_R__ complexIn, float *BQ_R__ realOut) = 0;
+    virtual void inversePolar(const float *BQ_R__ magIn, const float *BQ_R__ phaseIn, float *BQ_R__ realOut) = 0;
+    virtual void inverseCepstral(const float *BQ_R__ magIn, float *BQ_R__ cepOut) = 0;
+};    
+
+namespace FFTs {
+
+#ifdef HAVE_IPP
+
+class D_IPP : public FFTImpl
+{
+public:
+    D_IPP(int size) :
+        m_size(size), m_fspec(0), m_dspec(0)
+    { 
+        for (int i = 0; ; ++i) {
+            if (m_size & (1 << i)) {
+                m_order = i;
+                break;
+            }
+        }
+    }
+
+    ~D_IPP() {
+        if (m_fspec) {
+            ippsFFTFree_R_32f(m_fspec);
+            ippsFree(m_fbuf);
+            ippsFree(m_fpacked);
+            ippsFree(m_fspare);
+        }
+        if (m_dspec) {
+            ippsFFTFree_R_64f(m_dspec);
+            ippsFree(m_dbuf);
+            ippsFree(m_dpacked);
+            ippsFree(m_dspare);
+        }
+    }
+
+    FFT::Precisions
+    getSupportedPrecisions() const {
+        return FFT::SinglePrecision | FFT::DoublePrecision;
+    }
+
+    //!!! rv check
+
+    void initFloat() {
+        if (m_fspec) return;
+        int specSize, specBufferSize, bufferSize;
+        ippsFFTGetSize_R_32f(m_order, IPP_FFT_NODIV_BY_ANY, ippAlgHintFast,
+                             &specSize, &specBufferSize, &bufferSize);
+        m_fbuf = ippsMalloc_8u(bufferSize);
+        m_fpacked = ippsMalloc_32f(m_size + 2);
+        m_fspare = ippsMalloc_32f(m_size / 2 + 1);
+        ippsFFTInitAlloc_R_32f(&m_fspec, m_order, IPP_FFT_NODIV_BY_ANY, 
+                               ippAlgHintFast);
+    }
+
+    void initDouble() {
+        if (m_dspec) return;
+        int specSize, specBufferSize, bufferSize;
+        ippsFFTGetSize_R_64f(m_order, IPP_FFT_NODIV_BY_ANY, ippAlgHintFast,
+                             &specSize, &specBufferSize, &bufferSize);
+        m_dbuf = ippsMalloc_8u(bufferSize);
+        m_dpacked = ippsMalloc_64f(m_size + 2);
+        m_dspare = ippsMalloc_64f(m_size / 2 + 1);
+        ippsFFTInitAlloc_R_64f(&m_dspec, m_order, IPP_FFT_NODIV_BY_ANY, 
+                               ippAlgHintFast);
+    }
+
+    void packFloat(const float *BQ_R__ re, const float *BQ_R__ im) {
+        int index = 0;
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            m_fpacked[index++] = re[i];
+            index++;
+        }
+        index = 0;
+        if (im) {
+            for (int i = 0; i <= hs; ++i) {
+                index++;
+                m_fpacked[index++] = im[i];
+            }
+        } else {
+            for (int i = 0; i <= hs; ++i) {
+                index++;
+                m_fpacked[index++] = 0.f;
+            }
+        }
+    }
+
+    void packDouble(const double *BQ_R__ re, const double *BQ_R__ im) {
+        int index = 0;
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            m_dpacked[index++] = re[i];
+            index++;
+        }
+        index = 0;
+        if (im) {
+            for (int i = 0; i <= hs; ++i) {
+                index++;
+                m_dpacked[index++] = im[i];
+            }
+        } else {
+            for (int i = 0; i <= hs; ++i) {
+                index++;
+                m_dpacked[index++] = 0.0;
+            }
+        }
+    }
+
+    void unpackFloat(float *re, float *BQ_R__ im) { // re may be equal to m_fpacked
+        int index = 0;
+        const int hs = m_size/2;
+        if (im) {
+            for (int i = 0; i <= hs; ++i) {
+                index++;
+                im[i] = m_fpacked[index++];
+            }
+        }
+        index = 0;
+        for (int i = 0; i <= hs; ++i) {
+            re[i] = m_fpacked[index++];
+            index++;
+        }
+    }        
+
+    void unpackDouble(double *re, double *BQ_R__ im) { // re may be equal to m_dpacked
+        int index = 0;
+        const int hs = m_size/2;
+        if (im) {
+            for (int i = 0; i <= hs; ++i) {
+                index++;
+                im[i] = m_dpacked[index++];
+            }
+        }
+        index = 0;
+        for (int i = 0; i <= hs; ++i) {
+            re[i] = m_dpacked[index++];
+            index++;
+        }
+    }        
+
+    void forward(const double *BQ_R__ realIn, double *BQ_R__ realOut, double *BQ_R__ imagOut) {
+        if (!m_dspec) initDouble();
+        ippsFFTFwd_RToCCS_64f(realIn, m_dpacked, m_dspec, m_dbuf);
+        unpackDouble(realOut, imagOut);
+    }
+
+    void forwardInterleaved(const double *BQ_R__ realIn, double *BQ_R__ complexOut) {
+        if (!m_dspec) initDouble();
+        ippsFFTFwd_RToCCS_64f(realIn, complexOut, m_dspec, m_dbuf);
+    }
+
+    void forwardPolar(const double *BQ_R__ realIn, double *BQ_R__ magOut, double *BQ_R__ phaseOut) {
+        if (!m_dspec) initDouble();
+        ippsFFTFwd_RToCCS_64f(realIn, m_dpacked, m_dspec, m_dbuf);
+        unpackDouble(m_dpacked, m_dspare);
+        ippsCartToPolar_64f(m_dpacked, m_dspare, magOut, phaseOut, m_size/2+1);
+    }
+
+    void forwardMagnitude(const double *BQ_R__ realIn, double *BQ_R__ magOut) {
+        if (!m_dspec) initDouble();
+        ippsFFTFwd_RToCCS_64f(realIn, m_dpacked, m_dspec, m_dbuf);
+        unpackDouble(m_dpacked, m_dspare);
+        ippsMagnitude_64f(m_dpacked, m_dspare, magOut, m_size/2+1);
+    }
+
+    void forward(const float *BQ_R__ realIn, float *BQ_R__ realOut, float *BQ_R__ imagOut) {
+        if (!m_fspec) initFloat();
+        ippsFFTFwd_RToCCS_32f(realIn, m_fpacked, m_fspec, m_fbuf);
+        unpackFloat(realOut, imagOut);
+    }
+
+    void forwardInterleaved(const float *BQ_R__ realIn, float *BQ_R__ complexOut) {
+        if (!m_fspec) initFloat();
+        ippsFFTFwd_RToCCS_32f(realIn, complexOut, m_fspec, m_fbuf);
+    }
+
+    void forwardPolar(const float *BQ_R__ realIn, float *BQ_R__ magOut, float *BQ_R__ phaseOut) {
+        if (!m_fspec) initFloat();
+        ippsFFTFwd_RToCCS_32f(realIn, m_fpacked, m_fspec, m_fbuf);
+        unpackFloat(m_fpacked, m_fspare);
+        ippsCartToPolar_32f(m_fpacked, m_fspare, magOut, phaseOut, m_size/2+1);
+    }
+
+    void forwardMagnitude(const float *BQ_R__ realIn, float *BQ_R__ magOut) {
+        if (!m_fspec) initFloat();
+        ippsFFTFwd_RToCCS_32f(realIn, m_fpacked, m_fspec, m_fbuf);
+        unpackFloat(m_fpacked, m_fspare);
+        ippsMagnitude_32f(m_fpacked, m_fspare, magOut, m_size/2+1);
+    }
+
+    void inverse(const double *BQ_R__ realIn, const double *BQ_R__ imagIn, double *BQ_R__ realOut) {
+        if (!m_dspec) initDouble();
+        packDouble(realIn, imagIn);
+        ippsFFTInv_CCSToR_64f(m_dpacked, realOut, m_dspec, m_dbuf);
+    }
+
+    void inverseInterleaved(const double *BQ_R__ complexIn, double *BQ_R__ realOut) {
+        if (!m_dspec) initDouble();
+        ippsFFTInv_CCSToR_64f(complexIn, realOut, m_dspec, m_dbuf);
+    }
+
+    void inversePolar(const double *BQ_R__ magIn, const double *BQ_R__ phaseIn, double *BQ_R__ realOut) {
+        if (!m_dspec) initDouble();
+        ippsPolarToCart_64f(magIn, phaseIn, realOut, m_dspare, m_size/2+1);
+        packDouble(realOut, m_dspare); // to m_dpacked
+        ippsFFTInv_CCSToR_64f(m_dpacked, realOut, m_dspec, m_dbuf);
+    }
+
+    void inverseCepstral(const double *BQ_R__ magIn, double *BQ_R__ cepOut) {
+        if (!m_dspec) initDouble();
+        const int hs1 = m_size/2 + 1;
+        ippsCopy_64f(magIn, m_dspare, hs1);
+        ippsAddC_64f_I(0.000001, m_dspare, hs1);
+        ippsLn_64f_I(m_dspare, hs1);
+        packDouble(m_dspare, 0);
+        ippsFFTInv_CCSToR_64f(m_dpacked, cepOut, m_dspec, m_dbuf);
+    }
+    
+    void inverse(const float *BQ_R__ realIn, const float *BQ_R__ imagIn, float *BQ_R__ realOut) {
+        if (!m_fspec) initFloat();
+        packFloat(realIn, imagIn);
+        ippsFFTInv_CCSToR_32f(m_fpacked, realOut, m_fspec, m_fbuf);
+    }
+
+    void inverseInterleaved(const float *BQ_R__ complexIn, float *BQ_R__ realOut) {
+        if (!m_fspec) initFloat();
+        ippsFFTInv_CCSToR_32f(complexIn, realOut, m_fspec, m_fbuf);
+    }
+
+    void inversePolar(const float *BQ_R__ magIn, const float *BQ_R__ phaseIn, float *BQ_R__ realOut) {
+        if (!m_fspec) initFloat();
+        ippsPolarToCart_32f(magIn, phaseIn, realOut, m_fspare, m_size/2+1);
+        packFloat(realOut, m_fspare); // to m_fpacked
+        ippsFFTInv_CCSToR_32f(m_fpacked, realOut, m_fspec, m_fbuf);
+    }
+
+    void inverseCepstral(const float *BQ_R__ magIn, float *BQ_R__ cepOut) {
+        if (!m_fspec) initFloat();
+        const int hs1 = m_size/2 + 1;
+        ippsCopy_32f(magIn, m_fspare, hs1);
+        ippsAddC_32f_I(0.000001f, m_fspare, hs1);
+        ippsLn_32f_I(m_fspare, hs1);
+        packFloat(m_fspare, 0);
+        ippsFFTInv_CCSToR_32f(m_fpacked, cepOut, m_fspec, m_fbuf);
+    }
+
+private:
+    const int m_size;
+    int m_order;
+    IppsFFTSpec_R_32f *m_fspec;
+    IppsFFTSpec_R_64f *m_dspec;
+    Ipp8u *m_fbuf;
+    Ipp8u *m_dbuf;
+    float *m_fpacked;
+    float *m_fspare;
+    double *m_dpacked;
+    double *m_dspare;
+};
+
+#endif /* HAVE_IPP */
+
+#ifdef HAVE_VDSP
+
+class D_VDSP : public FFTImpl
+{
+public:
+    D_VDSP(int size) :
+        m_size(size), m_fspec(0), m_dspec(0),
+        m_fpacked(0), m_fspare(0),
+        m_dpacked(0), m_dspare(0)
+    { 
+        for (int i = 0; ; ++i) {
+            if (m_size & (1 << i)) {
+                m_order = i;
+                break;
+            }
+        }
+    }
+
+    ~D_VDSP() {
+        if (m_fspec) {
+            vDSP_destroy_fftsetup(m_fspec);
+            deallocate(m_fspare);
+            deallocate(m_fspare2);
+            deallocate(m_fbuf->realp);
+            deallocate(m_fbuf->imagp);
+            delete m_fbuf;
+            deallocate(m_fpacked->realp);
+            deallocate(m_fpacked->imagp);
+            delete m_fpacked;
+        }
+        if (m_dspec) {
+            vDSP_destroy_fftsetupD(m_dspec);
+            deallocate(m_dspare);
+            deallocate(m_dspare2);
+            deallocate(m_dbuf->realp);
+            deallocate(m_dbuf->imagp);
+            delete m_dbuf;
+            deallocate(m_dpacked->realp);
+            deallocate(m_dpacked->imagp);
+            delete m_dpacked;
+        }
+    }
+
+    FFT::Precisions
+    getSupportedPrecisions() const {
+        return FFT::SinglePrecision | FFT::DoublePrecision;
+    }
+
+    //!!! rv check
+
+    void initFloat() {
+        if (m_fspec) return;
+        m_fspec = vDSP_create_fftsetup(m_order, FFT_RADIX2);
+        m_fbuf = new DSPSplitComplex;
+        //!!! "If possible, tempBuffer->realp and tempBuffer->imagp should be 32-byte aligned for best performance."
+        m_fbuf->realp = allocate<float>(m_size);
+        m_fbuf->imagp = allocate<float>(m_size);
+        m_fpacked = new DSPSplitComplex;
+        m_fpacked->realp = allocate<float>(m_size / 2 + 1);
+        m_fpacked->imagp = allocate<float>(m_size / 2 + 1);
+        m_fspare = allocate<float>(m_size + 2);
+        m_fspare2 = allocate<float>(m_size + 2);
+    }
+
+    void initDouble() {
+        if (m_dspec) return;
+        m_dspec = vDSP_create_fftsetupD(m_order, FFT_RADIX2);
+        m_dbuf = new DSPDoubleSplitComplex;
+        //!!! "If possible, tempBuffer->realp and tempBuffer->imagp should be 32-byte aligned for best performance."
+        m_dbuf->realp = allocate<double>(m_size);
+        m_dbuf->imagp = allocate<double>(m_size);
+        m_dpacked = new DSPDoubleSplitComplex;
+        m_dpacked->realp = allocate<double>(m_size / 2 + 1);
+        m_dpacked->imagp = allocate<double>(m_size / 2 + 1);
+        m_dspare = allocate<double>(m_size + 2);
+        m_dspare2 = allocate<double>(m_size + 2);
+    }
+
+    void packReal(const float *BQ_R__ const re) {
+        // Pack input for forward transform 
+        vDSP_ctoz((DSPComplex *)re, 2, m_fpacked, 1, m_size/2);
+    }
+    void packComplex(const float *BQ_R__ const re, const float *BQ_R__ const im) {
+        // Pack input for inverse transform 
+        if (re) v_copy(m_fpacked->realp, re, m_size/2 + 1);
+        else v_zero(m_fpacked->realp, m_size/2 + 1);
+        if (im) v_copy(m_fpacked->imagp, im, m_size/2 + 1);
+        else v_zero(m_fpacked->imagp, m_size/2 + 1);
+        fnyq();
+    }
+
+    void unpackReal(float *BQ_R__ const re) {
+        // Unpack output for inverse transform
+        vDSP_ztoc(m_fpacked, 1, (DSPComplex *)re, 2, m_size/2);
+    }
+    void unpackComplex(float *BQ_R__ const re, float *BQ_R__ const im) {
+        // Unpack output for forward transform
+        // vDSP forward FFTs are scaled 2x (for some reason)
+        float two = 2.f;
+        vDSP_vsdiv(m_fpacked->realp, 1, &two, re, 1, m_size/2 + 1);
+        vDSP_vsdiv(m_fpacked->imagp, 1, &two, im, 1, m_size/2 + 1);
+    }
+    void unpackComplex(float *BQ_R__ const cplx) {
+        // Unpack output for forward transform
+        // vDSP forward FFTs are scaled 2x (for some reason)
+        const int hs1 = m_size/2 + 1;
+        for (int i = 0; i < hs1; ++i) {
+            cplx[i*2] = m_fpacked->realp[i] / 2.f;
+            cplx[i*2+1] = m_fpacked->imagp[i] / 2.f;
+        }
+    }
+
+    void packReal(const double *BQ_R__ const re) {
+        // Pack input for forward transform
+        vDSP_ctozD((DSPDoubleComplex *)re, 2, m_dpacked, 1, m_size/2);
+    }
+    void packComplex(const double *BQ_R__ const re, const double *BQ_R__ const im) {
+        // Pack input for inverse transform
+        if (re) v_copy(m_dpacked->realp, re, m_size/2 + 1);
+        else v_zero(m_dpacked->realp, m_size/2 + 1);
+        if (im) v_copy(m_dpacked->imagp, im, m_size/2 + 1);
+        else v_zero(m_dpacked->imagp, m_size/2 + 1);
+        dnyq();
+    }
+
+    void unpackReal(double *BQ_R__ const re) {
+        // Unpack output for inverse transform
+        vDSP_ztocD(m_dpacked, 1, (DSPDoubleComplex *)re, 2, m_size/2);
+    }
+    void unpackComplex(double *BQ_R__ const re, double *BQ_R__ const im) {
+        // Unpack output for forward transform
+        // vDSP forward FFTs are scaled 2x (for some reason)
+        double two = 2.0;
+        vDSP_vsdivD(m_dpacked->realp, 1, &two, re, 1, m_size/2 + 1);
+        vDSP_vsdivD(m_dpacked->imagp, 1, &two, im, 1, m_size/2 + 1);
+    }
+    void unpackComplex(double *BQ_R__ const cplx) {
+        // Unpack output for forward transform
+        // vDSP forward FFTs are scaled 2x (for some reason)
+        const int hs1 = m_size/2 + 1;
+        for (int i = 0; i < hs1; ++i) {
+            cplx[i*2] = m_dpacked->realp[i] / 2.0;
+            cplx[i*2+1] = m_dpacked->imagp[i] / 2.0;
+        }
+    }
+
+    void fdenyq() {
+        // for fft result in packed form, unpack the DC and Nyquist bins
+        const int hs = m_size/2;
+        m_fpacked->realp[hs] = m_fpacked->imagp[0];
+        m_fpacked->imagp[hs] = 0.f;
+        m_fpacked->imagp[0] = 0.f;
+    }
+    void ddenyq() {
+        // for fft result in packed form, unpack the DC and Nyquist bins
+        const int hs = m_size/2;
+        m_dpacked->realp[hs] = m_dpacked->imagp[0];
+        m_dpacked->imagp[hs] = 0.;
+        m_dpacked->imagp[0] = 0.;
+    }
+
+    void fnyq() {
+        // for ifft input in packed form, pack the DC and Nyquist bins
+        const int hs = m_size/2;
+        m_fpacked->imagp[0] = m_fpacked->realp[hs];
+        m_fpacked->realp[hs] = 0.f;
+        m_fpacked->imagp[hs] = 0.f;
+    }
+    void dnyq() {
+        // for ifft input in packed form, pack the DC and Nyquist bins
+        const int hs = m_size/2;
+        m_dpacked->imagp[0] = m_dpacked->realp[hs];
+        m_dpacked->realp[hs] = 0.;
+        m_dpacked->imagp[hs] = 0.;
+    }
+
+    void forward(const double *BQ_R__ realIn, double *BQ_R__ realOut, double *BQ_R__ imagOut) {
+        if (!m_dspec) initDouble();
+        packReal(realIn);
+        vDSP_fft_zriptD(m_dspec, m_dpacked, 1, m_dbuf, m_order, FFT_FORWARD);
+        ddenyq();
+        unpackComplex(realOut, imagOut);
+    }
+
+    void forwardInterleaved(const double *BQ_R__ realIn, double *BQ_R__ complexOut) {
+        if (!m_dspec) initDouble();
+        packReal(realIn);
+        vDSP_fft_zriptD(m_dspec, m_dpacked, 1, m_dbuf, m_order, FFT_FORWARD);
+        ddenyq();
+        unpackComplex(complexOut);
+    }
+
+    void forwardPolar(const double *BQ_R__ realIn, double *BQ_R__ magOut, double *BQ_R__ phaseOut) {
+        if (!m_dspec) initDouble();
+        const int hs1 = m_size/2+1;
+        packReal(realIn);
+        vDSP_fft_zriptD(m_dspec, m_dpacked, 1, m_dbuf, m_order, FFT_FORWARD);
+        ddenyq();
+        // vDSP forward FFTs are scaled 2x (for some reason)
+        for (int i = 0; i < hs1; ++i) m_dpacked->realp[i] /= 2.0;
+        for (int i = 0; i < hs1; ++i) m_dpacked->imagp[i] /= 2.0;
+        v_cartesian_to_polar(magOut, phaseOut,
+                             m_dpacked->realp, m_dpacked->imagp, hs1);
+    }
+
+    void forwardMagnitude(const double *BQ_R__ realIn, double *BQ_R__ magOut) {
+        if (!m_dspec) initDouble();
+        packReal(realIn);
+        vDSP_fft_zriptD(m_dspec, m_dpacked, 1, m_dbuf, m_order, FFT_FORWARD);
+        ddenyq();
+        const int hs1 = m_size/2+1;
+        vDSP_zvmagsD(m_dpacked, 1, m_dspare, 1, hs1);
+        vvsqrt(m_dspare2, m_dspare, &hs1);
+        // vDSP forward FFTs are scaled 2x (for some reason)
+        double two = 2.0;
+        vDSP_vsdivD(m_dspare2, 1, &two, magOut, 1, hs1);
+    }
+
+    void forward(const float *BQ_R__ realIn, float *BQ_R__ realOut, float *BQ_R__ imagOut) {
+        if (!m_fspec) initFloat();
+        packReal(realIn);
+        vDSP_fft_zript(m_fspec, m_fpacked, 1, m_fbuf, m_order, FFT_FORWARD);
+        fdenyq();
+        unpackComplex(realOut, imagOut);
+    }
+
+    void forwardInterleaved(const float *BQ_R__ realIn, float *BQ_R__ complexOut) {
+        if (!m_fspec) initFloat();
+        packReal(realIn);
+        vDSP_fft_zript(m_fspec, m_fpacked, 1, m_fbuf, m_order, FFT_FORWARD);
+        fdenyq();
+        unpackComplex(complexOut);
+    }
+
+    void forwardPolar(const float *BQ_R__ realIn, float *BQ_R__ magOut, float *BQ_R__ phaseOut) {
+        if (!m_fspec) initFloat();
+        const int hs1 = m_size/2+1;
+        packReal(realIn);
+        vDSP_fft_zript(m_fspec, m_fpacked, 1, m_fbuf, m_order, FFT_FORWARD);
+        fdenyq();
+        // vDSP forward FFTs are scaled 2x (for some reason)
+        for (int i = 0; i < hs1; ++i) m_fpacked->realp[i] /= 2.f;
+        for (int i = 0; i < hs1; ++i) m_fpacked->imagp[i] /= 2.f;
+        v_cartesian_to_polar(magOut, phaseOut,
+                             m_fpacked->realp, m_fpacked->imagp, hs1);
+    }
+
+    void forwardMagnitude(const float *BQ_R__ realIn, float *BQ_R__ magOut) {
+        if (!m_fspec) initFloat();
+        packReal(realIn);
+        vDSP_fft_zript(m_fspec, m_fpacked, 1, m_fbuf, m_order, FFT_FORWARD);
+        fdenyq();
+        const int hs1 = m_size/2 + 1;
+        vDSP_zvmags(m_fpacked, 1, m_fspare, 1, hs1);
+        vvsqrtf(m_fspare2, m_fspare, &hs1);
+        // vDSP forward FFTs are scaled 2x (for some reason)
+        float two = 2.f;
+        vDSP_vsdiv(m_fspare2, 1, &two, magOut, 1, hs1);
+    }
+
+    void inverse(const double *BQ_R__ realIn, const double *BQ_R__ imagIn, double *BQ_R__ realOut) {
+        if (!m_dspec) initDouble();
+        packComplex(realIn, imagIn);
+        vDSP_fft_zriptD(m_dspec, m_dpacked, 1, m_dbuf, m_order, FFT_INVERSE);
+        unpackReal(realOut);
+    }
+
+    void inverseInterleaved(const double *BQ_R__ complexIn, double *BQ_R__ realOut) {
+        if (!m_dspec) initDouble();
+        double *d[2] = { m_dpacked->realp, m_dpacked->imagp };
+        v_deinterleave(d, complexIn, 2, m_size/2 + 1);
+        vDSP_fft_zriptD(m_dspec, m_dpacked, 1, m_dbuf, m_order, FFT_INVERSE);
+        unpackReal(realOut);
+    }
+
+    void inversePolar(const double *BQ_R__ magIn, const double *BQ_R__ phaseIn, double *BQ_R__ realOut) {
+        if (!m_dspec) initDouble();
+        const int hs1 = m_size/2+1;
+        vvsincos(m_dpacked->imagp, m_dpacked->realp, phaseIn, &hs1);
+        double *const rp = m_dpacked->realp;
+        double *const ip = m_dpacked->imagp;
+        for (int i = 0; i < hs1; ++i) rp[i] *= magIn[i];
+        for (int i = 0; i < hs1; ++i) ip[i] *= magIn[i];
+        dnyq();
+        vDSP_fft_zriptD(m_dspec, m_dpacked, 1, m_dbuf, m_order, FFT_INVERSE);
+        unpackReal(realOut);
+    }
+
+    void inverseCepstral(const double *BQ_R__ magIn, double *BQ_R__ cepOut) {
+        if (!m_dspec) initDouble();
+        const int hs1 = m_size/2 + 1;
+        v_copy(m_dspare, magIn, hs1);
+        for (int i = 0; i < hs1; ++i) m_dspare[i] += 0.000001;
+        vvlog(m_dspare2, m_dspare, &hs1);
+        inverse(m_dspare2, 0, cepOut);
+    }
+    
+    void inverse(const float *BQ_R__ realIn, const float *BQ_R__ imagIn, float *BQ_R__ realOut) {
+        if (!m_fspec) initFloat();
+        packComplex(realIn, imagIn);
+        vDSP_fft_zript(m_fspec, m_fpacked, 1, m_fbuf, m_order, FFT_INVERSE);
+        unpackReal(realOut);
+    }
+
+    void inverseInterleaved(const float *BQ_R__ complexIn, float *BQ_R__ realOut) {
+        if (!m_fspec) initFloat();
+        float *f[2] = { m_fpacked->realp, m_fpacked->imagp };
+        v_deinterleave(f, complexIn, 2, m_size/2 + 1);
+        vDSP_fft_zript(m_fspec, m_fpacked, 1, m_fbuf, m_order, FFT_INVERSE);
+        unpackReal(realOut);
+    }
+
+    void inversePolar(const float *BQ_R__ magIn, const float *BQ_R__ phaseIn, float *BQ_R__ realOut) {
+        if (!m_fspec) initFloat();
+
+        const int hs1 = m_size/2+1;
+        vvsincosf(m_fpacked->imagp, m_fpacked->realp, phaseIn, &hs1);
+        float *const rp = m_fpacked->realp;
+        float *const ip = m_fpacked->imagp;
+        for (int i = 0; i < hs1; ++i) rp[i] *= magIn[i];
+        for (int i = 0; i < hs1; ++i) ip[i] *= magIn[i];
+        fnyq();
+        vDSP_fft_zript(m_fspec, m_fpacked, 1, m_fbuf, m_order, FFT_INVERSE);
+        unpackReal(realOut);
+    }
+
+    void inverseCepstral(const float *BQ_R__ magIn, float *BQ_R__ cepOut) {
+        if (!m_fspec) initFloat();
+        const int hs1 = m_size/2 + 1;
+        v_copy(m_fspare, magIn, hs1);
+        for (int i = 0; i < hs1; ++i) m_fspare[i] += 0.000001f;
+        vvlogf(m_fspare2, m_fspare, &hs1);
+        inverse(m_fspare2, 0, cepOut);
+    }
+
+private:
+    const int m_size;
+    int m_order;
+    FFTSetup m_fspec;
+    FFTSetupD m_dspec;
+    DSPSplitComplex *m_fbuf;
+    DSPDoubleSplitComplex *m_dbuf;
+    DSPSplitComplex *m_fpacked;
+    float *m_fspare;
+    float *m_fspare2;
+    DSPDoubleSplitComplex *m_dpacked;
+    double *m_dspare;
+    double *m_dspare2;
+};
+
+#endif /* HAVE_VDSP */
+
+#ifdef HAVE_MEDIALIB
+
+class D_MEDIALIB : public FFTImpl
+{
+public:
+    D_MEDIALIB(int size) :
+        m_size(size),
+        m_dpacked(0), m_fpacked(0)
+    { 
+        for (int i = 0; ; ++i) {
+            if (m_size & (1 << i)) {
+                m_order = i;
+                break;
+            }
+        }
+    }
+
+    ~D_MEDIALIB() {
+        if (m_dpacked) {
+            deallocate(m_dpacked);
+        }
+        if (m_fpacked) {
+            deallocate(m_fpacked);
+        }
+    }
+
+    FFT::Precisions
+    getSupportedPrecisions() const {
+        return FFT::SinglePrecision | FFT::DoublePrecision;
+    }
+
+    //!!! rv check
+
+    void initFloat() {
+        m_fpacked = allocate<float>(m_size*2);
+    }
+
+    void initDouble() {
+        m_dpacked = allocate<double>(m_size*2);
+    }
+
+    void packFloatConjugates() {
+        const int hs = m_size / 2;
+        for (int i = 1; i <= hs; ++i) {
+            m_fpacked[(m_size-i)*2] = m_fpacked[2*i];
+            m_fpacked[(m_size-i)*2 + 1] = -m_fpacked[2*i + 1];
+        }
+    }
+
+    void packDoubleConjugates() {
+        const int hs = m_size / 2;
+        for (int i = 1; i <= hs; ++i) {
+            m_dpacked[(m_size-i)*2] = m_dpacked[2*i];
+            m_dpacked[(m_size-i)*2 + 1] = -m_dpacked[2*i + 1];
+        }
+    }
+
+    void packFloat(const float *BQ_R__ re, const float *BQ_R__ im) {
+        int index = 0;
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            m_fpacked[index++] = re[i];
+            index++;
+        }
+        index = 0;
+        if (im) {
+            for (int i = 0; i <= hs; ++i) {
+                index++;
+                m_fpacked[index++] = im[i];
+            }
+        } else {
+            for (int i = 0; i <= hs; ++i) {
+                index++;
+                m_fpacked[index++] = 0.f;
+            }
+        }
+        packFloatConjugates();
+    }
+
+    void packDouble(const double *BQ_R__ re, const double *BQ_R__ im) {
+        int index = 0;
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            m_dpacked[index++] = re[i];
+            index++;
+        }
+        index = 0;
+        if (im) {
+            for (int i = 0; i <= hs; ++i) {
+                index++;
+                m_dpacked[index++] = im[i];
+            }
+        } else {
+            for (int i = 0; i <= hs; ++i) {
+                index++;
+                m_dpacked[index++] = 0.0;
+            }
+        }
+        packDoubleConjugates();
+    }
+
+    void unpackFloat(float *re, float *BQ_R__ im) { // re may be equal to m_fpacked
+        int index = 0;
+        const int hs = m_size/2;
+        if (im) {
+            for (int i = 0; i <= hs; ++i) {
+                index++;
+                im[i] = m_fpacked[index++];
+            }
+        }
+        index = 0;
+        for (int i = 0; i <= hs; ++i) {
+            re[i] = m_fpacked[index++];
+            index++;
+        }
+    }        
+
+    void unpackDouble(double *re, double *BQ_R__ im) { // re may be equal to m_dpacked
+        int index = 0;
+        const int hs = m_size/2;
+        if (im) {
+            for (int i = 0; i <= hs; ++i) {
+                index++;
+                im[i] = m_dpacked[index++];
+            }
+        }
+        index = 0;
+        for (int i = 0; i <= hs; ++i) {
+            re[i] = m_dpacked[index++];
+            index++;
+        }
+    }
+
+    void forward(const double *BQ_R__ realIn, double *BQ_R__ realOut, double *BQ_R__ imagOut) {
+        if (!m_dpacked) initDouble();
+        mlib_SignalFFT_1_D64C_D64(m_dpacked, realIn, m_order);
+        unpackDouble(realOut, imagOut);
+    }
+
+    void forwardInterleaved(const double *BQ_R__ realIn, double *BQ_R__ complexOut) {
+        if (!m_dpacked) initDouble();
+        // mlib FFT gives the whole redundant complex result
+        mlib_SignalFFT_1_D64C_D64(m_dpacked, realIn, m_order);
+        v_copy(complexOut, m_dpacked, m_size + 2);
+    }
+
+    void forwardPolar(const double *BQ_R__ realIn, double *BQ_R__ magOut, double *BQ_R__ phaseOut) {
+        if (!m_dpacked) initDouble();
+        mlib_SignalFFT_1_D64C_D64(m_dpacked, realIn, m_order);
+        const int hs = m_size/2;
+        int index = 0;
+        for (int i = 0; i <= hs; ++i) {
+            int reali = index;
+            ++index;
+            magOut[i] = sqrt(m_dpacked[reali] * m_dpacked[reali] +
+                             m_dpacked[index] * m_dpacked[index]);
+            phaseOut[i] = atan2(m_dpacked[index], m_dpacked[reali]) ;
+            ++index;
+        }
+    }
+
+    void forwardMagnitude(const double *BQ_R__ realIn, double *BQ_R__ magOut) {
+        if (!m_dpacked) initDouble();
+        mlib_SignalFFT_1_D64C_D64(m_dpacked, realIn, m_order);
+        const int hs = m_size/2;
+        int index = 0;
+        for (int i = 0; i <= hs; ++i) {
+            int reali = index;
+            ++index;
+            magOut[i] = sqrt(m_dpacked[reali] * m_dpacked[reali] +
+                             m_dpacked[index] * m_dpacked[index]);
+            ++index;
+        }
+    }
+
+    void forward(const float *BQ_R__ realIn, float *BQ_R__ realOut, float *BQ_R__ imagOut) {
+        if (!m_fpacked) initFloat();
+        mlib_SignalFFT_1_F32C_F32(m_fpacked, realIn, m_order);
+        unpackFloat(realOut, imagOut);
+    }
+
+    void forwardInterleaved(const float *BQ_R__ realIn, float *BQ_R__ complexOut) {
+        if (!m_fpacked) initFloat();
+        // mlib FFT gives the whole redundant complex result
+        mlib_SignalFFT_1_F32C_F32(m_fpacked, realIn, m_order);
+        v_copy(complexOut, m_fpacked, m_size + 2);
+    }
+
+    void forwardPolar(const float *BQ_R__ realIn, float *BQ_R__ magOut, float *BQ_R__ phaseOut) {
+        if (!m_fpacked) initFloat();
+        mlib_SignalFFT_1_F32C_F32(m_fpacked, realIn, m_order);
+        const int hs = m_size/2;
+        int index = 0;
+        for (int i = 0; i <= hs; ++i) {
+            int reali = index;
+            ++index;
+            magOut[i] = sqrtf(m_fpacked[reali] * m_fpacked[reali] +
+                              m_fpacked[index] * m_fpacked[index]);
+            phaseOut[i] = atan2f(m_fpacked[index], m_fpacked[reali]);
+            ++index;
+        }
+    }
+
+    void forwardMagnitude(const float *BQ_R__ realIn, float *BQ_R__ magOut) {
+        if (!m_fpacked) initFloat();
+        mlib_SignalFFT_1_F32C_F32(m_fpacked, realIn, m_order);
+        const int hs = m_size/2;
+        int index = 0;
+        for (int i = 0; i <= hs; ++i) {
+            int reali = index;
+            ++index;
+            magOut[i] = sqrtf(m_fpacked[reali] * m_fpacked[reali] +
+                              m_fpacked[index] * m_fpacked[index]);
+            ++index;
+        }
+    }
+
+    void inverse(const double *BQ_R__ realIn, const double *BQ_R__ imagIn, double *BQ_R__ realOut) {
+        if (!m_dpacked) initDouble();
+        packDouble(realIn, imagIn);
+        mlib_SignalIFFT_2_D64_D64C(realOut, m_dpacked, m_order);
+    }
+
+    void inverseInterleaved(const double *BQ_R__ complexIn, double *BQ_R__ realOut) {
+        if (!m_dpacked) initDouble();
+        v_copy(m_dpacked, complexIn, m_size + 2);
+        packDoubleConjugates();
+        mlib_SignalIFFT_2_D64_D64C(realOut, m_dpacked, m_order);
+    }
+
+    void inversePolar(const double *BQ_R__ magIn, const double *BQ_R__ phaseIn, double *BQ_R__ realOut) {
+        if (!m_dpacked) initDouble();
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            double real = magIn[i] * cos(phaseIn[i]);
+            double imag = magIn[i] * sin(phaseIn[i]);
+            m_dpacked[i*2] = real;
+            m_dpacked[i*2 + 1] = imag;
+        }
+        packDoubleConjugates();
+        mlib_SignalIFFT_2_D64_D64C(realOut, m_dpacked, m_order);
+    }
+
+    void inverseCepstral(const double *BQ_R__ magIn, double *BQ_R__ cepOut) {
+        if (!m_dpacked) initDouble();
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            m_dpacked[i*2] = log(magIn[i] + 0.000001);
+            m_dpacked[i*2 + 1] = 0.0;
+        }
+        packDoubleConjugates();
+        mlib_SignalIFFT_2_D64_D64C(cepOut, m_dpacked, m_order);
+    }
+    
+    void inverse(const float *BQ_R__ realIn, const float *BQ_R__ imagIn, float *BQ_R__ realOut) {
+        if (!m_fpacked) initFloat();
+        packFloat(realIn, imagIn);
+        mlib_SignalIFFT_2_F32_F32C(realOut, m_fpacked, m_order);
+    }
+    
+    void inverseInterleaved(const float *BQ_R__ complexIn, float *BQ_R__ realOut) {
+        if (!m_fpacked) initFloat();
+        v_convert(m_fpacked, complexIn, m_size + 2);
+        packFloatConjugates();
+        mlib_SignalIFFT_2_F32_F32C(realOut, m_fpacked, m_order);
+    }
+
+    void inversePolar(const float *BQ_R__ magIn, const float *BQ_R__ phaseIn, float *BQ_R__ realOut) {
+        if (!m_fpacked) initFloat();
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            double real = magIn[i] * cos(phaseIn[i]);
+            double imag = magIn[i] * sin(phaseIn[i]);
+            m_fpacked[i*2] = real;
+            m_fpacked[i*2 + 1] = imag;
+        }
+        packFloatConjugates();
+        mlib_SignalIFFT_2_F32_F32C(realOut, m_fpacked, m_order);
+    }
+
+    void inverseCepstral(const float *BQ_R__ magIn, float *BQ_R__ cepOut) {
+        if (!m_fpacked) initFloat();
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            m_fpacked[i*2] = logf(magIn[i] + 0.000001);
+            m_fpacked[i*2 + 1] = 0.f;
+        }
+        packFloatConjugates();
+        mlib_SignalIFFT_2_F32_F32C(cepOut, m_fpacked, m_order);
+    }
+
+private:
+    const int m_size;
+    int m_order;
+    double *m_dpacked;
+    float *m_fpacked;
+};
+
+#endif /* HAVE_MEDIALIB */
+
+#ifdef HAVE_OPENMAX
+
+class D_OPENMAX : public FFTImpl
+{
+    // Convert a signed 32-bit integer to a float in the range [-1,1)
+    static inline float i2f(OMX_S32 i)
+    {
+        return float(i) / float(OMX_MAX_S32);
+    }
+
+    // Convert a signed 32-bit integer to a double in the range [-1,1)
+    static inline double i2d(OMX_S32 i)
+    {
+        return double(i) / double(OMX_MAX_S32);
+    }
+
+    // Convert a float in the range [-1,1) to a signed 32-bit integer
+    static inline OMX_S32 f2i(float f)
+    {
+        return OMX_S32(f * OMX_MAX_S32);
+    }
+
+    // Convert a double in the range [-1,1) to a signed 32-bit integer
+    static inline OMX_S32 d2i(double d)
+    {
+        return OMX_S32(d * OMX_MAX_S32);
+    }
+
+public:
+    D_OPENMAX(int size) :
+        m_size(size),
+        m_packed(0)
+    { 
+        for (int i = 0; ; ++i) {
+            if (m_size & (1 << i)) {
+                m_order = i;
+                break;
+            }
+        }
+    }
+
+    ~D_OPENMAX() {
+        if (m_packed) {
+            deallocate(m_packed);
+            deallocate(m_buf);
+            deallocate(m_fbuf);
+            deallocate(m_spec);
+        }
+    }
+
+    FFT::Precisions
+    getSupportedPrecisions() const {
+        return FFT::SinglePrecision;
+    }
+
+    //!!! rv check
+
+    // The OpenMAX implementation uses a fixed-point representation in
+    // 32-bit signed integers, with a downward scaling factor (0-32
+    // bits) supplied as an argument to the FFT function.
+
+    void initFloat() {
+        initDouble();
+    }
+
+    void initDouble() {
+        if (!m_packed) {
+            m_buf = allocate<OMX_S32>(m_size);
+            m_packed = allocate<OMX_S32>(m_size*2 + 2);
+            m_fbuf = allocate<float>(m_size*2 + 2);
+            OMX_INT sz = 0;
+            omxSP_FFTGetBufSize_R_S32(m_order, &sz);
+            m_spec = (OMXFFTSpec_R_S32 *)allocate<char>(sz);
+            omxSP_FFTInit_R_S32(m_spec, m_order);
+        }
+    }
+
+    void packFloat(const float *BQ_R__ re) {
+        // prepare fixed point input for forward transform
+        for (int i = 0; i < m_size; ++i) {
+            m_buf[i] = f2i(re[i]);
+        }
+    }
+
+    void packDouble(const double *BQ_R__ re) {
+        // prepare fixed point input for forward transform
+        for (int i = 0; i < m_size; ++i) {
+            m_buf[i] = d2i(re[i]);
+        }
+    }
+
+    void unpackFloat(float *BQ_R__ re, float *BQ_R__ im) {
+        // convert fixed point output for forward transform
+        int index = 0;
+        const int hs = m_size/2;
+        if (im) {
+            for (int i = 0; i <= hs; ++i) {
+                index++;
+                im[i] = i2f(m_packed[index++]);
+            }
+            v_scale(im, m_size, hs + 1);
+        }
+        index = 0;
+        for (int i = 0; i <= hs; ++i) {
+            re[i] = i2f(m_packed[index++]);
+            index++;
+        }
+        v_scale(re, m_size, hs + 1);
+    }        
+
+    void unpackDouble(double *BQ_R__ re, double *BQ_R__ im) {
+        // convert fixed point output for forward transform
+        int index = 0;
+        const int hs = m_size/2;
+        if (im) {
+            for (int i = 0; i <= hs; ++i) {
+                index++;
+                im[i] = i2d(m_packed[index++]);
+            }
+            v_scale(im, m_size, hs + 1);
+        }
+        index = 0;
+        for (int i = 0; i <= hs; ++i) {
+            re[i] = i2d(m_packed[index++]);
+            index++;
+        }
+        v_scale(re, m_size, hs + 1);
+    }
+
+    void unpackFloatInterleaved(float *BQ_R__ cplx) {
+        // convert fixed point output for forward transform
+        for (int i = 0; i < m_size + 2; ++i) {
+            cplx[i] = i2f(m_packed[i]);
+        }            
+        v_scale(cplx, m_size, m_size + 2);
+    }
+
+    void unpackDoubleInterleaved(double *BQ_R__ cplx) {
+        // convert fixed point output for forward transform
+        for (int i = 0; i < m_size + 2; ++i) {
+            cplx[i] = i2d(m_packed[i]);
+        }            
+        v_scale(cplx, m_size, m_size + 2);
+    }
+
+    void packFloat(const float *BQ_R__ re, const float *BQ_R__ im) {
+        // prepare fixed point input for inverse transform
+        int index = 0;
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            m_packed[index++] = f2i(re[i]);
+            index++;
+        }
+        index = 0;
+        if (im) {
+            for (int i = 0; i <= hs; ++i) {
+                index++;
+                m_packed[index++] = f2i(im[i]);
+            }
+        } else {
+            for (int i = 0; i <= hs; ++i) {
+                index++;
+                m_packed[index++] = 0;
+            }
+        }
+    }
+
+    void packDouble(const double *BQ_R__ re, const double *BQ_R__ im) {
+        // prepare fixed point input for inverse transform
+        int index = 0;
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            m_packed[index++] = d2i(re[i]);
+            index++;
+        }
+        index = 0;
+        if (im) {
+            for (int i = 0; i <= hs; ++i) {
+                index++;
+                m_packed[index++] = d2i(im[i]);
+            }
+        } else {
+            for (int i = 0; i <= hs; ++i) {
+                index++;
+                m_packed[index++] = 0;
+            }
+        }
+    }
+
+    void convertFloat(const float *BQ_R__ f) {
+        // convert interleaved input for inverse interleaved transform
+        const int n = m_size + 2;
+        for (int i = 0; i < n; ++i) {
+            m_packed[i] = f2i(f[i]);
+        }
+    }        
+
+    void convertDouble(const double *BQ_R__ d) {
+        // convert interleaved input for inverse interleaved transform
+        const int n = m_size + 2;
+        for (int i = 0; i < n; ++i) {
+            m_packed[i] = d2i(d[i]);
+        }
+    }        
+
+    void unpackFloat(float *BQ_R__ re) {
+        // convert fixed point output for inverse transform
+        for (int i = 0; i < m_size; ++i) {
+            re[i] = i2f(m_buf[i]) * m_size;
+        }
+    }
+
+    void unpackDouble(double *BQ_R__ re) {
+        // convert fixed point output for inverse transform
+        for (int i = 0; i < m_size; ++i) {
+            re[i] = i2d(m_buf[i]) * m_size;
+        }
+    }
+
+    void forward(const double *BQ_R__ realIn, double *BQ_R__ realOut, double *BQ_R__ imagOut) {
+        if (!m_packed) initDouble();
+        packDouble(realIn);
+        omxSP_FFTFwd_RToCCS_S32_Sfs(m_buf, m_packed, m_spec, m_order);
+        unpackDouble(realOut, imagOut);
+    }
+    
+    void forwardInterleaved(const double *BQ_R__ realIn, double *BQ_R__ complexOut) {
+        if (!m_packed) initDouble();
+        packDouble(realIn);
+        omxSP_FFTFwd_RToCCS_S32_Sfs(m_buf, m_packed, m_spec, m_order);
+        unpackDoubleInterleaved(complexOut);
+    }
+
+    void forwardPolar(const double *BQ_R__ realIn, double *BQ_R__ magOut, double *BQ_R__ phaseOut) {
+        if (!m_packed) initDouble();
+        packDouble(realIn);
+        omxSP_FFTFwd_RToCCS_S32_Sfs(m_buf, m_packed, m_spec, m_order);
+        unpackDouble(magOut, phaseOut); // temporarily
+        // at this point we actually have real/imag in the mag/phase arrays
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            double real = magOut[i];
+            double imag = phaseOut[i];
+            c_magphase(magOut + i, phaseOut + i, real, imag);
+        }
+    }
+
+    void forwardMagnitude(const double *BQ_R__ realIn, double *BQ_R__ magOut) {
+        if (!m_packed) initDouble();
+        packDouble(realIn);
+        omxSP_FFTFwd_RToCCS_S32_Sfs(m_buf, m_packed, m_spec, m_order);
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            int reali = i * 2;
+            int imagi = reali + 1;
+            double real = i2d(m_packed[reali]) * m_size;
+            double imag = i2d(m_packed[imagi]) * m_size;
+            magOut[i] = sqrt(real * real + imag * imag);
+        }
+    }
+
+    void forward(const float *BQ_R__ realIn, float *BQ_R__ realOut, float *BQ_R__ imagOut) {
+        if (!m_packed) initFloat();
+        packFloat(realIn);
+        omxSP_FFTFwd_RToCCS_S32_Sfs(m_buf, m_packed, m_spec, m_order);
+        unpackFloat(realOut, imagOut);
+    }
+
+    void forwardInterleaved(const float *BQ_R__ realIn, float *BQ_R__ complexOut) {
+        if (!m_packed) initFloat();
+        packFloat(realIn);
+        omxSP_FFTFwd_RToCCS_S32_Sfs(m_buf, m_packed, m_spec, m_order);
+        unpackFloatInterleaved(complexOut);
+    }
+
+    void forwardPolar(const float *BQ_R__ realIn, float *BQ_R__ magOut, float *BQ_R__ phaseOut) {
+        if (!m_packed) initFloat();
+
+        packFloat(realIn);
+        omxSP_FFTFwd_RToCCS_S32_Sfs(m_buf, m_packed, m_spec, m_order);
+        unpackFloat(magOut, phaseOut); // temporarily
+        // at this point we actually have real/imag in the mag/phase arrays
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            float real = magOut[i];
+            float imag = phaseOut[i];
+            c_magphase(magOut + i, phaseOut + i, real, imag);
+        }
+    }
+
+    void forwardMagnitude(const float *BQ_R__ realIn, float *BQ_R__ magOut) {
+        if (!m_packed) initFloat();
+        packFloat(realIn);
+        omxSP_FFTFwd_RToCCS_S32_Sfs(m_buf, m_packed, m_spec, m_order);
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            int reali = i * 2;
+            int imagi = reali + 1;
+            float real = i2f(m_packed[reali]) * m_size;
+            float imag = i2f(m_packed[imagi]) * m_size;
+            magOut[i] = sqrtf(real * real + imag * imag);
+        }
+    }
+
+    void inverse(const double *BQ_R__ realIn, const double *BQ_R__ imagIn, double *BQ_R__ realOut) {
+        if (!m_packed) initDouble();
+        packDouble(realIn, imagIn);
+        omxSP_FFTInv_CCSToR_S32_Sfs(m_packed, m_buf, m_spec, 0);
+        unpackDouble(realOut);
+    }
+
+    void inverseInterleaved(const double *BQ_R__ complexIn, double *BQ_R__ realOut) {
+        if (!m_packed) initDouble();
+        convertDouble(complexIn);
+        omxSP_FFTInv_CCSToR_S32_Sfs(m_packed, m_buf, m_spec, 0);
+        unpackDouble(realOut);
+    }
+
+    void inversePolar(const double *BQ_R__ magIn, const double *BQ_R__ phaseIn, double *BQ_R__ realOut) {
+        if (!m_packed) initDouble();
+        int index = 0;
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            double real, imag;
+            c_phasor(&real, &imag, phaseIn[i]);
+            m_fbuf[index++] = float(real);
+            m_fbuf[index++] = float(imag);
+        }
+        convertFloat(m_fbuf);
+        omxSP_FFTInv_CCSToR_S32_Sfs(m_packed, m_buf, m_spec, 0);
+        unpackDouble(realOut);
+    }
+
+    void inverseCepstral(const double *BQ_R__ magIn, double *BQ_R__ cepOut) {
+        if (!m_packed) initDouble();
+        //!!! implement
+#warning OpenMAX implementation lacks cepstral transforms
+    }
+    
+    void inverse(const float *BQ_R__ realIn, const float *BQ_R__ imagIn, float *BQ_R__ realOut) {
+        if (!m_packed) initFloat();
+        packFloat(realIn, imagIn);
+        omxSP_FFTInv_CCSToR_S32_Sfs(m_packed, m_buf, m_spec, 0);
+        unpackFloat(realOut);
+    }
+
+    void inverseInterleaved(const float *BQ_R__ complexIn, float *BQ_R__ realOut) {
+        if (!m_packed) initFloat();
+        convertFloat(complexIn);
+        omxSP_FFTInv_CCSToR_S32_Sfs(m_packed, m_buf, m_spec, 0);
+        unpackFloat(realOut);
+    }
+
+    void inversePolar(const float *BQ_R__ magIn, const float *BQ_R__ phaseIn, float *BQ_R__ realOut) {
+        if (!m_packed) initFloat();
+        const int hs = m_size/2;
+        v_polar_to_cartesian_interleaved(m_fbuf, magIn, phaseIn, hs+1);
+        convertFloat(m_fbuf);
+        omxSP_FFTInv_CCSToR_S32_Sfs(m_packed, m_buf, m_spec, 0);
+        unpackFloat(realOut);
+    }
+
+    void inverseCepstral(const float *BQ_R__ magIn, float *BQ_R__ cepOut) {
+        if (!m_packed) initFloat();
+        //!!! implement
+#warning OpenMAX implementation lacks cepstral transforms
+    }
+
+private:
+    const int m_size;
+    int m_order;
+    OMX_S32 *m_packed;
+    OMX_S32 *m_buf;
+    float *m_fbuf;
+    OMXFFTSpec_R_S32 *m_spec;
+
+};
+
+#endif /* HAVE_OPENMAX */
+
+#ifdef HAVE_FFTW3
+
+/*
+ Define FFTW_DOUBLE_ONLY to make all uses of FFTW functions be
+ double-precision (so "float" FFTs are calculated by casting to
+ doubles and using the double-precision FFTW function).
+
+ Define FFTW_SINGLE_ONLY to make all uses of FFTW functions be
+ single-precision (so "double" FFTs are calculated by casting to
+ floats and using the single-precision FFTW function).
+
+ Neither of these flags is desirable for either performance or
+ precision. The main reason to define either flag is to avoid linking
+ against both fftw3 and fftw3f libraries.
+*/
+
+//#define FFTW_DOUBLE_ONLY 1
+//#define FFTW_SINGLE_ONLY 1
+
+#if defined(FFTW_DOUBLE_ONLY) && defined(FFTW_SINGLE_ONLY)
+// Can't meaningfully define both
+#error Can only define one of FFTW_DOUBLE_ONLY and FFTW_SINGLE_ONLY
+#endif
+
+#if defined(FFTW_FLOAT_ONLY)
+#warning FFTW_FLOAT_ONLY is deprecated, use FFTW_SINGLE_ONLY instead
+#define FFTW_SINGLE_ONLY 1
+#endif
+
+#ifdef FFTW_DOUBLE_ONLY
+#define fft_float_type double
+#define fftwf_complex fftw_complex
+#define fftwf_plan fftw_plan
+#define fftwf_plan_dft_r2c_1d fftw_plan_dft_r2c_1d
+#define fftwf_plan_dft_c2r_1d fftw_plan_dft_c2r_1d
+#define fftwf_destroy_plan fftw_destroy_plan
+#define fftwf_malloc fftw_malloc
+#define fftwf_free fftw_free
+#define fftwf_execute fftw_execute
+#define atan2f atan2
+#define sqrtf sqrt
+#define cosf cos
+#define sinf sin
+#else
+#define fft_float_type float
+#endif /* FFTW_DOUBLE_ONLY */
+
+#ifdef FFTW_SINGLE_ONLY
+#define fft_double_type float
+#define fftw_complex fftwf_complex
+#define fftw_plan fftwf_plan
+#define fftw_plan_dft_r2c_1d fftwf_plan_dft_r2c_1d
+#define fftw_plan_dft_c2r_1d fftwf_plan_dft_c2r_1d
+#define fftw_destroy_plan fftwf_destroy_plan
+#define fftw_malloc fftwf_malloc
+#define fftw_free fftwf_free
+#define fftw_execute fftwf_execute
+#define atan2 atan2f
+#define sqrt sqrtf
+#define cos cosf
+#define sin sinf
+#else
+#define fft_double_type double
+#endif /* FFTW_SINGLE_ONLY */
+
+class D_FFTW : public FFTImpl
+{
+public:
+    D_FFTW(int size) :
+        m_fplanf(0), m_dplanf(0), m_size(size)
+    {
+        initMutex();
+    }
+
+    ~D_FFTW() {
+        if (m_fplanf) {
+            lock();
+            bool save = false;
+            if (m_extantf > 0 && --m_extantf == 0) save = true;
+#ifndef FFTW_DOUBLE_ONLY
+            if (save) saveWisdom('f');
+#endif
+            fftwf_destroy_plan(m_fplanf);
+            fftwf_destroy_plan(m_fplani);
+            fftwf_free(m_fbuf);
+            fftwf_free(m_fpacked);
+            unlock();
+        }
+        if (m_dplanf) {
+            lock();
+            bool save = false;
+            if (m_extantd > 0 && --m_extantd == 0) save = true;
+#ifndef FFTW_SINGLE_ONLY
+            if (save) saveWisdom('d');
+#endif
+            fftw_destroy_plan(m_dplanf);
+            fftw_destroy_plan(m_dplani);
+            fftw_free(m_dbuf);
+            fftw_free(m_dpacked);
+            unlock();
+        }
+        destroyMutex();
+    }
+
+    FFT::Precisions
+    getSupportedPrecisions() const {
+#ifdef FFTW_SINGLE_ONLY
+        return FFT::SinglePrecision;
+#else
+#ifdef FFTW_DOUBLE_ONLY
+        return FFT::DoublePrecision;
+#else
+        return FFT::SinglePrecision | FFT::DoublePrecision;
+#endif
+#endif
+    }
+
+    void initFloat() {
+        if (m_fplanf) return;
+        bool load = false;
+        lock();
+        if (m_extantf++ == 0) load = true;
+#ifdef FFTW_DOUBLE_ONLY
+        if (load) loadWisdom('d');
+#else
+        if (load) loadWisdom('f');
+#endif
+        m_fbuf = (fft_float_type *)fftw_malloc(m_size * sizeof(fft_float_type));
+        m_fpacked = (fftwf_complex *)fftw_malloc
+            ((m_size/2 + 1) * sizeof(fftwf_complex));
+        m_fplanf = fftwf_plan_dft_r2c_1d
+            (m_size, m_fbuf, m_fpacked, FFTW_MEASURE);
+        m_fplani = fftwf_plan_dft_c2r_1d
+            (m_size, m_fpacked, m_fbuf, FFTW_MEASURE);
+        unlock();
+    }
+
+    void initDouble() {
+        if (m_dplanf) return;
+        bool load = false;
+        lock();
+        if (m_extantd++ == 0) load = true;
+#ifdef FFTW_SINGLE_ONLY
+        if (load) loadWisdom('f');
+#else
+        if (load) loadWisdom('d');
+#endif
+        m_dbuf = (fft_double_type *)fftw_malloc(m_size * sizeof(fft_double_type));
+        m_dpacked = (fftw_complex *)fftw_malloc
+            ((m_size/2 + 1) * sizeof(fftw_complex));
+        m_dplanf = fftw_plan_dft_r2c_1d
+            (m_size, m_dbuf, m_dpacked, FFTW_MEASURE);
+        m_dplani = fftw_plan_dft_c2r_1d
+            (m_size, m_dpacked, m_dbuf, FFTW_MEASURE);
+        unlock();
+    }
+
+    void loadWisdom(char type) { wisdom(false, type); }
+    void saveWisdom(char type) { wisdom(true, type); }
+
+    void wisdom(bool save, char type) {
+
+#ifdef FFTW_DOUBLE_ONLY
+        if (type == 'f') return;
+#endif
+#ifdef FFTW_SINGLE_ONLY
+        if (type == 'd') return;
+#endif
+
+        const char *home = getenv("HOME");
+        if (!home) return;
+
+        char fn[256];
+        snprintf(fn, 256, "%s/%s.%c", home, ".turbot.wisdom", type);
+
+        FILE *f = fopen(fn, save ? "wb" : "rb");
+        if (!f) return;
+
+        if (save) {
+            switch (type) {
+#ifdef FFTW_DOUBLE_ONLY
+            case 'f': break;
+#else
+            case 'f': fftwf_export_wisdom_to_file(f); break;
+#endif
+#ifdef FFTW_SINGLE_ONLY
+            case 'd': break;
+#else
+            case 'd': fftw_export_wisdom_to_file(f); break;
+#endif
+            default: break;
+            }
+        } else {
+            switch (type) {
+#ifdef FFTW_DOUBLE_ONLY
+            case 'f': break;
+#else
+            case 'f': fftwf_import_wisdom_from_file(f); break;
+#endif
+#ifdef FFTW_SINGLE_ONLY
+            case 'd': break;
+#else
+            case 'd': fftw_import_wisdom_from_file(f); break;
+#endif
+            default: break;
+            }
+        }
+
+        fclose(f);
+    }
+
+    void packFloat(const float *BQ_R__ re, const float *BQ_R__ im) {
+        const int hs = m_size/2;
+        fftwf_complex *const BQ_R__ fpacked = m_fpacked; 
+        for (int i = 0; i <= hs; ++i) {
+            fpacked[i][0] = re[i];
+        }
+        if (im) {
+            for (int i = 0; i <= hs; ++i) {
+                fpacked[i][1] = im[i];
+            }
+        } else {
+            for (int i = 0; i <= hs; ++i) {
+                fpacked[i][1] = 0.f;
+            }
+        }                
+    }
+
+    void packDouble(const double *BQ_R__ re, const double *BQ_R__ im) {
+        const int hs = m_size/2;
+        fftw_complex *const BQ_R__ dpacked = m_dpacked; 
+        for (int i = 0; i <= hs; ++i) {
+            dpacked[i][0] = re[i];
+        }
+        if (im) {
+            for (int i = 0; i <= hs; ++i) {
+                dpacked[i][1] = im[i];
+            }
+        } else {
+            for (int i = 0; i <= hs; ++i) {
+                dpacked[i][1] = 0.0;
+            }
+        }
+    }
+
+    void unpackFloat(float *BQ_R__ re, float *BQ_R__ im) {
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            re[i] = m_fpacked[i][0];
+        }
+        if (im) {
+            for (int i = 0; i <= hs; ++i) {
+                im[i] = m_fpacked[i][1];
+            }
+        }
+    }        
+
+    void unpackDouble(double *BQ_R__ re, double *BQ_R__ im) {
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            re[i] = m_dpacked[i][0];
+        }
+        if (im) {
+            for (int i = 0; i <= hs; ++i) {
+                im[i] = m_dpacked[i][1];
+            }
+        }
+    }        
+
+    void forward(const double *BQ_R__ realIn, double *BQ_R__ realOut, double *BQ_R__ imagOut) {
+        if (!m_dplanf) initDouble();
+        const int sz = m_size;
+        fft_double_type *const BQ_R__ dbuf = m_dbuf;
+#ifndef FFTW_SINGLE_ONLY
+        if (realIn != dbuf) 
+#endif
+            for (int i = 0; i < sz; ++i) {
+                dbuf[i] = realIn[i];
+            }
+        fftw_execute(m_dplanf);
+        unpackDouble(realOut, imagOut);
+    }
+
+    void forwardInterleaved(const double *BQ_R__ realIn, double *BQ_R__ complexOut) {
+        if (!m_dplanf) initDouble();
+        const int sz = m_size;
+        fft_double_type *const BQ_R__ dbuf = m_dbuf;
+#ifndef FFTW_SINGLE_ONLY
+        if (realIn != dbuf) 
+#endif
+            for (int i = 0; i < sz; ++i) {
+                dbuf[i] = realIn[i];
+            }
+        fftw_execute(m_dplanf);
+        v_convert(complexOut, (fft_double_type *)m_dpacked, sz + 2);
+    }
+
+    void forwardPolar(const double *BQ_R__ realIn, double *BQ_R__ magOut, double *BQ_R__ phaseOut) {
+        if (!m_dplanf) initDouble();
+        fft_double_type *const BQ_R__ dbuf = m_dbuf;
+        const int sz = m_size;
+#ifndef FFTW_SINGLE_ONLY
+        if (realIn != dbuf)
+#endif
+            for (int i = 0; i < sz; ++i) {
+                dbuf[i] = realIn[i];
+            }
+        fftw_execute(m_dplanf);
+        v_cartesian_interleaved_to_polar(magOut, phaseOut,
+                                         (double *)m_dpacked, m_size/2+1);
+    }
+
+    void forwardMagnitude(const double *BQ_R__ realIn, double *BQ_R__ magOut) {
+        if (!m_dplanf) initDouble();
+        fft_double_type *const BQ_R__ dbuf = m_dbuf;
+        const int sz = m_size;
+#ifndef FFTW_SINGLE_ONLY
+        if (realIn != m_dbuf)
+#endif
+            for (int i = 0; i < sz; ++i) {
+                dbuf[i] = realIn[i];
+            }
+        fftw_execute(m_dplanf);
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            magOut[i] = sqrt(m_dpacked[i][0] * m_dpacked[i][0] +
+                             m_dpacked[i][1] * m_dpacked[i][1]);
+        }
+    }
+
+    void forward(const float *BQ_R__ realIn, float *BQ_R__ realOut, float *BQ_R__ imagOut) {
+        if (!m_fplanf) initFloat();
+        fft_float_type *const BQ_R__ fbuf = m_fbuf;
+        const int sz = m_size;
+#ifndef FFTW_DOUBLE_ONLY
+        if (realIn != fbuf)
+#endif
+            for (int i = 0; i < sz; ++i) {
+                fbuf[i] = realIn[i];
+            }
+        fftwf_execute(m_fplanf);
+        unpackFloat(realOut, imagOut);
+    }
+
+    void forwardInterleaved(const float *BQ_R__ realIn, float *BQ_R__ complexOut) {
+        if (!m_fplanf) initFloat();
+        fft_float_type *const BQ_R__ fbuf = m_fbuf;
+        const int sz = m_size;
+#ifndef FFTW_DOUBLE_ONLY
+        if (realIn != fbuf)
+#endif
+            for (int i = 0; i < sz; ++i) {
+                fbuf[i] = realIn[i];
+            }
+        fftwf_execute(m_fplanf);
+        v_convert(complexOut, (fft_float_type *)m_fpacked, sz + 2);
+    }
+
+    void forwardPolar(const float *BQ_R__ realIn, float *BQ_R__ magOut, float *BQ_R__ phaseOut) {
+        if (!m_fplanf) initFloat();
+        fft_float_type *const BQ_R__ fbuf = m_fbuf;
+        const int sz = m_size;
+#ifndef FFTW_DOUBLE_ONLY
+        if (realIn != fbuf) 
+#endif
+            for (int i = 0; i < sz; ++i) {
+                fbuf[i] = realIn[i];
+            }
+        fftwf_execute(m_fplanf);
+        v_cartesian_interleaved_to_polar(magOut, phaseOut,
+                                         (float *)m_fpacked, m_size/2+1);
+    }
+
+    void forwardMagnitude(const float *BQ_R__ realIn, float *BQ_R__ magOut) {
+        if (!m_fplanf) initFloat();
+        fft_float_type *const BQ_R__ fbuf = m_fbuf;
+        const int sz = m_size;
+#ifndef FFTW_DOUBLE_ONLY
+        if (realIn != fbuf)
+#endif
+            for (int i = 0; i < sz; ++i) {
+                fbuf[i] = realIn[i];
+            }
+        fftwf_execute(m_fplanf);
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            magOut[i] = sqrtf(m_fpacked[i][0] * m_fpacked[i][0] +
+                              m_fpacked[i][1] * m_fpacked[i][1]);
+        }
+    }
+
+    void inverse(const double *BQ_R__ realIn, const double *BQ_R__ imagIn, double *BQ_R__ realOut) {
+        if (!m_dplanf) initDouble();
+        packDouble(realIn, imagIn);
+        fftw_execute(m_dplani);
+        const int sz = m_size;
+        fft_double_type *const BQ_R__ dbuf = m_dbuf;
+#ifndef FFTW_SINGLE_ONLY
+        if (realOut != dbuf) 
+#endif
+            for (int i = 0; i < sz; ++i) {
+                realOut[i] = dbuf[i];
+            }
+    }
+
+    void inverseInterleaved(const double *BQ_R__ complexIn, double *BQ_R__ realOut) {
+        if (!m_dplanf) initDouble();
+        v_convert((double *)m_dpacked, complexIn, m_size + 2);
+        fftw_execute(m_dplani);
+        const int sz = m_size;
+        fft_double_type *const BQ_R__ dbuf = m_dbuf;
+#ifndef FFTW_SINGLE_ONLY
+        if (realOut != dbuf) 
+#endif
+            for (int i = 0; i < sz; ++i) {
+                realOut[i] = dbuf[i];
+            }
+    }
+
+    void inversePolar(const double *BQ_R__ magIn, const double *BQ_R__ phaseIn, double *BQ_R__ realOut) {
+        if (!m_dplanf) initDouble();
+        const int hs = m_size/2;
+        fftw_complex *const BQ_R__ dpacked = m_dpacked;
+        for (int i = 0; i <= hs; ++i) {
+            dpacked[i][0] = magIn[i] * cos(phaseIn[i]);
+        }
+        for (int i = 0; i <= hs; ++i) {
+            dpacked[i][1] = magIn[i] * sin(phaseIn[i]);
+        }
+        fftw_execute(m_dplani);
+        const int sz = m_size;
+        fft_double_type *const BQ_R__ dbuf = m_dbuf;
+#ifndef FFTW_SINGLE_ONLY
+        if (realOut != dbuf)
+#endif
+            for (int i = 0; i < sz; ++i) {
+                realOut[i] = dbuf[i];
+            }
+    }
+
+    void inverseCepstral(const double *BQ_R__ magIn, double *BQ_R__ cepOut) {
+        if (!m_dplanf) initDouble();
+        fft_double_type *const BQ_R__ dbuf = m_dbuf;
+        fftw_complex *const BQ_R__ dpacked = m_dpacked;
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            dpacked[i][0] = log(magIn[i] + 0.000001);
+        }
+        for (int i = 0; i <= hs; ++i) {
+            dpacked[i][1] = 0.0;
+        }
+        fftw_execute(m_dplani);
+        const int sz = m_size;
+#ifndef FFTW_SINGLE_ONLY
+        if (cepOut != dbuf)
+#endif
+            for (int i = 0; i < sz; ++i) {
+                cepOut[i] = dbuf[i];
+            }
+    }
+
+    void inverse(const float *BQ_R__ realIn, const float *BQ_R__ imagIn, float *BQ_R__ realOut) {
+        if (!m_fplanf) initFloat();
+        packFloat(realIn, imagIn);
+        fftwf_execute(m_fplani);
+        const int sz = m_size;
+        fft_float_type *const BQ_R__ fbuf = m_fbuf;
+#ifndef FFTW_DOUBLE_ONLY
+        if (realOut != fbuf)
+#endif
+            for (int i = 0; i < sz; ++i) {
+                realOut[i] = fbuf[i];
+            }
+    }
+
+    void inverseInterleaved(const float *BQ_R__ complexIn, float *BQ_R__ realOut) {
+        if (!m_fplanf) initFloat();
+        v_copy((float *)m_fpacked, complexIn, m_size + 2);
+        fftwf_execute(m_fplani);
+        const int sz = m_size;
+        fft_float_type *const BQ_R__ fbuf = m_fbuf;
+#ifndef FFTW_DOUBLE_ONLY
+        if (realOut != fbuf)
+#endif
+            for (int i = 0; i < sz; ++i) {
+                realOut[i] = fbuf[i];
+            }
+    }
+
+    void inversePolar(const float *BQ_R__ magIn, const float *BQ_R__ phaseIn, float *BQ_R__ realOut) {
+        if (!m_fplanf) initFloat();
+        const int hs = m_size/2;
+        fftwf_complex *const BQ_R__ fpacked = m_fpacked;
+        for (int i = 0; i <= hs; ++i) {
+            fpacked[i][0] = magIn[i] * cosf(phaseIn[i]);
+        }
+        for (int i = 0; i <= hs; ++i) {
+            fpacked[i][1] = magIn[i] * sinf(phaseIn[i]);
+        }
+        fftwf_execute(m_fplani);
+        const int sz = m_size;
+        fft_float_type *const BQ_R__ fbuf = m_fbuf;
+#ifndef FFTW_DOUBLE_ONLY
+        if (realOut != fbuf)
+#endif
+            for (int i = 0; i < sz; ++i) {
+                realOut[i] = fbuf[i];
+            }
+    }
+
+    void inverseCepstral(const float *BQ_R__ magIn, float *BQ_R__ cepOut) {
+        if (!m_fplanf) initFloat();
+        const int hs = m_size/2;
+        fftwf_complex *const BQ_R__ fpacked = m_fpacked;
+        for (int i = 0; i <= hs; ++i) {
+            fpacked[i][0] = logf(magIn[i] + 0.000001f);
+        }
+        for (int i = 0; i <= hs; ++i) {
+            fpacked[i][1] = 0.f;
+        }
+        fftwf_execute(m_fplani);
+        const int sz = m_size;
+        fft_float_type *const BQ_R__ fbuf = m_fbuf;
+#ifndef FFTW_DOUBLE_ONLY
+        if (cepOut != fbuf)
+#endif
+            for (int i = 0; i < sz; ++i) {
+                cepOut[i] = fbuf[i];
+            }
+    }
+
+private:
+    fftwf_plan m_fplanf;
+    fftwf_plan m_fplani;
+#ifdef FFTW_DOUBLE_ONLY
+    double *m_fbuf;
+#else
+    float *m_fbuf;
+#endif
+    fftwf_complex *m_fpacked;
+    fftw_plan m_dplanf;
+    fftw_plan m_dplani;
+#ifdef FFTW_SINGLE_ONLY
+    float *m_dbuf;
+#else
+    double *m_dbuf;
+#endif
+    fftw_complex *m_dpacked;
+    const int m_size;
+    static int m_extantf;
+    static int m_extantd;
+#ifdef NO_THREADING
+    void initMutex() {}
+    void destroyMutex() {}
+    void lock() {}
+    void unlock() {}
+#else
+#ifdef _WIN32
+    static HANDLE m_commonMutex;
+    void initMutex() { m_commonMutex = CreateMutex(NULL, FALSE, NULL); }
+    void destroyMutex() { CloseHandle(m_commonMutex); }
+    void lock() { WaitForSingleObject(m_commonMutex, INFINITE); }
+    void unlock() { ReleaseMutex(m_commonMutex); }
+#else
+    static pthread_mutex_t m_commonMutex;
+    void initMutex() { pthread_mutex_init(&m_commonMutex, 0); }
+    void destroyMutex() { pthread_mutex_destroy(&m_commonMutex); }
+    void lock() { pthread_mutex_lock(&m_commonMutex); }
+    void unlock() { pthread_mutex_unlock(&m_commonMutex); }
+#endif
+#endif
+};
+
+int
+D_FFTW::m_extantf = 0;
+
+int
+D_FFTW::m_extantd = 0;
+
+#ifndef NO_THREADING
+#ifdef _WIN32
+HANDLE D_FFTW::m_commonMutex;
+#else
+pthread_mutex_t D_FFTW::m_commonMutex;
+#endif
+#endif
+
+#endif /* HAVE_FFTW3 */
+
+#ifdef HAVE_SFFT
+
+/*
+ Define SFFT_DOUBLE_ONLY to make all uses of SFFT functions be
+ double-precision (so "float" FFTs are calculated by casting to
+ doubles and using the double-precision SFFT function).
+
+ Define SFFT_SINGLE_ONLY to make all uses of SFFT functions be
+ single-precision (so "double" FFTs are calculated by casting to
+ floats and using the single-precision SFFT function).
+
+ Neither of these flags is desirable for either performance or
+ precision.
+*/
+
+//#define SFFT_DOUBLE_ONLY 1
+//#define SFFT_SINGLE_ONLY 1
+
+#if defined(SFFT_DOUBLE_ONLY) && defined(SFFT_SINGLE_ONLY)
+// Can't meaningfully define both
+#error Can only define one of SFFT_DOUBLE_ONLY and SFFT_SINGLE_ONLY
+#endif
+
+#ifdef SFFT_DOUBLE_ONLY
+#define fft_float_type double
+#define FLAG_SFFT_FLOAT SFFT_DOUBLE
+#define atan2f atan2
+#define sqrtf sqrt
+#define cosf cos
+#define sinf sin
+#define logf log
+#else
+#define FLAG_SFFT_FLOAT SFFT_FLOAT
+#define fft_float_type float
+#endif /* SFFT_DOUBLE_ONLY */
+
+#ifdef SFFT_SINGLE_ONLY
+#define fft_double_type float
+#define FLAG_SFFT_DOUBLE SFFT_FLOAT
+#define atan2 atan2f
+#define sqrt sqrtf
+#define cos cosf
+#define sin sinf
+#define log logf
+#else
+#define FLAG_SFFT_DOUBLE SFFT_DOUBLE
+#define fft_double_type double
+#endif /* SFFT_SINGLE_ONLY */
+
+class D_SFFT : public FFTImpl
+{
+public:
+    D_SFFT(int size) :
+        m_fplanf(0), m_fplani(0), m_dplanf(0), m_dplani(0), m_size(size)
+    {
+    }
+
+    ~D_SFFT() {
+        if (m_fplanf) {
+            sfft_free(m_fplanf);
+            sfft_free(m_fplani);
+            deallocate(m_fbuf);
+            deallocate(m_fresult);
+        }
+        if (m_dplanf) {
+            sfft_free(m_dplanf);
+            sfft_free(m_dplani);
+            deallocate(m_dbuf);
+            deallocate(m_dresult);
+        }
+    }
+
+    FFT::Precisions
+    getSupportedPrecisions() const {
+#ifdef SFFT_SINGLE_ONLY
+        return FFT::SinglePrecision;
+#else
+#ifdef SFFT_DOUBLE_ONLY
+        return FFT::DoublePrecision;
+#else
+        return FFT::SinglePrecision | FFT::DoublePrecision;
+#endif
+#endif
+    }
+
+    void initFloat() {
+        if (m_fplanf) return;
+        m_fbuf = allocate<fft_float_type>(2 * m_size);
+        m_fresult = allocate<fft_float_type>(2 * m_size);
+        m_fplanf = sfft_init(m_size, SFFT_FORWARD | FLAG_SFFT_FLOAT);
+        m_fplani = sfft_init(m_size, SFFT_BACKWARD | FLAG_SFFT_FLOAT);
+        if (!m_fplanf || !m_fplani) {
+            if (!m_fplanf) {
+                std::cerr << "D_SFFT: Failed to construct forward float transform for size " << m_size << " (check SFFT library's target configuration)" << std::endl;
+            } else {
+                std::cerr << "D_SFFT: Failed to construct inverse float transform for size " << m_size << " (check SFFT library's target configuration)" << std::endl;
+            }
+#ifndef NO_EXCEPTIONS
+            throw FFT::InternalError;
+#else
+            abort();
+#endif
+        }
+    }
+
+    void initDouble() {
+        if (m_dplanf) return;
+        m_dbuf = allocate<fft_double_type>(2 * m_size);
+        m_dresult = allocate<fft_double_type>(2 * m_size);
+        m_dplanf = sfft_init(m_size, SFFT_FORWARD | FLAG_SFFT_DOUBLE);
+        m_dplani = sfft_init(m_size, SFFT_BACKWARD | FLAG_SFFT_DOUBLE);
+        if (!m_dplanf || !m_dplani) {
+            if (!m_dplanf) {
+                std::cerr << "D_SFFT: Failed to construct forward double transform for size " << m_size << " (check SFFT library's target configuration)" << std::endl;
+            } else {
+                std::cerr << "D_SFFT: Failed to construct inverse double transform for size " << m_size << " (check SFFT library's target configuration)" << std::endl;
+            }
+#ifndef NO_EXCEPTIONS
+            throw FFT::InternalError;
+#else
+            abort();
+#endif
+        }
+    }
+
+    void packFloat(const float *BQ_R__ re, const float *BQ_R__ im, fft_float_type *target, int n) {
+        for (int i = 0; i < n; ++i) target[i*2] = re[i];
+        if (im) {
+            for (int i = 0; i < n; ++i) target[i*2+1] = im[i]; 
+        } else {
+            for (int i = 0; i < n; ++i) target[i*2+1] = 0.f;
+        }                
+    }
+
+    void packDouble(const double *BQ_R__ re, const double *BQ_R__ im, fft_double_type *target, int n) {
+        for (int i = 0; i < n; ++i) target[i*2] = re[i];
+        if (im) {
+            for (int i = 0; i < n; ++i) target[i*2+1] = im[i];
+        } else {
+            for (int i = 0; i < n; ++i) target[i*2+1] = 0.0;
+        }                
+    }
+
+    void unpackFloat(const fft_float_type *source, float *BQ_R__ re, float *BQ_R__ im, int n) {
+        for (int i = 0; i < n; ++i) re[i] = source[i*2];
+        if (im) {
+            for (int i = 0; i < n; ++i) im[i] = source[i*2+1];
+        }
+    }        
+
+    void unpackDouble(const fft_double_type *source, double *BQ_R__ re, double *BQ_R__ im, int n) {
+        for (int i = 0; i < n; ++i) re[i] = source[i*2];
+        if (im) {
+            for (int i = 0; i < n; ++i) im[i] = source[i*2+1];
+        }
+    }        
+
+    template<typename T>
+    void mirror(T *BQ_R__ cplx, int n) {
+        for (int i = 1; i <= n/2; ++i) {
+            int j = n-i;
+            cplx[j*2] = cplx[i*2];
+            cplx[j*2+1] = -cplx[i*2+1];
+        }
+    }
+
+    void forward(const double *BQ_R__ realIn, double *BQ_R__ realOut, double *BQ_R__ imagOut) {
+        if (!m_dplanf) initDouble();
+        packDouble(realIn, 0, m_dbuf, m_size);
+        sfft_execute(m_dplanf, m_dbuf, m_dresult);
+        unpackDouble(m_dresult, realOut, imagOut, m_size/2+1);
+    }
+
+    void forwardInterleaved(const double *BQ_R__ realIn, double *BQ_R__ complexOut) {
+        if (!m_dplanf) initDouble();
+        packDouble(realIn, 0, m_dbuf, m_size);
+        sfft_execute(m_dplanf, m_dbuf, m_dresult);
+        v_convert(complexOut, m_dresult, m_size+2); // i.e. m_size/2+1 complex
+    }
+
+    void forwardPolar(const double *BQ_R__ realIn, double *BQ_R__ magOut, double *BQ_R__ phaseOut) {
+        if (!m_dplanf) initDouble();
+        packDouble(realIn, 0, m_dbuf, m_size);
+        sfft_execute(m_dplanf, m_dbuf, m_dresult);
+        v_cartesian_interleaved_to_polar(magOut, phaseOut,
+                                         m_dresult, m_size/2+1);
+    }
+
+    void forwardMagnitude(const double *BQ_R__ realIn, double *BQ_R__ magOut) {
+        if (!m_dplanf) initDouble();
+        packDouble(realIn, 0, m_dbuf, m_size);
+        sfft_execute(m_dplanf, m_dbuf, m_dresult);
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            magOut[i] = sqrt(m_dresult[i*2] * m_dresult[i*2] +
+                             m_dresult[i*2+1] * m_dresult[i*2+1]);
+        }
+    }
+
+    void forward(const float *BQ_R__ realIn, float *BQ_R__ realOut, float *BQ_R__ imagOut) {
+        if (!m_fplanf) initFloat();
+        packFloat(realIn, 0, m_fbuf, m_size);
+        sfft_execute(m_fplanf, m_fbuf, m_fresult);
+        unpackFloat(m_fresult, realOut, imagOut, m_size/2+1);
+    }
+
+    void forwardInterleaved(const float *BQ_R__ realIn, float *BQ_R__ complexOut) {
+        if (!m_fplanf) initFloat();
+        packFloat(realIn, 0, m_fbuf, m_size);
+        sfft_execute(m_fplanf, m_fbuf, m_fresult);
+        v_convert(complexOut, m_fresult, m_size+2); // i.e. m_size/2+1 complex
+    }
+
+    void forwardPolar(const float *BQ_R__ realIn, float *BQ_R__ magOut, float *BQ_R__ phaseOut) {
+        if (!m_fplanf) initFloat();
+        packFloat(realIn, 0, m_fbuf, m_size);
+        sfft_execute(m_fplanf, m_fbuf, m_fresult);
+        v_cartesian_interleaved_to_polar(magOut, phaseOut,
+                                         m_fresult, m_size/2+1);
+    }
+
+    void forwardMagnitude(const float *BQ_R__ realIn, float *BQ_R__ magOut) {
+        if (!m_fplanf) initFloat();
+        packFloat(realIn, 0, m_fbuf, m_size);
+        sfft_execute(m_fplanf, m_fbuf, m_fresult);
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            magOut[i] = sqrtf(m_fresult[i*2] * m_fresult[i*2] +
+                              m_fresult[i*2+1] * m_fresult[i*2+1]);
+        }
+    }
+
+    void inverse(const double *BQ_R__ realIn, const double *BQ_R__ imagIn, double *BQ_R__ realOut) {
+        if (!m_dplanf) initDouble();
+        packDouble(realIn, imagIn, m_dbuf, m_size/2+1);
+        mirror(m_dbuf, m_size);
+        sfft_execute(m_dplani, m_dbuf, m_dresult);
+        for (int i = 0; i < m_size; ++i) {
+            realOut[i] = m_dresult[i*2];
+        }
+    }
+
+    void inverseInterleaved(const double *BQ_R__ complexIn, double *BQ_R__ realOut) {
+        if (!m_dplanf) initDouble();
+        v_convert((double *)m_dbuf, complexIn, m_size + 2);
+        mirror(m_dbuf, m_size);
+        sfft_execute(m_dplani, m_dbuf, m_dresult);
+        for (int i = 0; i < m_size; ++i) {
+            realOut[i] = m_dresult[i*2];
+        }
+    }
+
+    void inversePolar(const double *BQ_R__ magIn, const double *BQ_R__ phaseIn, double *BQ_R__ realOut) {
+        if (!m_dplanf) initDouble();
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            m_dbuf[i*2] = magIn[i] * cos(phaseIn[i]);
+            m_dbuf[i*2+1] = magIn[i] * sin(phaseIn[i]);
+        }
+        mirror(m_dbuf, m_size);
+        sfft_execute(m_dplani, m_dbuf, m_dresult);
+        for (int i = 0; i < m_size; ++i) {
+            realOut[i] = m_dresult[i*2];
+        }
+    }
+
+    void inverseCepstral(const double *BQ_R__ magIn, double *BQ_R__ cepOut) {
+        if (!m_dplanf) initDouble();
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            m_dbuf[i*2] = log(magIn[i] + 0.000001);
+            m_dbuf[i*2+1] = 0.0;
+        }
+        mirror(m_dbuf, m_size);
+        sfft_execute(m_dplani, m_dbuf, m_dresult);
+        for (int i = 0; i < m_size; ++i) {
+            cepOut[i] = m_dresult[i*2];
+        }
+    }
+
+    void inverse(const float *BQ_R__ realIn, const float *BQ_R__ imagIn, float *BQ_R__ realOut) {
+        if (!m_fplanf) initFloat();
+        packFloat(realIn, imagIn, m_fbuf, m_size/2+1);
+        mirror(m_fbuf, m_size);
+        sfft_execute(m_fplani, m_fbuf, m_fresult);
+        for (int i = 0; i < m_size; ++i) {
+            realOut[i] = m_fresult[i*2];
+        }
+    }
+
+    void inverseInterleaved(const float *BQ_R__ complexIn, float *BQ_R__ realOut) {
+        if (!m_fplanf) initFloat();
+        v_convert((float *)m_fbuf, complexIn, m_size + 2);
+        mirror(m_fbuf, m_size);
+        sfft_execute(m_fplani, m_fbuf, m_fresult);
+        for (int i = 0; i < m_size; ++i) {
+            realOut[i] = m_fresult[i*2];
+        }
+    }
+
+    void inversePolar(const float *BQ_R__ magIn, const float *BQ_R__ phaseIn, float *BQ_R__ realOut) {
+        if (!m_fplanf) initFloat();
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            m_fbuf[i*2] = magIn[i] * cosf(phaseIn[i]);
+            m_fbuf[i*2+1] = magIn[i] * sinf(phaseIn[i]);
+        }
+        mirror(m_fbuf, m_size);
+        sfft_execute(m_fplani, m_fbuf, m_fresult);
+        for (int i = 0; i < m_size; ++i) {
+            realOut[i] = m_fresult[i*2];
+        }
+    }
+
+    void inverseCepstral(const float *BQ_R__ magIn, float *BQ_R__ cepOut) {
+        if (!m_fplanf) initFloat();
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            m_fbuf[i*2] = logf(magIn[i] + 0.00001);
+            m_fbuf[i*2+1] = 0.0f;
+        }
+        sfft_execute(m_fplani, m_fbuf, m_fresult);
+        for (int i = 0; i < m_size; ++i) {
+            cepOut[i] = m_fresult[i*2];
+        }
+    }
+
+private:
+    sfft_plan_t *m_fplanf;
+    sfft_plan_t *m_fplani;
+    fft_float_type *m_fbuf;
+    fft_float_type *m_fresult;
+
+    sfft_plan_t *m_dplanf;
+    sfft_plan_t *m_dplani;
+    fft_double_type *m_dbuf;
+    fft_double_type *m_dresult;
+
+    const int m_size;
+};
+
+#endif /* HAVE_SFFT */
+
+#ifdef HAVE_KISSFFT
+
+class D_KISSFFT : public FFTImpl
+{
+public:
+    D_KISSFFT(int size) :
+        m_size(size),
+        m_fplanf(0),  
+        m_fplani(0)
+    {
+#ifdef FIXED_POINT
+#error KISSFFT is not configured for float values
+#endif
+        if (sizeof(kiss_fft_scalar) != sizeof(float)) {
+            std::cerr << "ERROR: KISSFFT is not configured for float values"
+                      << std::endl;
+        }
+
+        m_fbuf = new kiss_fft_scalar[m_size + 2];
+        m_fpacked = new kiss_fft_cpx[m_size + 2];
+        m_fplanf = kiss_fftr_alloc(m_size, 0, NULL, NULL);
+        m_fplani = kiss_fftr_alloc(m_size, 1, NULL, NULL);
+    }
+
+    ~D_KISSFFT() {
+        kiss_fftr_free(m_fplanf);
+        kiss_fftr_free(m_fplani);
+        kiss_fft_cleanup();
+
+        delete[] m_fbuf;
+        delete[] m_fpacked;
+    }
+
+    FFT::Precisions
+    getSupportedPrecisions() const {
+        return FFT::SinglePrecision;
+    }
+
+    void initFloat() { }
+    void initDouble() { }
+
+    void packFloat(const float *BQ_R__ re, const float *BQ_R__ im) {
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            m_fpacked[i].r = re[i];
+        }
+        if (im) {
+            for (int i = 0; i <= hs; ++i) {
+                m_fpacked[i].i = im[i];
+            }
+        } else {
+            for (int i = 0; i <= hs; ++i) {
+                m_fpacked[i].i = 0.f;
+            }
+        }
+    }
+
+    void unpackFloat(float *BQ_R__ re, float *BQ_R__ im) {
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            re[i] = m_fpacked[i].r;
+        }
+        if (im) {
+            for (int i = 0; i <= hs; ++i) {
+                im[i] = m_fpacked[i].i;
+            }
+        }
+    }        
+
+    void packDouble(const double *BQ_R__ re, const double *BQ_R__ im) {
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            m_fpacked[i].r = float(re[i]);
+        }
+        if (im) {
+            for (int i = 0; i <= hs; ++i) {
+                m_fpacked[i].i = float(im[i]);
+            }
+        } else {
+            for (int i = 0; i <= hs; ++i) {
+                m_fpacked[i].i = 0.f;
+            }
+        }
+    }
+
+    void unpackDouble(double *BQ_R__ re, double *BQ_R__ im) {
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            re[i] = double(m_fpacked[i].r);
+        }
+        if (im) {
+            for (int i = 0; i <= hs; ++i) {
+                im[i] = double(m_fpacked[i].i);
+            }
+        }
+    }        
+
+    void forward(const double *BQ_R__ realIn, double *BQ_R__ realOut, double *BQ_R__ imagOut) {
+
+        v_convert(m_fbuf, realIn, m_size);
+        kiss_fftr(m_fplanf, m_fbuf, m_fpacked);
+        unpackDouble(realOut, imagOut);
+    }
+
+    void forwardInterleaved(const double *BQ_R__ realIn, double *BQ_R__ complexOut) {
+
+        v_convert(m_fbuf, realIn, m_size);
+        kiss_fftr(m_fplanf, m_fbuf, m_fpacked);
+        v_convert(complexOut, (float *)m_fpacked, m_size + 2);
+    }
+
+    void forwardPolar(const double *BQ_R__ realIn, double *BQ_R__ magOut, double *BQ_R__ phaseOut) {
+
+        for (int i = 0; i < m_size; ++i) {
+            m_fbuf[i] = float(realIn[i]);
+        }
+
+        kiss_fftr(m_fplanf, m_fbuf, m_fpacked);
+
+        const int hs = m_size/2;
+
+        for (int i = 0; i <= hs; ++i) {
+            magOut[i] = sqrt(double(m_fpacked[i].r) * double(m_fpacked[i].r) +
+                             double(m_fpacked[i].i) * double(m_fpacked[i].i));
+        }
+
+        for (int i = 0; i <= hs; ++i) {
+            phaseOut[i] = atan2(double(m_fpacked[i].i), double(m_fpacked[i].r));
+        }
+    }
+
+    void forwardMagnitude(const double *BQ_R__ realIn, double *BQ_R__ magOut) {
+
+        for (int i = 0; i < m_size; ++i) {
+            m_fbuf[i] = float(realIn[i]);
+        }
+
+        kiss_fftr(m_fplanf, m_fbuf, m_fpacked);
+
+        const int hs = m_size/2;
+
+        for (int i = 0; i <= hs; ++i) {
+            magOut[i] = sqrt(double(m_fpacked[i].r) * double(m_fpacked[i].r) +
+                             double(m_fpacked[i].i) * double(m_fpacked[i].i));
+        }
+    }
+
+    void forward(const float *BQ_R__ realIn, float *BQ_R__ realOut, float *BQ_R__ imagOut) {
+
+        kiss_fftr(m_fplanf, realIn, m_fpacked);
+        unpackFloat(realOut, imagOut);
+    }
+
+    void forwardInterleaved(const float *BQ_R__ realIn, float *BQ_R__ complexOut) {
+
+        kiss_fftr(m_fplanf, realIn, (kiss_fft_cpx *)complexOut);
+    }
+
+    void forwardPolar(const float *BQ_R__ realIn, float *BQ_R__ magOut, float *BQ_R__ phaseOut) {
+
+        kiss_fftr(m_fplanf, realIn, m_fpacked);
+
+        const int hs = m_size/2;
+
+        for (int i = 0; i <= hs; ++i) {
+            magOut[i] = sqrtf(m_fpacked[i].r * m_fpacked[i].r +
+                              m_fpacked[i].i * m_fpacked[i].i);
+        }
+
+        for (int i = 0; i <= hs; ++i) {
+            phaseOut[i] = atan2f(m_fpacked[i].i, m_fpacked[i].r);
+        }
+    }
+
+    void forwardMagnitude(const float *BQ_R__ realIn, float *BQ_R__ magOut) {
+
+        kiss_fftr(m_fplanf, realIn, m_fpacked);
+
+        const int hs = m_size/2;
+
+        for (int i = 0; i <= hs; ++i) {
+            magOut[i] = sqrtf(m_fpacked[i].r * m_fpacked[i].r +
+                              m_fpacked[i].i * m_fpacked[i].i);
+        }
+    }
+
+    void inverse(const double *BQ_R__ realIn, const double *BQ_R__ imagIn, double *BQ_R__ realOut) {
+
+        packDouble(realIn, imagIn);
+
+        kiss_fftri(m_fplani, m_fpacked, m_fbuf);
+
+        for (int i = 0; i < m_size; ++i) {
+            realOut[i] = m_fbuf[i];
+        }
+    }
+
+    void inverseInterleaved(const double *BQ_R__ complexIn, double *BQ_R__ realOut) {
+
+        v_convert((float *)m_fpacked, complexIn, m_size + 2);
+
+        kiss_fftri(m_fplani, m_fpacked, m_fbuf);
+
+        for (int i = 0; i < m_size; ++i) {
+            realOut[i] = m_fbuf[i];
+        }
+    }
+
+    void inversePolar(const double *BQ_R__ magIn, const double *BQ_R__ phaseIn, double *BQ_R__ realOut) {
+
+        const int hs = m_size/2;
+
+        for (int i = 0; i <= hs; ++i) {
+            m_fpacked[i].r = float(magIn[i] * cos(phaseIn[i]));
+            m_fpacked[i].i = float(magIn[i] * sin(phaseIn[i]));
+        }
+
+        kiss_fftri(m_fplani, m_fpacked, m_fbuf);
+
+        for (int i = 0; i < m_size; ++i) {
+            realOut[i] = m_fbuf[i];
+        }
+    }
+
+    void inverseCepstral(const double *BQ_R__ magIn, double *BQ_R__ cepOut) {
+
+        const int hs = m_size/2;
+
+        for (int i = 0; i <= hs; ++i) {
+            m_fpacked[i].r = float(log(magIn[i] + 0.000001));
+            m_fpacked[i].i = 0.0f;
+        }
+
+        kiss_fftri(m_fplani, m_fpacked, m_fbuf);
+
+        for (int i = 0; i < m_size; ++i) {
+            cepOut[i] = m_fbuf[i];
+        }
+    }
+    
+    void inverse(const float *BQ_R__ realIn, const float *BQ_R__ imagIn, float *BQ_R__ realOut) {
+
+        packFloat(realIn, imagIn);
+        kiss_fftri(m_fplani, m_fpacked, realOut);
+    }
+
+    void inverseInterleaved(const float *BQ_R__ complexIn, float *BQ_R__ realOut) {
+
+        v_copy((float *)m_fpacked, complexIn, m_size + 2);
+        kiss_fftri(m_fplani, m_fpacked, realOut);
+    }
+
+    void inversePolar(const float *BQ_R__ magIn, const float *BQ_R__ phaseIn, float *BQ_R__ realOut) {
+
+        const int hs = m_size/2;
+
+        for (int i = 0; i <= hs; ++i) {
+            m_fpacked[i].r = magIn[i] * cosf(phaseIn[i]);
+            m_fpacked[i].i = magIn[i] * sinf(phaseIn[i]);
+        }
+
+        kiss_fftri(m_fplani, m_fpacked, realOut);
+    }
+
+    void inverseCepstral(const float *BQ_R__ magIn, float *BQ_R__ cepOut) {
+
+        const int hs = m_size/2;
+
+        for (int i = 0; i <= hs; ++i) {
+            m_fpacked[i].r = logf(magIn[i] + 0.000001f);
+            m_fpacked[i].i = 0.0f;
+        }
+
+        kiss_fftri(m_fplani, m_fpacked, cepOut);
+    }
+
+private:
+    const int m_size;
+    kiss_fftr_cfg m_fplanf;
+    kiss_fftr_cfg m_fplani;
+    kiss_fft_scalar *m_fbuf;
+    kiss_fft_cpx *m_fpacked;
+};
+
+#endif /* HAVE_KISSFFT */
+
+#ifdef USE_BUILTIN_FFT
+
+class D_Cross : public FFTImpl
+{
+public:
+    D_Cross(int size) : m_size(size), m_table(0) {
+        
+        m_a = new double[size];
+        m_b = new double[size];
+        m_c = new double[size];
+        m_d = new double[size];
+
+        m_table = new int[m_size];
+    
+        int bits;
+        int i, j, k, m;
+
+        for (i = 0; ; ++i) {
+            if (m_size & (1 << i)) {
+                bits = i;
+                break;
+            }
+        }
+        
+        for (i = 0; i < m_size; ++i) {
+            
+            m = i;
+            
+            for (j = k = 0; j < bits; ++j) {
+                k = (k << 1) | (m & 1);
+                m >>= 1;
+            }
+            
+            m_table[i] = k;
+        }
+    }
+
+    ~D_Cross() {
+        delete[] m_table;
+        delete[] m_a;
+        delete[] m_b;
+        delete[] m_c;
+        delete[] m_d;
+    }
+
+    FFT::Precisions
+    getSupportedPrecisions() const {
+        return FFT::DoublePrecision;
+    }
+
+    void initFloat() { }
+    void initDouble() { }
+
+    void forward(const double *BQ_R__ realIn, double *BQ_R__ realOut, double *BQ_R__ imagOut) {
+        basefft(false, realIn, 0, m_c, m_d);
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) realOut[i] = m_c[i];
+        if (imagOut) {
+            for (int i = 0; i <= hs; ++i) imagOut[i] = m_d[i];
+        }
+    }
+
+    void forwardInterleaved(const double *BQ_R__ realIn, double *BQ_R__ complexOut) {
+        basefft(false, realIn, 0, m_c, m_d);
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) complexOut[i*2] = m_c[i];
+        for (int i = 0; i <= hs; ++i) complexOut[i*2+1] = m_d[i];
+    }
+
+    void forwardPolar(const double *BQ_R__ realIn, double *BQ_R__ magOut, double *BQ_R__ phaseOut) {
+        basefft(false, realIn, 0, m_c, m_d);
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            magOut[i] = sqrt(m_c[i] * m_c[i] + m_d[i] * m_d[i]);
+            phaseOut[i] = atan2(m_d[i], m_c[i]) ;
+        }
+    }
+
+    void forwardMagnitude(const double *BQ_R__ realIn, double *BQ_R__ magOut) {
+        basefft(false, realIn, 0, m_c, m_d);
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            magOut[i] = sqrt(m_c[i] * m_c[i] + m_d[i] * m_d[i]);
+        }
+    }
+
+    void forward(const float *BQ_R__ realIn, float *BQ_R__ realOut, float *BQ_R__ imagOut) {
+        for (int i = 0; i < m_size; ++i) m_a[i] = realIn[i];
+        basefft(false, m_a, 0, m_c, m_d);
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) realOut[i] = m_c[i];
+        if (imagOut) {
+            for (int i = 0; i <= hs; ++i) imagOut[i] = m_d[i];
+        }
+    }
+
+    void forwardInterleaved(const float *BQ_R__ realIn, float *BQ_R__ complexOut) {
+        for (int i = 0; i < m_size; ++i) m_a[i] = realIn[i];
+        basefft(false, m_a, 0, m_c, m_d);
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) complexOut[i*2] = m_c[i];
+        for (int i = 0; i <= hs; ++i) complexOut[i*2+1] = m_d[i];
+    }
+
+    void forwardPolar(const float *BQ_R__ realIn, float *BQ_R__ magOut, float *BQ_R__ phaseOut) {
+        for (int i = 0; i < m_size; ++i) m_a[i] = realIn[i];
+        basefft(false, m_a, 0, m_c, m_d);
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            magOut[i] = sqrt(m_c[i] * m_c[i] + m_d[i] * m_d[i]);
+            phaseOut[i] = atan2(m_d[i], m_c[i]) ;
+        }
+    }
+
+    void forwardMagnitude(const float *BQ_R__ realIn, float *BQ_R__ magOut) {
+        for (int i = 0; i < m_size; ++i) m_a[i] = realIn[i];
+        basefft(false, m_a, 0, m_c, m_d);
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            magOut[i] = sqrt(m_c[i] * m_c[i] + m_d[i] * m_d[i]);
+        }
+    }
+
+    void inverse(const double *BQ_R__ realIn, const double *BQ_R__ imagIn, double *BQ_R__ realOut) {
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            double real = realIn[i];
+            double imag = imagIn[i];
+            m_a[i] = real;
+            m_b[i] = imag;
+            if (i > 0) {
+                m_a[m_size-i] = real;
+                m_b[m_size-i] = -imag;
+            }
+        }
+        basefft(true, m_a, m_b, realOut, m_d);
+    }
+
+    void inverseInterleaved(const double *BQ_R__ complexIn, double *BQ_R__ realOut) {
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            double real = complexIn[i*2];
+            double imag = complexIn[i*2+1];
+            m_a[i] = real;
+            m_b[i] = imag;
+            if (i > 0) {
+                m_a[m_size-i] = real;
+                m_b[m_size-i] = -imag;
+            }
+        }
+        basefft(true, m_a, m_b, realOut, m_d);
+    }
+
+    void inversePolar(const double *BQ_R__ magIn, const double *BQ_R__ phaseIn, double *BQ_R__ realOut) {
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            double real = magIn[i] * cos(phaseIn[i]);
+            double imag = magIn[i] * sin(phaseIn[i]);
+            m_a[i] = real;
+            m_b[i] = imag;
+            if (i > 0) {
+                m_a[m_size-i] = real;
+                m_b[m_size-i] = -imag;
+            }
+        }
+        basefft(true, m_a, m_b, realOut, m_d);
+    }
+
+    void inverseCepstral(const double *BQ_R__ magIn, double *BQ_R__ cepOut) {
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            double real = log(magIn[i] + 0.000001);
+            m_a[i] = real;
+            m_b[i] = 0.0;
+            if (i > 0) {
+                m_a[m_size-i] = real;
+                m_b[m_size-i] = 0.0;
+            }
+        }
+        basefft(true, m_a, m_b, cepOut, m_d);
+    }
+
+    void inverse(const float *BQ_R__ realIn, const float *BQ_R__ imagIn, float *BQ_R__ realOut) {
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            float real = realIn[i];
+            float imag = imagIn[i];
+            m_a[i] = real;
+            m_b[i] = imag;
+            if (i > 0) {
+                m_a[m_size-i] = real;
+                m_b[m_size-i] = -imag;
+            }
+        }
+        basefft(true, m_a, m_b, m_c, m_d);
+        for (int i = 0; i < m_size; ++i) realOut[i] = m_c[i];
+    }
+
+    void inverseInterleaved(const float *BQ_R__ complexIn, float *BQ_R__ realOut) {
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            float real = complexIn[i*2];
+            float imag = complexIn[i*2+1];
+            m_a[i] = real;
+            m_b[i] = imag;
+            if (i > 0) {
+                m_a[m_size-i] = real;
+                m_b[m_size-i] = -imag;
+            }
+        }
+        basefft(true, m_a, m_b, m_c, m_d);
+        for (int i = 0; i < m_size; ++i) realOut[i] = m_c[i];
+    }
+
+    void inversePolar(const float *BQ_R__ magIn, const float *BQ_R__ phaseIn, float *BQ_R__ realOut) {
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            float real = magIn[i] * cosf(phaseIn[i]);
+            float imag = magIn[i] * sinf(phaseIn[i]);
+            m_a[i] = real;
+            m_b[i] = imag;
+            if (i > 0) {
+                m_a[m_size-i] = real;
+                m_b[m_size-i] = -imag;
+            }
+        }
+        basefft(true, m_a, m_b, m_c, m_d);
+        for (int i = 0; i < m_size; ++i) realOut[i] = m_c[i];
+    }
+
+    void inverseCepstral(const float *BQ_R__ magIn, float *BQ_R__ cepOut) {
+        const int hs = m_size/2;
+        for (int i = 0; i <= hs; ++i) {
+            float real = logf(magIn[i] + 0.000001);
+            m_a[i] = real;
+            m_b[i] = 0.0;
+            if (i > 0) {
+                m_a[m_size-i] = real;
+                m_b[m_size-i] = 0.0;
+            }
+        }
+        basefft(true, m_a, m_b, m_c, m_d);
+        for (int i = 0; i < m_size; ++i) cepOut[i] = m_c[i];
+    }
+
+private:
+    const int m_size;
+    int *m_table;
+    double *m_a;
+    double *m_b;
+    double *m_c;
+    double *m_d;
+    void basefft(bool inverse, const double *BQ_R__ ri, const double *BQ_R__ ii, double *BQ_R__ ro, double *BQ_R__ io);
+};
+
+void
+D_Cross::basefft(bool inverse, const double *BQ_R__ ri, const double *BQ_R__ ii, double *BQ_R__ ro, double *BQ_R__ io)
+{
+    if (!ri || !ro || !io) return;
+
+    int i, j, k, m;
+    int blockSize, blockEnd;
+
+    double tr, ti;
+
+    double angle = 2.0 * M_PI;
+    if (inverse) angle = -angle;
+
+    const int n = m_size;
+
+    if (ii) {
+	for (i = 0; i < n; ++i) {
+	    ro[m_table[i]] = ri[i];
+        }
+	for (i = 0; i < n; ++i) {
+	    io[m_table[i]] = ii[i];
+	}
+    } else {
+	for (i = 0; i < n; ++i) {
+	    ro[m_table[i]] = ri[i];
+        }
+	for (i = 0; i < n; ++i) {
+	    io[m_table[i]] = 0.0;
+	}
+    }
+
+    blockEnd = 1;
+
+    for (blockSize = 2; blockSize <= n; blockSize <<= 1) {
+
+	double delta = angle / (double)blockSize;
+	double sm2 = -sin(-2 * delta);
+	double sm1 = -sin(-delta);
+	double cm2 = cos(-2 * delta);
+	double cm1 = cos(-delta);
+	double w = 2 * cm1;
+	double ar[3], ai[3];
+
+	for (i = 0; i < n; i += blockSize) {
+
+	    ar[2] = cm2;
+	    ar[1] = cm1;
+
+	    ai[2] = sm2;
+	    ai[1] = sm1;
+
+	    for (j = i, m = 0; m < blockEnd; j++, m++) {
+
+		ar[0] = w * ar[1] - ar[2];
+		ar[2] = ar[1];
+		ar[1] = ar[0];
+
+		ai[0] = w * ai[1] - ai[2];
+		ai[2] = ai[1];
+		ai[1] = ai[0];
+
+		k = j + blockEnd;
+		tr = ar[0] * ro[k] - ai[0] * io[k];
+		ti = ar[0] * io[k] + ai[0] * ro[k];
+
+		ro[k] = ro[j] - tr;
+		io[k] = io[j] - ti;
+
+		ro[j] += tr;
+		io[j] += ti;
+	    }
+	}
+
+	blockEnd = blockSize;
+    }
+
+/* fftw doesn't rescale, so nor will we
+
+    if (inverse) {
+
+	double denom = (double)n;
+
+	for (i = 0; i < n; i++) {
+	    ro[i] /= denom;
+	    io[i] /= denom;
+	}
+    }
+*/
+}
+
+#endif /* USE_BUILTIN_FFT */
+
+} /* end namespace FFTs */
+
+std::string
+FFT::m_implementation;
+
+std::set<std::string>
+FFT::getImplementations()
+{
+    std::set<std::string> impls;
+#ifdef HAVE_IPP
+    impls.insert("ipp");
+#endif
+#ifdef HAVE_FFTW3
+    impls.insert("fftw");
+#endif
+#ifdef HAVE_KISSFFT
+    impls.insert("kissfft");
+#endif
+#ifdef HAVE_VDSP
+    impls.insert("vdsp");
+#endif
+#ifdef HAVE_MEDIALIB
+    impls.insert("medialib");
+#endif
+#ifdef HAVE_OPENMAX
+    impls.insert("openmax");
+#endif
+#ifdef HAVE_SFFT
+    impls.insert("sfft");
+#endif
+#ifdef USE_BUILTIN_FFT
+    impls.insert("cross");
+#endif
+    return impls;
+}
+
+void
+FFT::pickDefaultImplementation()
+{
+    if (m_implementation != "") return;
+
+    std::set<std::string> impls = getImplementations();
+
+    std::string best = "cross";
+    if (impls.find("kissfft") != impls.end()) best = "kissfft";
+    if (impls.find("medialib") != impls.end()) best = "medialib";
+    if (impls.find("openmax") != impls.end()) best = "openmax";
+    if (impls.find("sfft") != impls.end()) best = "sfft";
+    if (impls.find("fftw") != impls.end()) best = "fftw";
+    if (impls.find("vdsp") != impls.end()) best = "vdsp";
+    if (impls.find("ipp") != impls.end()) best = "ipp";
+    
+    m_implementation = best;
+}
+
+std::string
+FFT::getDefaultImplementation()
+{
+    return m_implementation;
+}
+
+void
+FFT::setDefaultImplementation(std::string i)
+{
+    m_implementation = i;
+}
+
+FFT::FFT(int size, int debugLevel) :
+    d(0)
+{
+    if ((size < 2) ||
+        (size & (size-1))) {
+        std::cerr << "FFT::FFT(" << size << "): power-of-two sizes only supported, minimum size 2" << std::endl;
+#ifndef NO_EXCEPTIONS
+        throw InvalidSize;
+#else
+        abort();
+#endif
+    }
+
+    if (m_implementation == "") pickDefaultImplementation();
+    std::string impl = m_implementation;
+
+    if (debugLevel > 0) {
+        std::cerr << "FFT::FFT(" << size << "): using implementation: "
+                  << impl << std::endl;
+    }
+
+    if (impl == "ipp") {
+#ifdef HAVE_IPP
+        d = new FFTs::D_IPP(size);
+#endif
+    } else if (impl == "fftw") {
+#ifdef HAVE_FFTW3
+        d = new FFTs::D_FFTW(size);
+#endif
+    } else if (impl == "kissfft") {        
+#ifdef HAVE_KISSFFT
+        d = new FFTs::D_KISSFFT(size);
+#endif
+    } else if (impl == "vdsp") {
+#ifdef HAVE_VDSP
+        d = new FFTs::D_VDSP(size);
+#endif
+    } else if (impl == "medialib") {
+#ifdef HAVE_MEDIALIB
+        d = new FFTs::D_MEDIALIB(size);
+#endif
+    } else if (impl == "openmax") {
+#ifdef HAVE_OPENMAX
+        d = new FFTs::D_OPENMAX(size);
+#endif
+    } else if (impl == "sfft") {
+#ifdef HAVE_SFFT
+        d = new FFTs::D_SFFT(size);
+#endif
+    } else if (impl == "cross") {
+#ifdef USE_BUILTIN_FFT
+        d = new FFTs::D_Cross(size);
+#endif
+    }
+
+    if (!d) {
+        std::cerr << "FFT::FFT(" << size << "): ERROR: implementation "
+                  << impl << " is not compiled in" << std::endl;
+#ifndef NO_EXCEPTIONS
+        throw InvalidImplementation;
+#else
+        abort();
+#endif
+    }
+}
+
+FFT::~FFT()
+{
+    delete d;
+}
+
+#ifndef NO_EXCEPTIONS
+#define CHECK_NOT_NULL(x) \
+    if (!(x)) { \
+        std::cerr << "FFT: ERROR: Null argument " #x << std::endl;  \
+        throw NullArgument; \
+    }
+#else
+#define CHECK_NOT_NULL(x) \
+    if (!(x)) { \
+        std::cerr << "FFT: ERROR: Null argument " #x << std::endl;  \
+        std::cerr << "FFT: Would be throwing NullArgument here, if exceptions were not disabled" << std::endl;  \
+        return; \
+    }
+#endif
+
+void
+FFT::forward(const double *BQ_R__ realIn, double *BQ_R__ realOut, double *BQ_R__ imagOut)
+{
+    CHECK_NOT_NULL(realIn);
+    CHECK_NOT_NULL(realOut);
+    CHECK_NOT_NULL(imagOut);
+    d->forward(realIn, realOut, imagOut);
+}
+
+void
+FFT::forwardInterleaved(const double *BQ_R__ realIn, double *BQ_R__ complexOut)
+{
+    CHECK_NOT_NULL(realIn);
+    CHECK_NOT_NULL(complexOut);
+    d->forwardInterleaved(realIn, complexOut);
+}
+
+void
+FFT::forwardPolar(const double *BQ_R__ realIn, double *BQ_R__ magOut, double *BQ_R__ phaseOut)
+{
+    CHECK_NOT_NULL(realIn);
+    CHECK_NOT_NULL(magOut);
+    CHECK_NOT_NULL(phaseOut);
+    d->forwardPolar(realIn, magOut, phaseOut);
+}
+
+void
+FFT::forwardMagnitude(const double *BQ_R__ realIn, double *BQ_R__ magOut)
+{
+    CHECK_NOT_NULL(realIn);
+    CHECK_NOT_NULL(magOut);
+    d->forwardMagnitude(realIn, magOut);
+}
+
+void
+FFT::forward(const float *BQ_R__ realIn, float *BQ_R__ realOut, float *BQ_R__ imagOut)
+{
+    CHECK_NOT_NULL(realIn);
+    CHECK_NOT_NULL(realOut);
+    CHECK_NOT_NULL(imagOut);
+    d->forward(realIn, realOut, imagOut);
+}
+
+void
+FFT::forwardInterleaved(const float *BQ_R__ realIn, float *BQ_R__ complexOut)
+{
+    CHECK_NOT_NULL(realIn);
+    CHECK_NOT_NULL(complexOut);
+    d->forwardInterleaved(realIn, complexOut);
+}
+
+void
+FFT::forwardPolar(const float *BQ_R__ realIn, float *BQ_R__ magOut, float *BQ_R__ phaseOut)
+{
+    CHECK_NOT_NULL(realIn);
+    CHECK_NOT_NULL(magOut);
+    CHECK_NOT_NULL(phaseOut);
+    d->forwardPolar(realIn, magOut, phaseOut);
+}
+
+void
+FFT::forwardMagnitude(const float *BQ_R__ realIn, float *BQ_R__ magOut)
+{
+    CHECK_NOT_NULL(realIn);
+    CHECK_NOT_NULL(magOut);
+    d->forwardMagnitude(realIn, magOut);
+}
+
+void
+FFT::inverse(const double *BQ_R__ realIn, const double *BQ_R__ imagIn, double *BQ_R__ realOut)
+{
+    CHECK_NOT_NULL(realIn);
+    CHECK_NOT_NULL(imagIn);
+    CHECK_NOT_NULL(realOut);
+    d->inverse(realIn, imagIn, realOut);
+}
+
+void
+FFT::inverseInterleaved(const double *BQ_R__ complexIn, double *BQ_R__ realOut)
+{
+    CHECK_NOT_NULL(complexIn);
+    CHECK_NOT_NULL(realOut);
+    d->inverseInterleaved(complexIn, realOut);
+}
+
+void
+FFT::inversePolar(const double *BQ_R__ magIn, const double *BQ_R__ phaseIn, double *BQ_R__ realOut)
+{
+    CHECK_NOT_NULL(magIn);
+    CHECK_NOT_NULL(phaseIn);
+    CHECK_NOT_NULL(realOut);
+    d->inversePolar(magIn, phaseIn, realOut);
+}
+
+void
+FFT::inverseCepstral(const double *BQ_R__ magIn, double *BQ_R__ cepOut)
+{
+    CHECK_NOT_NULL(magIn);
+    CHECK_NOT_NULL(cepOut);
+    d->inverseCepstral(magIn, cepOut);
+}
+
+void
+FFT::inverse(const float *BQ_R__ realIn, const float *BQ_R__ imagIn, float *BQ_R__ realOut)
+{
+    CHECK_NOT_NULL(realIn);
+    CHECK_NOT_NULL(imagIn);
+    CHECK_NOT_NULL(realOut);
+    d->inverse(realIn, imagIn, realOut);
+}
+
+void
+FFT::inverseInterleaved(const float *BQ_R__ complexIn, float *BQ_R__ realOut)
+{
+    CHECK_NOT_NULL(complexIn);
+    CHECK_NOT_NULL(realOut);
+    d->inverseInterleaved(complexIn, realOut);
+}
+
+void
+FFT::inversePolar(const float *BQ_R__ magIn, const float *BQ_R__ phaseIn, float *BQ_R__ realOut)
+{
+    CHECK_NOT_NULL(magIn);
+    CHECK_NOT_NULL(phaseIn);
+    CHECK_NOT_NULL(realOut);
+    d->inversePolar(magIn, phaseIn, realOut);
+}
+
+void
+FFT::inverseCepstral(const float *BQ_R__ magIn, float *BQ_R__ cepOut)
+{
+    CHECK_NOT_NULL(magIn);
+    CHECK_NOT_NULL(cepOut);
+    d->inverseCepstral(magIn, cepOut);
+}
+
+void
+FFT::initFloat() 
+{
+    d->initFloat();
+}
+
+void
+FFT::initDouble() 
+{
+    d->initDouble();
+}
+
+FFT::Precisions
+FFT::getSupportedPrecisions() const
+{
+    return d->getSupportedPrecisions();
+}
+
+#ifdef FFT_MEASUREMENT
+
+std::string
+FFT::tune()
+{
+    std::ostringstream os;
+    os << "FFT::tune()..." << std::endl;
+
+    std::vector<int> sizes;
+    std::map<FFTImpl *, int> candidates;
+    std::map<int, int> wins;
+
+    sizes.push_back(512);
+    sizes.push_back(1024);
+    sizes.push_back(4096);
+    
+    for (unsigned int si = 0; si < sizes.size(); ++si) {
+
+        int size = sizes[si];
+
+        while (!candidates.empty()) {
+            delete candidates.begin()->first;
+            candidates.erase(candidates.begin());
+        }
+
+        FFTImpl *d;
+        
+#ifdef HAVE_IPP
+        std::cerr << "Constructing new IPP FFT object for size " << size << "..." << std::endl;
+        d = new FFTs::D_IPP(size);
+        d->initFloat();
+        d->initDouble();
+        candidates[d] = 0;
+#endif
+        
+#ifdef HAVE_FFTW3
+        os << "Constructing new FFTW3 FFT object for size " << size << "..." << std::endl;
+        d = new FFTs::D_FFTW(size);
+        d->initFloat();
+        d->initDouble();
+        candidates[d] = 1;
+#endif
+
+#ifdef HAVE_KISSFFT
+        os << "Constructing new KISSFFT object for size " << size << "..." << std::endl;
+        d = new FFTs::D_KISSFFT(size);
+        d->initFloat();
+        d->initDouble();
+        candidates[d] = 2;
+#endif        
+
+#ifdef USE_BUILTIN_FFT
+        os << "Constructing new Cross FFT object for size " << size << "..." << std::endl;
+        d = new FFTs::D_Cross(size);
+        d->initFloat();
+        d->initDouble();
+        candidates[d] = 3;
+#endif
+        
+#ifdef HAVE_VDSP
+        os << "Constructing new vDSP FFT object for size " << size << "..." << std::endl;
+        d = new FFTs::D_VDSP(size);
+        d->initFloat();
+        d->initDouble();
+        candidates[d] = 4;
+#endif
+        
+#ifdef HAVE_MEDIALIB
+        std::cerr << "Constructing new MediaLib FFT object for size " << size << "..." << std::endl;
+        d = new FFTs::D_MEDIALIB(size);
+        d->initFloat();
+        d->initDouble();
+        candidates[d] = 5;
+#endif
+        
+#ifdef HAVE_OPENMAX
+        os << "Constructing new OpenMAX FFT object for size " << size << "..." << std::endl;
+        d = new FFTs::D_OPENMAX(size);
+        d->initFloat();
+        d->initDouble();
+        candidates[d] = 6;
+#endif
+        
+#ifdef HAVE_SFFT
+        os << "Constructing new SFFT FFT object for size " << size << "..." << std::endl;
+        d = new FFTs::D_SFFT(size);
+//        d->initFloat();
+        d->initDouble();
+        candidates[d] = 6;
+#endif
+
+        os << "CLOCKS_PER_SEC = " << CLOCKS_PER_SEC << std::endl;
+        float divisor = float(CLOCKS_PER_SEC) / 1000.f;
+        
+        os << "Timing order is: ";
+        for (std::map<FFTImpl *, int>::iterator ci = candidates.begin();
+             ci != candidates.end(); ++ci) {
+            os << ci->second << " ";
+        }
+        os << std::endl;
+
+        int iterations = 500;
+        os << "Iterations: " << iterations << std::endl;
+
+        double *da = new double[size];
+        double *db = new double[size];
+        double *dc = new double[size];
+        double *dd = new double[size];
+        double *di = new double[size + 2];
+        double *dj = new double[size + 2];
+
+        float *fa = new float[size];
+        float *fb = new float[size];
+        float *fc = new float[size];
+        float *fd = new float[size];
+        float *fi = new float[size + 2];
+        float *fj = new float[size + 2];
+
+        for (int type = 0; type < 16; ++type) {
+    
+            //!!!
+            if ((type > 3 && type < 8) ||
+                (type > 11)) {
+                continue;
+            }
+
+            if (type > 7) {
+                // inverse transform: bigger inputs, to simulate the
+                // fact that the forward transform is unscaled
+                for (int i = 0; i < size; ++i) {
+                    da[i] = drand48() * size;
+                    fa[i] = da[i];
+                    db[i] = drand48() * size;
+                    fb[i] = db[i];
+                }
+            } else {    
+                for (int i = 0; i < size; ++i) {
+                    da[i] = drand48();
+                    fa[i] = da[i];
+                    db[i] = drand48();
+                    fb[i] = db[i];
+                }
+            }
+                
+            for (int i = 0; i < size + 2; ++i) {
+                di[i] = drand48();
+                fi[i] = di[i];
+            }
+
+            int low = -1;
+            int lowscore = 0;
+
+            const char *names[] = {
+
+                "Forward Cartesian Double",
+                "Forward Interleaved Double",
+                "Forward Polar Double",
+                "Forward Magnitude Double",
+                "Forward Cartesian Float",
+                "Forward Interleaved Float",
+                "Forward Polar Float",
+                "Forward Magnitude Float",
+
+                "Inverse Cartesian Double",
+                "Inverse Interleaved Double",
+                "Inverse Polar Double",
+                "Inverse Cepstral Double",
+                "Inverse Cartesian Float",
+                "Inverse Interleaved Float",
+                "Inverse Polar Float",
+                "Inverse Cepstral Float"
+            };
+            os << names[type] << " :: ";
+
+            for (std::map<FFTImpl *, int>::iterator ci = candidates.begin();
+                 ci != candidates.end(); ++ci) {
+
+                FFTImpl *d = ci->first;
+
+                double mean = 0;
+
+                clock_t start = clock();
+                
+                for (int i = 0; i < iterations; ++i) {
+
+                    if (i == 0) {
+                        for (int j = 0; j < size; ++j) {
+                            dc[j] = 0;
+                            dd[j] = 0;
+                            fc[j] = 0;
+                            fd[j] = 0;
+                            fj[j] = 0;
+                            dj[j] = 0;
+                        }
+                    }
+
+                    switch (type) {
+                    case 0: d->forward(da, dc, dd); break;
+                    case 1: d->forwardInterleaved(da, dj); break;
+                    case 2: d->forwardPolar(da, dc, dd); break;
+                    case 3: d->forwardMagnitude(da, dc); break;
+                    case 4: d->forward(fa, fc, fd); break;
+                    case 5: d->forwardInterleaved(fa, fj); break;
+                    case 6: d->forwardPolar(fa, fc, fd); break;
+                    case 7: d->forwardMagnitude(fa, fc); break;
+                    case 8: d->inverse(da, db, dc); break;
+                    case 9: d->inverseInterleaved(di, dc); break;
+                    case 10: d->inversePolar(da, db, dc); break;
+                    case 11: d->inverseCepstral(da, dc); break;
+                    case 12: d->inverse(fa, fb, fc); break;
+                    case 13: d->inverseInterleaved(fi, fc); break;
+                    case 14: d->inversePolar(fa, fb, fc); break;
+                    case 15: d->inverseCepstral(fa, fc); break;
+                    }
+
+                    if (i == 0) {
+                        mean = 0;
+                        for (int j = 0; j < size; ++j) {
+                            mean += dc[j];
+                            mean += dd[j];
+                            mean += fc[j];
+                            mean += fd[j];
+                            mean += fj[j];
+                            mean += dj[j];
+                        }
+                        mean /= size * 6;
+                    }
+                }
+
+                clock_t end = clock();
+
+                os << float(end - start)/divisor << " (" << mean << ") ";
+
+                if (low == -1 || (end - start) < lowscore) {
+                    low = ci->second;
+                    lowscore = end - start;
+                }
+            }
+
+            os << std::endl;
+
+            os << "  size " << size << ", type " << type << ": fastest is " << low << " (time " << float(lowscore)/divisor << ")" << std::endl;
+
+            wins[low]++;
+        }
+        
+        delete[] fa;
+        delete[] fb;
+        delete[] fc;
+        delete[] fd;
+        delete[] da;
+        delete[] db;
+        delete[] dc;
+        delete[] dd;
+    }
+
+    while (!candidates.empty()) {
+        delete candidates.begin()->first;
+        candidates.erase(candidates.begin());
+    }
+
+    int bestscore = 0;
+    int best = -1;
+
+    for (std::map<int, int>::iterator wi = wins.begin(); wi != wins.end(); ++wi) {
+        if (best == -1 || wi->second > bestscore) {
+            best = wi->first;
+            bestscore = wi->second;
+        }
+    }
+
+    os << "overall winner is " << best << " with " << bestscore << " wins" << std::endl;
+
+    return os.str();
+}
+
+#endif
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqfft/test/Compares.h	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,77 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    bqfft
+
+    A small library wrapping various FFT implementations for some
+    common audio processing use cases.
+
+    Copyright 2007-2015 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#ifndef TEST_COMPARES_H
+#define TEST_COMPARES_H
+
+// These macros are used for comparing generated results, and they
+// aren't always going to be exact. Adding 0.1 to each value gives
+// us a little more fuzz in qFuzzyCompare (which ultimately does
+// the comparison).
+
+#define COMPARE_ZERO(a) \
+    QCOMPARE(a + 0.1, 0.1)
+
+#define COMPARE_ZERO_F(a) \
+    QCOMPARE(a + 0.1f, 0.1f)
+
+#define COMPARE_FUZZIER(a, b) \
+    QCOMPARE(a + 0.1, b + 0.1)
+
+#define COMPARE_FUZZIER_F(a, b) \
+    QCOMPARE(a + 0.1f, b + 0.1f)
+
+#define COMPARE_ALL(a, n) \
+    for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \
+        COMPARE_FUZZIER(a[cmp_i], n); \
+    }
+
+#define COMPARE_SCALED(a, b, s)						\
+    for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \
+        COMPARE_FUZZIER(a[cmp_i] / s, b[cmp_i]); \
+    }
+
+#define COMPARE_ALL_F(a, n) \
+    for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \
+        COMPARE_FUZZIER_F(a[cmp_i], n); \
+    }
+
+#define COMPARE_SCALED_F(a, b, s)						\
+    for (int cmp_i = 0; cmp_i < (int)(sizeof(a)/sizeof(a[0])); ++cmp_i) { \
+        COMPARE_FUZZIER_F(a[cmp_i] / s, b[cmp_i]); \
+    }
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqfft/test/TestFFT.h	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,510 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    bqfft
+
+    A small library wrapping various FFT implementations for some
+    common audio processing use cases.
+
+    Copyright 2007-2015 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#ifndef TEST_FFT_H
+#define TEST_FFT_H
+
+#include "bqfft/FFT.h"
+
+#include <QObject>
+#include <QtTest>
+
+#include <cstdio>
+
+#include "Compares.h"
+
+namespace breakfastquay {
+
+class TestFFT : public QObject
+{
+    Q_OBJECT
+
+private:
+    void idat() {
+        QTest::addColumn<QString>("implementation");
+        std::set<std::string> impls = FFT::getImplementations();
+        foreach (std::string i, impls) {
+            QTest::newRow(i.c_str()) << i.c_str();
+        }
+    }
+    QString ifetch() {
+        QFETCH(QString, implementation);
+        FFT::setDefaultImplementation(implementation.toLocal8Bit().data());
+        return implementation;
+    }
+
+    bool lackSingle() {
+        return !(FFT(4).getSupportedPrecisions() & FFT::SinglePrecision);
+    }
+    bool lackDouble() {
+        return !(FFT(4).getSupportedPrecisions() & FFT::DoublePrecision);
+    }
+
+private slots:
+
+    void checkD() {
+        QString impl = ifetch();
+    }
+
+    void dc() {
+        ifetch();
+	// DC-only signal. The DC bin is purely real
+	double in[] = { 1, 1, 1, 1 };
+	double re[3], im[3];
+	FFT(4).forward(in, re, im);
+	QCOMPARE(re[0], 4.0);
+	COMPARE_ZERO(re[1]);
+	COMPARE_ZERO(re[2]);
+	COMPARE_ALL(im, 0.0);
+	double back[4];
+        FFT(4).inverse(re, im, back);
+	COMPARE_SCALED(back, in, 4);
+    }
+
+    void sine() {
+        ifetch();
+	// Sine. Output is purely imaginary
+	double in[] = { 0, 1, 0, -1 };
+	double re[3], im[3];
+	FFT(4).forward(in, re, im);
+	COMPARE_ALL(re, 0.0);
+	COMPARE_ZERO(im[0]);
+	QCOMPARE(im[1], -2.0);
+	COMPARE_ZERO(im[2]);
+	double back[4];
+        FFT(4).inverse(re, im, back);
+	COMPARE_SCALED(back, in, 4);
+    }
+
+    void cosine() {
+        ifetch();
+	// Cosine. Output is purely real
+	double in[] = { 1, 0, -1, 0 };
+	double re[3], im[3];
+	FFT(4).forward(in, re, im);
+	COMPARE_ZERO(re[0]);
+	QCOMPARE(re[1], 2.0);
+	COMPARE_ZERO(re[2]);
+	COMPARE_ALL(im, 0.0);
+	double back[4];
+        FFT(4).inverse(re, im, back);
+	COMPARE_SCALED(back, in, 4);
+    }
+	
+    void sineCosine() {
+        ifetch();
+	// Sine and cosine mixed
+	double in[] = { 0.5, 1, -0.5, -1 };
+	double re[3], im[3];
+	FFT(4).forward(in, re, im);
+	COMPARE_ZERO(re[0]);
+	QCOMPARE(re[1], 1.0);
+	COMPARE_ZERO(re[2]);
+	COMPARE_ZERO(im[0]);
+	QCOMPARE(im[1], -2.0);
+	COMPARE_ZERO(im[2]);
+	double back[4];
+        FFT(4).inverse(re, im, back);
+	COMPARE_SCALED(back, in, 4);
+    }
+
+    void nyquist() {
+        ifetch();
+	double in[] = { 1, -1, 1, -1 };
+	double re[3], im[3];
+	FFT(4).forward(in, re, im);
+	COMPARE_ZERO(re[0]);
+	COMPARE_ZERO(re[1]);
+	QCOMPARE(re[2], 4.0);
+	COMPARE_ALL(im, 0.0);
+	double back[4];
+        FFT(4).inverse(re, im, back);
+	COMPARE_SCALED(back, in, 4);
+    }
+	
+    void interleaved() {
+        ifetch();
+	// Sine and cosine mixed, test output format
+	double in[] = { 0.5, 1, -0.5, -1 };
+	double out[6];
+	FFT(4).forwardInterleaved(in, out);
+	COMPARE_ZERO(out[0]);
+	COMPARE_ZERO(out[1]);
+	QCOMPARE(out[2], 1.0);
+	QCOMPARE(out[3], -2.0);
+	COMPARE_ZERO(out[4]);
+	COMPARE_ZERO(out[5]);
+	double back[4];
+        FFT(4).inverseInterleaved(out, back);
+	COMPARE_SCALED(back, in, 4);
+    }
+
+    void cosinePolar() {
+        ifetch();
+	double in[] = { 1, 0, -1, 0 };
+	double mag[3], phase[3];
+	FFT(4).forwardPolar(in, mag, phase);
+	COMPARE_ZERO(mag[0]);
+	QCOMPARE(mag[1], 2.0);
+	COMPARE_ZERO(mag[2]);
+        // No meaningful tests for phase[i] where mag[i]==0 (phase
+        // could legitimately be anything)
+	COMPARE_ZERO(phase[1]);
+	double back[4];
+        FFT(4).inversePolar(mag, phase, back);
+	COMPARE_SCALED(back, in, 4);
+    }
+
+    void sinePolar() {
+        ifetch();
+	double in[] = { 0, 1, 0, -1 };
+	double mag[3], phase[3];
+	FFT(4).forwardPolar(in, mag, phase);
+	COMPARE_ZERO(mag[0]);
+	QCOMPARE(mag[1], 2.0);
+	COMPARE_ZERO(mag[2]);
+        // No meaningful tests for phase[i] where mag[i]==0 (phase
+        // could legitimately be anything)
+	QCOMPARE(phase[1], -M_PI/2.0);
+	double back[4];
+        FFT(4).inversePolar(mag, phase, back);
+	COMPARE_SCALED(back, in, 4);
+    }
+
+    void magnitude() {
+        ifetch();
+	// Sine and cosine mixed
+	double in[] = { 0.5, 1, -0.5, -1 };
+	double out[3];
+	FFT(4).forwardMagnitude(in, out);
+	COMPARE_ZERO(out[0]);
+	QCOMPARE(float(out[1]), sqrtf(5.0));
+	COMPARE_ZERO(out[2]);
+    }
+
+    void dirac() {
+        ifetch();
+	double in[] = { 1, 0, 0, 0 };
+	double re[3], im[3];
+	FFT(4).forward(in, re, im);
+	QCOMPARE(re[0], 1.0);
+	QCOMPARE(re[1], 1.0);
+	QCOMPARE(re[2], 1.0);
+	COMPARE_ALL(im, 0.0);
+	double back[4];
+        FFT(4).inverse(re, im, back);
+	COMPARE_SCALED(back, in, 4);
+    }
+
+    void cepstrum() {
+        ifetch();
+	double in[] = { 1, 0, 0, 0, 1, 0, 0, 0 };
+	double mag[5];
+	FFT(8).forwardMagnitude(in, mag);
+	double cep[8];
+	FFT(8).inverseCepstral(mag, cep);
+	COMPARE_ZERO(cep[1]);
+	COMPARE_ZERO(cep[2]);
+	COMPARE_ZERO(cep[3]);
+	COMPARE_ZERO(cep[5]);
+	COMPARE_ZERO(cep[6]);
+	COMPARE_ZERO(cep[7]);
+	QVERIFY(fabs(-6.561181 - cep[0]/8) < 0.000001);
+	QVERIFY(fabs( 7.254329 - cep[4]/8) < 0.000001);
+    }
+
+    void forwardArrayBounds() {
+        ifetch();
+	// initialise bins to something recognisable, so we can tell
+	// if they haven't been written
+	double in[] = { 1, 1, -1, -1 };
+	double re[] = { 999, 999, 999, 999, 999 };
+	double im[] = { 999, 999, 999, 999, 999 };
+	FFT(4).forward(in, re+1, im+1);
+	// And check we haven't overrun the arrays
+	QCOMPARE(re[0], 999.0);
+	QCOMPARE(im[0], 999.0);
+	QCOMPARE(re[4], 999.0);
+	QCOMPARE(im[4], 999.0);
+    }
+
+    void inverseArrayBounds() {
+        ifetch();
+	// initialise bins to something recognisable, so we can tell
+	// if they haven't been written
+	double re[] = { 0, 1, 0 };
+	double im[] = { 0, -2, 0 };
+	double out[] = { 999, 999, 999, 999, 999, 999 };
+	FFT(4).inverse(re, im, out+1);
+	// And check we haven't overrun the arrays
+	QCOMPARE(out[0], 999.0);
+	QCOMPARE(out[5], 999.0);
+    }
+
+    void checkF() {
+        QString impl = ifetch();
+    }
+
+    void dcF() {
+        ifetch();
+	// DC-only signal. The DC bin is purely real
+	float in[] = { 1, 1, 1, 1 };
+	float re[3], im[3];
+	FFT(4).forward(in, re, im);
+	QCOMPARE(re[0], 4.0f);
+	COMPARE_ZERO_F(re[1]);
+	COMPARE_ZERO_F(re[2]);
+	COMPARE_ALL_F(im, 0.0f);
+	float back[4];
+        FFT(4).inverse(re, im, back);
+	COMPARE_SCALED_F(back, in, 4);
+    }
+
+    void sineF() {
+        ifetch();
+	// Sine. Output is purely imaginary
+	float in[] = { 0, 1, 0, -1 };
+	float re[3], im[3];
+	FFT(4).forward(in, re, im);
+	COMPARE_ALL_F(re, 0.0f);
+	COMPARE_ZERO_F(im[0]);
+	QCOMPARE(im[1], -2.0f);
+	COMPARE_ZERO_F(im[2]);
+	float back[4];
+        FFT(4).inverse(re, im, back);
+	COMPARE_SCALED_F(back, in, 4);
+    }
+
+    void cosineF() {
+        ifetch();
+	// Cosine. Output is purely real
+	float in[] = { 1, 0, -1, 0 };
+	float re[3], im[3];
+	FFT(4).forward(in, re, im);
+	COMPARE_ZERO_F(re[0]);
+	QCOMPARE(re[1], 2.0f);
+	COMPARE_ZERO_F(re[2]);
+	COMPARE_ALL_F(im, 0.0f);
+	float back[4];
+        FFT(4).inverse(re, im, back);
+	COMPARE_SCALED_F(back, in, 4);
+    }
+	
+    void sineCosineF() {
+        ifetch();
+	// Sine and cosine mixed
+	float in[] = { 0.5, 1, -0.5, -1 };
+	float re[3], im[3];
+	FFT(4).forward(in, re, im);
+	COMPARE_ZERO_F(re[0]);
+	QCOMPARE(re[1], 1.0f);
+	COMPARE_ZERO_F(re[2]);
+	COMPARE_ZERO_F(im[0]);
+	QCOMPARE(im[1], -2.0f);
+	COMPARE_ZERO_F(im[2]);
+	float back[4];
+        FFT(4).inverse(re, im, back);
+	COMPARE_SCALED_F(back, in, 4);
+    }
+
+    void nyquistF() {
+        ifetch();
+	float in[] = { 1, -1, 1, -1 };
+	float re[3], im[3];
+	FFT(4).forward(in, re, im);
+	COMPARE_ZERO_F(re[0]);
+	COMPARE_ZERO_F(re[1]);
+	QCOMPARE(re[2], 4.0f);
+	COMPARE_ALL_F(im, 0.0f);
+	float back[4];
+        FFT(4).inverse(re, im, back);
+	COMPARE_SCALED_F(back, in, 4);
+    }
+	
+    void interleavedF() {
+        ifetch();
+	// Sine and cosine mixed, test output format
+	float in[] = { 0.5, 1, -0.5, -1 };
+	float out[6];
+	FFT(4).forwardInterleaved(in, out);
+	COMPARE_ZERO_F(out[0]);
+	COMPARE_ZERO_F(out[1]);
+	QCOMPARE(out[2], 1.0f);
+	QCOMPARE(out[3], -2.0f);
+	COMPARE_ZERO_F(out[4]);
+	COMPARE_ZERO_F(out[5]);
+	float back[4];
+        FFT(4).inverseInterleaved(out, back);
+	COMPARE_SCALED_F(back, in, 4);
+    }
+
+    void cosinePolarF() {
+        ifetch();
+	float in[] = { 1, 0, -1, 0 };
+	float mag[3], phase[3];
+	FFT(4).forwardPolar(in, mag, phase);
+	COMPARE_ZERO_F(mag[0]);
+	QCOMPARE(mag[1], 2.0f);
+	COMPARE_ZERO_F(mag[2]);
+        // No meaningful tests for phase[i] where mag[i]==0 (phase
+        // could legitimately be anything)
+	COMPARE_ZERO_F(phase[1]);
+	float back[4];
+        FFT(4).inversePolar(mag, phase, back);
+	COMPARE_SCALED_F(back, in, 4);
+    }
+
+    void sinePolarF() {
+        ifetch();
+	float in[] = { 0, 1, 0, -1 };
+	float mag[3], phase[3];
+	FFT(4).forwardPolar(in, mag, phase);
+	COMPARE_ZERO_F(mag[0]);
+	QCOMPARE(mag[1], 2.0f);
+	COMPARE_ZERO_F(mag[2]);
+        // No meaningful tests for phase[i] where mag[i]==0 (phase
+        // could legitimately be anything)
+	QCOMPARE(phase[1], -float(M_PI)/2.0f);
+	float back[4];
+        FFT(4).inversePolar(mag, phase, back);
+	COMPARE_SCALED_F(back, in, 4);
+    }
+
+    void magnitudeF() {
+        ifetch();
+	// Sine and cosine mixed
+	float in[] = { 0.5, 1, -0.5, -1 };
+	float out[3];
+	FFT(4).forwardMagnitude(in, out);
+	COMPARE_ZERO_F(out[0]);
+	QCOMPARE(float(out[1]), sqrtf(5.0f));
+	COMPARE_ZERO_F(out[2]);
+    }
+
+    void diracF() {
+        ifetch();
+	float in[] = { 1, 0, 0, 0 };
+	float re[3], im[3];
+	FFT(4).forward(in, re, im);
+	QCOMPARE(re[0], 1.0f);
+	QCOMPARE(re[1], 1.0f);
+	QCOMPARE(re[2], 1.0f);
+	COMPARE_ALL_F(im, 0.0f);
+	float back[4];
+        FFT(4).inverse(re, im, back);
+	COMPARE_SCALED_F(back, in, 4);
+    }
+
+    void cepstrumF() {
+        ifetch();
+	float in[] = { 1, 0, 0, 0, 1, 0, 0, 0 };
+	float mag[5];
+	FFT(8).forwardMagnitude(in, mag);
+	float cep[8];
+	FFT(8).inverseCepstral(mag, cep);
+	COMPARE_ZERO_F(cep[1]);
+	COMPARE_ZERO_F(cep[2]);
+	COMPARE_ZERO_F(cep[3]);
+	COMPARE_ZERO_F(cep[5]);
+	COMPARE_ZERO_F(cep[6]);
+	COMPARE_ZERO_F(cep[7]);
+	QVERIFY(fabsf(-6.561181 - cep[0]/8) < 0.000001);
+	QVERIFY(fabsf( 7.254329 - cep[4]/8) < 0.000001);
+    }
+
+    void forwardArrayBoundsF() {
+        ifetch();
+	// initialise bins to something recognisable, so we can tell
+	// if they haven't been written
+	float in[] = { 1, 1, -1, -1 };
+	float re[] = { 999, 999, 999, 999, 999 };
+	float im[] = { 999, 999, 999, 999, 999 };
+	FFT(4).forward(in, re+1, im+1);
+	// And check we haven't overrun the arrays
+	QCOMPARE(re[0], 999.0f);
+	QCOMPARE(im[0], 999.0f);
+	QCOMPARE(re[4], 999.0f);
+	QCOMPARE(im[4], 999.0f);
+    }
+
+    void inverseArrayBoundsF() {
+        ifetch();
+	// initialise bins to something recognisable, so we can tell
+	// if they haven't been written
+	float re[] = { 0, 1, 0 };
+	float im[] = { 0, -2, 0 };
+	float out[] = { 999, 999, 999, 999, 999, 999 };
+	FFT(4).inverse(re, im, out+1);
+	// And check we haven't overrun the arrays
+	QCOMPARE(out[0], 999.0f);
+	QCOMPARE(out[5], 999.0f);
+    }
+
+    void checkD_data() { idat(); }
+    void dc_data() { idat(); }
+    void sine_data() { idat(); }
+    void cosine_data() { idat(); }
+    void sineCosine_data() { idat(); }
+    void sineCosineDC_data() { idat(); }
+    void nyquist_data() { idat(); }
+    void interleaved_data() { idat(); }
+    void cosinePolar_data() { idat(); }
+    void sinePolar_data() { idat(); }
+    void magnitude_data() { idat(); }
+    void dirac_data() { idat(); }
+    void cepstrum_data() { idat(); }
+    void forwardArrayBounds_data() { idat(); }
+    void inverseArrayBounds_data() { idat(); }
+
+    void checkF_data() { idat(); }
+    void dcF_data() { idat(); }
+    void sineF_data() { idat(); }
+    void cosineF_data() { idat(); }
+    void sineCosineF_data() { idat(); }
+    void sineCosineDCF_data() { idat(); }
+    void nyquistF_data() { idat(); }
+    void interleavedF_data() { idat(); }
+    void cosinePolarF_data() { idat(); }
+    void sinePolarF_data() { idat(); }
+    void magnitudeF_data() { idat(); }
+    void diracF_data() { idat(); }
+    void cepstrumF_data() { idat(); }
+    void forwardArrayBoundsF_data() { idat(); }
+    void inverseArrayBoundsF_data() { idat(); }
+};
+
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqfft/test/main.cpp	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,27 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+/* Copyright Chris Cannam - All Rights Reserved */
+
+#include "TestFFT.h"
+#include <QtTest>
+
+#include <iostream>
+
+int main(int argc, char *argv[])
+{
+    int good = 0, bad = 0;
+
+    QCoreApplication app(argc, argv);
+
+    breakfastquay::TestFFT tf;
+    if (QTest::qExec(&tf, argc, argv) == 0) ++good;
+    else ++bad;
+
+    if (bad > 0) {
+	std::cerr << "\n********* " << bad << " test suite(s) failed!\n" << std::endl;
+	return 1;
+    } else {
+        std::cerr << "All tests passed" << std::endl;
+        return 0;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqfft/test/test.pro	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,30 @@
+
+TEMPLATE = app
+TARGET = test-fft
+win*: TARGET = "TestFFT"
+
+QT += testlib
+QT -= gui
+
+DESTDIR = .
+QMAKE_LIBDIR += . ..
+
+LIBS += -lbqfft -lfftw3 -lfftw3f
+
+INCLUDEPATH += . .. ../../bqvec
+DEPENDPATH += . .. ../../bqvec
+
+HEADERS += TestFFT.h
+SOURCES += main.cpp
+
+!win32 {
+    !macx* {
+        QMAKE_POST_LINK=valgrind $${DESTDIR}/$${TARGET}
+    }
+    macx* {
+        QMAKE_POST_LINK=$${DESTDIR}/$${TARGET}.app/Contents/MacOS/$${TARGET}
+    }
+}
+
+win32-g++:QMAKE_POST_LINK=$${DESTDIR}$${TARGET}.exe
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqvec/COPYING	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,50 @@
+
+The following terms apply to the code in the src/ directory:
+
+    Copyright 2007-2014 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+
+
+The following terms apply to the code in the pommier/ directory:
+
+    Copyright (C) 2011  Julien Pommier
+
+    This software is provided 'as-is', without any express or implied
+    warranty.  In no event will the authors be held liable for any damages
+    arising from the use of this software.
+
+    Permission is granted to anyone to use this software for any purpose,
+    including commercial applications, and to alter it and redistribute it
+    freely, subject to the following restrictions:
+
+    1. The origin of this software must not be misrepresented; you must not
+       claim that you wrote the original software. If you use this software
+       in a product, an acknowledgment in the product documentation would be
+       appreciated but is not required.
+    2. Altered source versions must be plainly marked as such, and must not be
+       misrepresented as being the original software.
+    3. This notice may not be removed or altered from any source distribution.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqvec/Makefile	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,90 @@
+
+# Add to VECTOR_DEFINES the relevant options for your desired
+# third-party library support.
+#
+# Available options are
+#
+#  -DHAVE_IPP    Intel's Integrated Performance Primitives are available
+#  -DHAVE_VDSP   Apple's Accelerate framework is available
+#
+# These are optional (they affect performance, not function) and you
+# may define more than one of them.
+# 
+# Add any relevant -I flags for include paths as well.
+#
+# Note that you must supply the same flags when including bqvec
+# headers later as you are using now when compiling the library. (You
+# may find it simplest to just add the bqvec source files to your
+# application's build system and not build a bqvec library at all.)
+
+VECTOR_DEFINES	:= -DHAVE_VDSP
+
+
+# Add to ALLOCATOR_DEFINES options relating to aligned malloc.
+#
+# Available options are
+#
+#  -DHAVE_POSIX_MEMALIGN     The posix_memalign call is available in sys/mman.h
+#  -DLACK_POSIX_MEMALIGN     The posix_memalign call is not available
+#
+#  -DMALLOC_IS_ALIGNED       The malloc call already returns aligned memory
+#  -DMALLOC_IS_NOT_ALIGNED   The malloc call does not return aligned memory
+#
+#  -DUSE_OWN_ALIGNED_MALLOC  No aligned malloc is available, roll your own
+#
+#  -DLACK_BAD_ALLOC          The C++ library lacks the std::bad_alloc exception
+#
+# Here "aligned" is assumed to mean "aligned enough for whatever
+# vector stuff the space will be used for" which most likely means
+# 16-byte alignment.
+#
+# The default is to use _aligned_malloc when building with Visual C++,
+# system malloc when building on OS/X, and posix_memalign otherwise.
+#
+# Note that you must supply the same flags when including bqvec
+# headers later as you are using now when compiling the library. (You
+# may find it simplest to just add the bqvec source files to your
+# application's build system and not build a bqvec library at all.)
+
+ALLOCATOR_DEFINES := -DMALLOC_IS_ALIGNED
+
+
+SRC_DIR	:= src
+HEADER_DIR := bqvec
+
+SOURCES	:= $(wildcard $(SRC_DIR)/*.cpp)
+HEADERS	:= $(wildcard $(HEADER_DIR)/*.h) $(wildcard $(SRC_DIR)/*.h)
+
+OBJECTS	:= $(SOURCES:.cpp=.o)
+OBJECTS	:= $(OBJECTS:.c=.o)
+
+CXXFLAGS := $(VECTOR_DEFINES) $(ALLOCATOR_DEFINES) -I$(HEADER_DIR) -O3 -ffast-math -Wall -Werror -fpic
+
+LIBRARY	:= libbqvec.a
+
+all:	$(LIBRARY)
+
+$(LIBRARY):	$(OBJECTS)
+	$(AR) rc $@ $^
+
+clean:		
+	rm -f $(OBJECTS)
+
+distclean:	clean
+	rm -f $(LIBRARY)
+
+depend:
+	makedepend -Y -fMakefile $(SOURCES) $(HEADERS)
+
+
+# DO NOT DELETE
+
+src/VectorOpsComplex.o: bqvec/VectorOpsComplex.h bqvec/VectorOps.h
+src/VectorOpsComplex.o: bqvec/Restrict.h bqvec/ComplexTypes.h
+src/Allocators.o: bqvec/Allocators.h bqvec/VectorOps.h bqvec/Restrict.h
+bqvec/RingBuffer.o: bqvec/Barrier.h bqvec/Allocators.h bqvec/VectorOps.h
+bqvec/RingBuffer.o: bqvec/Restrict.h
+bqvec/VectorOpsComplex.o: bqvec/VectorOps.h bqvec/Restrict.h
+bqvec/VectorOpsComplex.o: bqvec/ComplexTypes.h
+bqvec/VectorOps.o: bqvec/Restrict.h
+bqvec/Allocators.o: bqvec/VectorOps.h bqvec/Restrict.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqvec/README.txt	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,17 @@
+
+bqvec
+=====
+
+A small library for efficient vector arithmetic and allocation in C++
+using raw C pointer arrays.
+
+C++ standard required: C++98 (does not use C++11)
+
+Copyright 2007-2015 Particular Programs Ltd.
+
+This code originated as part of the Rubber Band Library written by the
+same authors (see https://bitbucket.org/breakfastquay/rubberband/).
+It has been pulled out into a separate library and relicensed under a
+more permissive licence: a BSD/MIT-style licence, as opposed to the
+GPL used for Rubber Band.  See the file COPYING for details.
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqvec/bqvec/Allocators.h	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,286 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    bqvec
+
+    A small library for vector arithmetic and allocation in C++ using
+    raw C pointer arrays.
+
+    Copyright 2007-2015 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#ifndef BQVEC_ALLOCATORS_H
+#define BQVEC_ALLOCATORS_H
+
+/*
+ * Aligned and per-channel allocators and deallocators for raw C array
+ * buffers.
+ */
+
+#include <new> // for std::bad_alloc
+#include <stdlib.h>
+
+#ifndef HAVE_POSIX_MEMALIGN
+#ifndef _WIN32
+#ifndef __APPLE__
+#ifndef LACK_POSIX_MEMALIGN
+#define HAVE_POSIX_MEMALIGN
+#endif
+#endif
+#endif
+#endif
+
+#ifndef MALLOC_IS_NOT_ALIGNED
+#ifdef __APPLE_
+#ifndef MALLOC_IS_ALIGNED
+#define MALLOC_IS_ALIGNED
+#endif
+#endif
+#endif
+
+#ifdef HAVE_POSIX_MEMALIGN
+#include <sys/mman.h>
+#endif
+
+#ifdef LACK_BAD_ALLOC
+namespace std { struct bad_alloc { }; }
+#endif
+
+namespace breakfastquay {
+
+template <typename T>
+T *allocate(size_t count)
+{
+    void *ptr = 0;
+    // 32-byte alignment is required for at least OpenMAX
+    static const int alignment = 32;
+#ifdef USE_OWN_ALIGNED_MALLOC
+    // Alignment must be a power of two, bigger than the pointer
+    // size. Stuff the actual malloc'd pointer in just before the
+    // returned value.  This is the least desirable way to do this --
+    // the other options below are all better
+    size_t allocd = count * sizeof(T) + alignment;
+    void *buf = malloc(allocd);
+    if (buf) {
+        char *adj = (char *)buf;
+        while ((unsigned long long)adj & (alignment-1)) --adj;
+        ptr = ((char *)adj) + alignment;
+        ((void **)ptr)[-1] = buf;
+    }
+#else /* !USE_OWN_ALIGNED_MALLOC */
+#ifdef HAVE_POSIX_MEMALIGN
+    if (posix_memalign(&ptr, alignment, count * sizeof(T))) {
+        ptr = malloc(count * sizeof(T));
+    }
+#else /* !HAVE_POSIX_MEMALIGN */
+#ifdef __MSVC__
+    ptr = _aligned_malloc(count * sizeof(T), alignment);
+#else /* !__MSVC__ */
+#ifndef MALLOC_IS_ALIGNED
+#error "No aligned malloc available: define MALLOC_IS_ALIGNED to stick with system malloc, HAVE_POSIX_MEMALIGN if posix_memalign is available, or USE_OWN_ALIGNED_MALLOC to roll our own"
+#endif
+    // Note that malloc always aligns to 16 byte boundaries on OS/X
+    ptr = malloc(count * sizeof(T));
+    (void)alignment; // avoid compiler warning for unused 
+#endif /* !__MSVC__ */
+#endif /* !HAVE_POSIX_MEMALIGN */
+#endif /* !USE_OWN_ALIGNED_MALLOC */
+    if (!ptr) {
+#ifndef NO_EXCEPTIONS
+        throw(std::bad_alloc());
+#else
+        abort();
+#endif
+    }
+    return (T *)ptr;
+}
+
+#ifdef HAVE_IPP
+
+template <>
+float *allocate(size_t count);
+
+template <>
+double *allocate(size_t count);
+
+#endif
+	
+template <typename T>
+T *allocate_and_zero(size_t count)
+{
+    T *ptr = allocate<T>(count);
+    for (size_t i = 0; i < count; ++i) {
+        ptr[i] = T();
+    }
+    return ptr;
+}
+
+template <typename T>
+void deallocate(T *ptr)
+{
+#ifdef USE_OWN_ALIGNED_MALLOC
+    if (ptr) free(((void **)ptr)[-1]);
+#else /* !USE_OWN_ALIGNED_MALLOC */
+#ifdef __MSVC__
+    if (ptr) _aligned_free((void *)ptr);
+#else /* !__MSVC__ */
+    if (ptr) free((void *)ptr);
+#endif /* !__MSVC__ */
+#endif /* !USE_OWN_ALIGNED_MALLOC */
+}
+
+#ifdef HAVE_IPP
+
+template <>
+void deallocate(float *);
+
+template <>
+void deallocate(double *);
+
+#endif
+
+/// Reallocate preserving contents but leaving additional memory uninitialised	
+template <typename T>
+T *reallocate(T *ptr, size_t oldcount, size_t count)
+{
+    T *newptr = allocate<T>(count);
+    if (oldcount && ptr) {
+        for (size_t i = 0; i < oldcount && i < count; ++i) {
+            newptr[i] = ptr[i];
+        }
+    }
+    if (ptr) deallocate<T>(ptr);
+    return newptr;
+}
+
+/// Reallocate, zeroing all contents
+template <typename T>
+T *reallocate_and_zero(T *ptr, size_t oldcount, size_t count)
+{
+    ptr = reallocate(ptr, oldcount, count);
+    for (size_t i = 0; i < count; ++i) {
+        ptr[i] = T();
+    }
+    return ptr;
+}
+	
+/// Reallocate preserving contents and zeroing any additional memory	
+template <typename T>
+T *reallocate_and_zero_extension(T *ptr, size_t oldcount, size_t count)
+{
+    ptr = reallocate(ptr, oldcount, count);
+    if (count > oldcount) {
+        for (size_t i = oldcount; i < count; ++i) {
+            ptr[i] = T();
+        }
+    }
+    return ptr;
+}
+
+template <typename T>
+T **allocate_channels(size_t channels, size_t count)
+{
+    // We don't want to use the aligned allocate for the channel
+    // pointers, it might even make things slower
+    T **ptr = new T *[channels];
+    for (size_t c = 0; c < channels; ++c) {
+        ptr[c] = allocate<T>(count);
+    }
+    return ptr;
+}
+	
+template <typename T>
+T **allocate_and_zero_channels(size_t channels, size_t count)
+{
+    // We don't want to use the aligned allocate for the channel
+    // pointers, it might even make things slower
+    T **ptr = new T *[channels];
+    for (size_t c = 0; c < channels; ++c) {
+        ptr[c] = allocate_and_zero<T>(count);
+    }
+    return ptr;
+}
+
+template <typename T>
+void deallocate_channels(T **ptr, size_t channels)
+{
+    if (!ptr) return;
+    for (size_t c = 0; c < channels; ++c) {
+        deallocate<T>(ptr[c]);
+    }
+    delete[] ptr;
+}
+	
+template <typename T>
+T **reallocate_channels(T **ptr,
+                        size_t oldchannels, size_t oldcount,
+                        size_t channels, size_t count)
+{
+    T **newptr = allocate_channels<T>(channels, count);
+    if (oldcount && ptr) {
+        for (size_t c = 0; c < channels; ++c) {
+            for (size_t i = 0; i < oldcount && i < count; ++i) {
+                newptr[c][i] = ptr[c][i];
+            }
+        }
+    } 
+    if (ptr) deallocate_channels<T>(ptr, oldchannels);
+    return newptr;
+}
+	
+template <typename T>
+T **reallocate_and_zero_extend_channels(T **ptr,
+                                        size_t oldchannels, size_t oldcount,
+                                        size_t channels, size_t count)
+{
+    T **newptr = allocate_and_zero_channels<T>(channels, count);
+    if (oldcount && ptr) {
+        for (size_t c = 0; c < channels; ++c) {
+            for (size_t i = 0; i < oldcount && i < count; ++i) {
+                newptr[c][i] = ptr[c][i];
+            }
+        }
+    } 
+    if (ptr) deallocate_channels<T>(ptr, oldchannels);
+    return newptr;
+}
+
+/// RAII class to call deallocate() on destruction
+template <typename T>
+class Deallocator
+{
+public:
+    Deallocator(T *t) : m_t(t) { }
+    ~Deallocator() { deallocate<T>(m_t); }
+private:
+    T *m_t;
+};
+
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqvec/bqvec/Barrier.h	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,62 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    bqvec
+
+    A small library for vector arithmetic and allocation in C++ using
+    raw C pointer arrays.
+
+    Copyright 2007-2015 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#ifndef BQVEC_BARRIER_H
+#define BQVEC_BARRIER_H
+
+namespace breakfastquay {
+    extern void system_memorybarrier();
+}
+
+#if defined _WIN32
+
+#define BQ_MBARRIER() ::breakfastquay::system_memorybarrier()
+
+#elif defined __APPLE__
+
+#include <libkern/OSAtomic.h>
+#define BQ_MBARRIER() OSMemoryBarrier()
+
+#elif (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)
+
+#define BQ_MBARRIER() __sync_synchronize()
+
+#else
+
+#define BQ_MBARRIER() ::breakfastquay::system_memorybarrier()
+
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqvec/bqvec/ComplexTypes.h	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,52 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    bqvec
+
+    A small library for vector arithmetic and allocation in C++ using
+    raw C pointer arrays.
+
+    Copyright 2007-2015 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#ifndef BQVEC_COMPLEX_TYPES_H
+#define BQVEC_COMPLEX_TYPES_H
+
+namespace breakfastquay {
+
+#ifndef NO_COMPLEX_TYPES
+// Convertible with other complex types that store re+im consecutively
+typedef double bq_complex_element_t;
+struct bq_complex_t {
+    bq_complex_element_t re;
+    bq_complex_element_t im;
+};
+#endif
+
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqvec/bqvec/Range.h	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,57 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    bqvec
+
+    A small library for vector arithmetic and allocation in C++ using
+    raw C pointer arrays.
+
+    Copyright 2007-2015 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#ifndef BQVEC_RANGE_H
+#define BQVEC_RANGE_H
+
+namespace breakfastquay {
+
+/** Check whether an integer index is in range for a container,
+    avoiding overflows and signed/unsigned comparison warnings.
+*/
+template<typename T, typename C>
+bool in_range_for(const C &container, T i)
+{
+    if (i < 0) return false;
+    if (sizeof(T) > sizeof(typename C::size_type)) {
+	return i < static_cast<T>(container.size());
+    } else {
+	return static_cast<typename C::size_type>(i) < container.size();
+    }
+}
+
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqvec/bqvec/Restrict.h	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,51 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    bqvec
+
+    A small library for vector arithmetic and allocation in C++ using
+    raw C pointer arrays.
+
+    Copyright 2007-2015 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#ifndef BQVEC_RESTRICT_H
+#define BQVEC_RESTRICT_H
+
+#ifdef __MSVC__
+#define BQ_R__ __restrict
+#endif
+
+#ifdef __GNUC__
+#define BQ_R__ __restrict__
+#endif
+
+#ifndef BQ_R__
+#define BQ_R__
+#endif
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqvec/bqvec/RingBuffer.h	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,516 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    bqvec
+
+    A small library for vector arithmetic and allocation in C++ using
+    raw C pointer arrays.
+
+    Copyright 2007-2015 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#ifndef BQVEC_RINGBUFFER_H
+#define BQVEC_RINGBUFFER_H
+
+#include <sys/types.h>
+
+//#define DEBUG_RINGBUFFER 1
+
+#include "Barrier.h"
+#include "Allocators.h"
+
+#include <iostream>
+
+namespace breakfastquay {
+
+/**
+ * RingBuffer implements a lock-free ring buffer for one writer and
+ * one reader, that is to be used to store a sample type T.
+ *
+ * RingBuffer is thread-safe provided only one thread writes and only
+ * one thread reads.
+ */
+template <typename T>
+class RingBuffer
+{
+public:
+    /**
+     * Create a ring buffer with room to write n samples.
+     *
+     * Note that the internal storage size will actually be n+1
+     * samples, as one element is unavailable for administrative
+     * reasons.  Since the ring buffer performs best if its size is a
+     * power of two, this means n should ideally be some power of two
+     * minus one.
+     */
+    RingBuffer(int n);
+
+    virtual ~RingBuffer();
+
+    /**
+     * Return the total capacity of the ring buffer in samples.
+     * (This is the argument n passed to the constructor.)
+     */
+    int getSize() const;
+
+    /**
+     * Return a new ring buffer (allocated with "new" -- caller must
+     * delete when no longer needed) of the given size, containing the
+     * same data as this one.  If another thread reads from or writes
+     * to this buffer during the call, the results may be incomplete
+     * or inconsistent.  If this buffer's data will not fit in the new
+     * size, the contents are undefined.
+     */
+    RingBuffer<T> *resized(int newSize) const;
+
+    /**
+     * Reset read and write pointers, thus emptying the buffer.
+     * Should be called from the write thread.
+     */
+    void reset();
+
+    /**
+     * Return the amount of data available for reading, in samples.
+     */
+    int getReadSpace() const;
+
+    /**
+     * Return the amount of space available for writing, in samples.
+     */
+    int getWriteSpace() const;
+
+    /**
+     * Read n samples from the buffer.  If fewer than n are available,
+     * the remainder will be zeroed out.  Returns the number of
+     * samples actually read.
+     *
+     * This is a template function, taking an argument S for the target
+     * sample type, which is permitted to differ from T if the two
+     * types are compatible for arithmetic operations.
+     */
+    template <typename S>
+    int read(S *const BQ_R__ destination, int n);
+
+    /**
+     * Read n samples from the buffer, adding them to the destination.
+     * If fewer than n are available, the remainder will be left
+     * alone.  Returns the number of samples actually read.
+     *
+     * This is a template function, taking an argument S for the target
+     * sample type, which is permitted to differ from T if the two
+     * types are compatible for arithmetic operations.
+     */
+    template <typename S>
+    int readAdding(S *const BQ_R__ destination, int n);
+
+    /**
+     * Read one sample from the buffer.  If no sample is available,
+     * this will silently return zero.  Calling this repeatedly is
+     * obviously slower than calling read once, but it may be good
+     * enough if you don't want to allocate a buffer to read into.
+     */
+    T readOne();
+
+    /**
+     * Read n samples from the buffer, if available, without advancing
+     * the read pointer -- i.e. a subsequent read() or skip() will be
+     * necessary to empty the buffer.  If fewer than n are available,
+     * the remainder will be zeroed out.  Returns the number of
+     * samples actually read.
+     */
+    int peek(T *const BQ_R__ destination, int n) const;
+
+    /**
+     * Read one sample from the buffer, if available, without
+     * advancing the read pointer -- i.e. a subsequent read() or
+     * skip() will be necessary to empty the buffer.  Returns zero if
+     * no sample was available.
+     */
+    T peekOne() const;
+
+    /**
+     * Pretend to read n samples from the buffer, without actually
+     * returning them (i.e. discard the next n samples).  Returns the
+     * number of samples actually available for discarding.
+     */
+    int skip(int n);
+
+    /**
+     * Write n samples to the buffer.  If insufficient space is
+     * available, not all samples may actually be written.  Returns
+     * the number of samples actually written.
+     *
+     * This is a template function, taking an argument S for the source
+     * sample type, which is permitted to differ from T if the two
+     * types are compatible for assignment.
+     */
+    template <typename S>
+    int write(const S *const BQ_R__ source, int n);
+
+    /**
+     * Write n zero-value samples to the buffer.  If insufficient
+     * space is available, not all zeros may actually be written.
+     * Returns the number of zeroes actually written.
+     */
+    int zero(int n);
+
+protected:
+    T *const BQ_R__ m_buffer;
+    int             m_writer;
+    int             m_reader;
+    const int       m_size;
+
+    int readSpaceFor(int w, int r) const {
+        int space;
+        if (w > r) space = w - r;
+        else if (w < r) space = (w + m_size) - r;
+        else space = 0;
+        return space;
+    }
+
+    int writeSpaceFor(int w, int r) const {
+        int space = (r + m_size - w - 1);
+        if (space >= m_size) space -= m_size;
+        return space;
+    }
+
+private:
+    RingBuffer(const RingBuffer &); // not provided
+    RingBuffer &operator=(const RingBuffer &); // not provided
+};
+
+template <typename T>
+RingBuffer<T>::RingBuffer(int n) :
+    m_buffer(allocate<T>(n + 1)),
+    m_writer(0),
+    m_size(n + 1)
+{
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T>[" << this << "]::RingBuffer(" << n << ")" << std::endl;
+#endif
+
+    m_reader = 0;
+}
+
+template <typename T>
+RingBuffer<T>::~RingBuffer()
+{
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T>[" << this << "]::~RingBuffer" << std::endl;
+#endif
+
+    deallocate(m_buffer);
+}
+
+template <typename T>
+int
+RingBuffer<T>::getSize() const
+{
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T>[" << this << "]::getSize(): " << m_size-1 << std::endl;
+#endif
+
+    return m_size - 1;
+}
+
+template <typename T>
+RingBuffer<T> *
+RingBuffer<T>::resized(int newSize) const
+{
+    RingBuffer<T> *newBuffer = new RingBuffer<T>(newSize);
+
+    int w = m_writer;
+    int r = m_reader;
+
+    while (r != w) {
+        T value = m_buffer[r];
+        newBuffer->write(&value, 1);
+        if (++r == m_size) r = 0;
+    }
+
+    return newBuffer;
+}
+
+template <typename T>
+void
+RingBuffer<T>::reset()
+{
+#ifdef DEBUG_RINGBUFFER
+    std::cerr << "RingBuffer<T>[" << this << "]::reset" << std::endl;
+#endif
+
+    m_reader = m_writer;
+}
+
+template <typename T>
+int
+RingBuffer<T>::getReadSpace() const
+{
+    return readSpaceFor(m_writer, m_reader);
+}
+
+template <typename T>
+int
+RingBuffer<T>::getWriteSpace() const
+{
+    return writeSpaceFor(m_writer, m_reader);
+}
+
+template <typename T>
+template <typename S>
+int
+RingBuffer<T>::read(S *const BQ_R__ destination, int n)
+{
+    int w = m_writer;
+    int r = m_reader;
+
+    int available = readSpaceFor(w, r);
+    if (n > available) {
+	std::cerr << "WARNING: RingBuffer::read: " << n << " requested, only "
+                  << available << " available" << std::endl;
+	n = available;
+    }
+    if (n == 0) return n;
+
+    int here = m_size - r;
+    T *const BQ_R__ bufbase = m_buffer + r;
+
+    if (here >= n) {
+        v_convert(destination, bufbase, n);
+    } else {
+        v_convert(destination, bufbase, here);
+        v_convert(destination + here, m_buffer, n - here);
+    }
+
+    r += n;
+    while (r >= m_size) r -= m_size;
+
+    BQ_MBARRIER();
+    m_reader = r;
+
+    return n;
+}
+
+template <typename T>
+template <typename S>
+int
+RingBuffer<T>::readAdding(S *const BQ_R__ destination, int n)
+{
+    int w = m_writer;
+    int r = m_reader;
+
+    int available = readSpaceFor(w, r);
+    if (n > available) {
+	std::cerr << "WARNING: RingBuffer::read: " << n << " requested, only "
+                  << available << " available" << std::endl;
+	n = available;
+    }
+    if (n == 0) return n;
+
+    int here = m_size - r;
+    T *const BQ_R__ bufbase = m_buffer + r;
+
+    if (here >= n) {
+        v_add(destination, bufbase, n);
+    } else {
+        v_add(destination, bufbase, here);
+        v_add(destination + here, m_buffer, n - here);
+    }
+
+    r += n;
+    while (r >= m_size) r -= m_size;
+
+    BQ_MBARRIER();
+    m_reader = r;
+
+    return n;
+}
+
+template <typename T>
+T
+RingBuffer<T>::readOne()
+{
+    int w = m_writer;
+    int r = m_reader;
+
+    if (w == r) {
+	std::cerr << "WARNING: RingBuffer::readOne: no sample available"
+		  << std::endl;
+	return T();
+    }
+
+    T value = m_buffer[r];
+    if (++r == m_size) r = 0;
+
+    BQ_MBARRIER();
+    m_reader = r;
+
+    return value;
+}
+
+template <typename T>
+int
+RingBuffer<T>::peek(T *const BQ_R__ destination, int n) const
+{
+    int w = m_writer;
+    int r = m_reader;
+
+    int available = readSpaceFor(w, r);
+    if (n > available) {
+	std::cerr << "WARNING: RingBuffer::peek: " << n << " requested, only "
+                  << available << " available" << std::endl;
+	memset(destination + available, 0, (n - available) * sizeof(T));
+	n = available;
+    }
+    if (n == 0) return n;
+
+    int here = m_size - r;
+    const T *const BQ_R__ bufbase = m_buffer + r;
+
+    if (here >= n) {
+        v_copy(destination, bufbase, n);
+    } else {
+        v_copy(destination, bufbase, here);
+        v_copy(destination + here, m_buffer, n - here);
+    }
+
+    return n;
+}
+
+template <typename T>
+T
+RingBuffer<T>::peekOne() const
+{
+    int w = m_writer;
+    int r = m_reader;
+
+    if (w == r) {
+	std::cerr << "WARNING: RingBuffer::peekOne: no sample available"
+		  << std::endl;
+	return 0;
+    }
+
+    T value = m_buffer[r];
+    return value;
+}
+
+template <typename T>
+int
+RingBuffer<T>::skip(int n)
+{
+    int w = m_writer;
+    int r = m_reader;
+
+    int available = readSpaceFor(w, r);
+    if (n > available) {
+	std::cerr << "WARNING: RingBuffer::skip: " << n << " requested, only "
+                  << available << " available" << std::endl;
+	n = available;
+    }
+    if (n == 0) return n;
+
+    r += n;
+    while (r >= m_size) r -= m_size;
+
+    // No memory barrier required, because we didn't read any data
+    m_reader = r;
+
+    return n;
+}
+
+template <typename T>
+template <typename S>
+int
+RingBuffer<T>::write(const S *const BQ_R__ source, int n)
+{
+    int w = m_writer;
+    int r = m_reader;
+
+    int available = writeSpaceFor(w, r);
+    if (n > available) {
+	std::cerr << "WARNING: RingBuffer::write: " << n
+                  << " requested, only room for " << available << std::endl;
+	n = available;
+    }
+    if (n == 0) return n;
+
+    int here = m_size - w;
+    T *const BQ_R__ bufbase = m_buffer + w;
+
+    if (here >= n) {
+        v_convert<S, T>(bufbase, source, n);
+    } else {
+        v_convert<S, T>(bufbase, source, here);
+        v_convert<S, T>(m_buffer, source + here, n - here);
+    }
+
+    w += n;
+    while (w >= m_size) w -= m_size;
+
+    BQ_MBARRIER();
+    m_writer = w;
+
+    return n;
+}
+
+template <typename T>
+int
+RingBuffer<T>::zero(int n)
+{
+    int w = m_writer;
+    int r = m_reader;
+
+    int available = writeSpaceFor(w, r);
+    if (n > available) {
+	std::cerr << "WARNING: RingBuffer::zero: " << n
+                  << " requested, only room for " << available << std::endl;
+	n = available;
+    }
+    if (n == 0) return n;
+
+    int here = m_size - w;
+    T *const BQ_R__ bufbase = m_buffer + w;
+
+    if (here >= n) {
+        v_zero(bufbase, n);
+    } else {
+        v_zero(bufbase, here);
+        v_zero(m_buffer, n - here);
+    }
+
+    w += n;
+    while (w >= m_size) w -= m_size;
+
+    BQ_MBARRIER();
+    m_writer = w;
+
+    return n;
+}
+
+}
+
+#endif // BQVEC_RINGBUFFER_H
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqvec/bqvec/VectorOps.h	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,1110 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    bqvec
+
+    A small library for vector arithmetic and allocation in C++ using
+    raw C pointer arrays.
+
+    Copyright 2007-2015 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#ifndef BQVEC_VECTOR_OPS_H
+#define BQVEC_VECTOR_OPS_H
+
+#ifdef HAVE_IPP
+#ifndef _MSC_VER
+#include <inttypes.h>
+#endif
+#include <ipps.h>
+#include <ippac.h>
+#endif
+
+#ifdef HAVE_VDSP
+#include <Accelerate/Accelerate.h>
+#endif
+
+#include <cstring>
+
+#include "Restrict.h"
+
+namespace breakfastquay {
+
+/**
+ * General principle:
+ * 
+ * Write basic vector-manipulation loops in such a way as to promote
+ * the likelihood that a good current C++ compiler can auto-vectorize
+ * them (e.g. gcc-4.x with -ftree-vectorize). Provide calls out to
+ * supported vector libraries (e.g. IPP, Accelerate) where useful.
+ * No intrinsics or assembly.
+ *
+ * Note that all size and index arguments are plain machine ints, to
+ * facilitate compiler optimization and vectorization. In general
+ * these functions should be used only with buffers whose sizes are
+ * calculated from known processing parameters and that are known to
+ * be much smaller than 32-bit int range. For security reasons you
+ * should not use these functions with buffers whose sizes may be
+ * under control of the user or external input.
+ *
+ * Argument ordering:
+ *
+ * If a function operates on one or more vector sequences in memory,
+ * they appear as pointers at the start of the argument list. If one
+ * vector is the "destination", it appears first, with "source" second
+ * if present (i.e. argument ordering follows memcpy).
+ * 
+ * The final argument is always a count of the number of elements in
+ * each vector. 
+ *
+ * Some functions operate on a set of vectors at once: their names all
+ * contain the text _channels, and the number of channels (i.e. number
+ * of vectors) is the argument before last.
+ *
+ * Any additional arguments, e.g. scale factors, appear between the
+ * vector pointers at the start and the counts at the end.
+ *
+ * The caller takes responsibility for ensuring that vector pointer
+ * arguments are not aliased, i.e. that vectors provided as separate
+ * arguments to the same function are distinct and do not overlap,
+ * except where documented.
+ */
+
+/**
+ * v_zero
+ *
+ * Zero the elements in the given vector, of length \arg count.
+ */
+template<typename T>
+inline void v_zero(T *const BQ_R__ vec,
+                   const int count)
+{
+    const T value = T(0);
+    for (int i = 0; i < count; ++i) {
+        vec[i] = value;
+    }
+}
+
+#if defined HAVE_IPP
+template<> 
+inline void v_zero(float *const BQ_R__ vec, 
+                   const int count)
+{
+    ippsZero_32f(vec, count);
+}
+template<> 
+inline void v_zero(double *const BQ_R__ vec,
+                   const int count)
+{
+    ippsZero_64f(vec, count);
+}
+#elif defined HAVE_VDSP
+template<> 
+inline void v_zero(float *const BQ_R__ vec, 
+                   const int count)
+{
+    vDSP_vclr(vec, 1, count);
+}
+template<> 
+inline void v_zero(double *const BQ_R__ vec,
+                   const int count)
+{
+    vDSP_vclrD(vec, 1, count);
+}
+#endif
+
+/**
+ * v_zero_channels
+ *
+ * Zero the elements in the given set of \arg channels vectors, each
+ * of length \arg count.
+ */
+template<typename T>
+inline void v_zero_channels(T *const BQ_R__ *const BQ_R__ vec,
+                            const int channels,
+                            const int count)
+{
+    for (int c = 0; c < channels; ++c) {
+        v_zero(vec[c], count);
+    }
+}
+
+/**
+ * v_set
+ *
+ * Set all of the elements in the given vector, of length \arg count,
+ * to \arg value.
+ */
+template<typename T>
+inline void v_set(T *const BQ_R__ vec,
+                  const T value,
+                  const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        vec[i] = value;
+    }
+}
+
+/**
+ * v_copy
+ *
+ * Copy the contents of the vector \arg src to the vector \arg dst,
+ * both of length \arg count.
+ *
+ * Caller guarantees that \arg src and \arg dst are non-overlapping.
+ */
+template<typename T>
+inline void v_copy(T *const BQ_R__ dst,
+                   const T *const BQ_R__ src,
+                   const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        dst[i] = src[i];
+    }
+}
+
+#if defined HAVE_IPP
+template<>
+inline void v_copy(float *const BQ_R__ dst,
+                   const float *const BQ_R__ src,
+                   const int count)
+{
+    ippsCopy_32f(src, dst, count);
+}
+template<>
+inline void v_copy(double *const BQ_R__ dst,
+                   const double *const BQ_R__ src,
+                   const int count)
+{
+    ippsCopy_64f(src, dst, count);
+}
+#endif
+
+/**
+ * v_copy_channels
+ *
+ * Copy the contents of the individual vectors in the set \arg src to
+ * the corresponding vectors in the set \arg dst. All vectors have
+ * length \arg count and there are \arg channels vectors in each set.
+ *
+ * Caller guarantees that all of the \arg src and \arg dst vectors are
+ * non-overlapping with each other.
+ */
+template<typename T>
+inline void v_copy_channels(T *const BQ_R__ *const BQ_R__ dst,
+                            const T *const BQ_R__ *const BQ_R__ src,
+                            const int channels,
+                            const int count)
+{
+    for (int c = 0; c < channels; ++c) {
+        v_copy(dst[c], src[c], count);
+    }
+}
+
+/**
+ * v_move
+ *
+ * Copy the contents of vector \arg src to the vector \arg dst, both
+ * of length \arg count. The two vectors may overlap. (If you know
+ * that they cannot overlap, use v_copy instead.)
+ */
+template<typename T>
+inline void v_move(T *const dst,       // not BQ_R__ (aliased)
+                   const T *const src, // not BQ_R__ (aliased)
+                   const int count)
+{
+    memmove(dst, src, count * sizeof(T));
+}
+
+#if defined HAVE_IPP
+template<>
+inline void v_move(float *const dst,
+                   const float *const src,
+                   const int count)
+{
+    ippsMove_32f(src, dst, count);
+}
+template<>
+inline void v_move(double *const dst,
+                   const double *const src,
+                   const int count)
+{
+    ippsMove_64f(src, dst, count);
+}
+#endif
+
+/**
+ * v_convert
+ *
+ * Copy the contents of vector \arg src to the vector \arg dst, both
+ * of length \arg count. The two vectors may have different value
+ * types, e.g. double and float. If they have the same type, this is
+ * equivalent to v_copy.
+ *
+ * Caller guarantees that \arg src and \arg dst are non-overlapping.
+ */
+template<typename T, typename U>
+inline void v_convert(U *const BQ_R__ dst,
+                      const T *const BQ_R__ src,
+                      const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        dst[i] = U(src[i]);
+    }
+}
+
+template<>
+inline void v_convert(float *const BQ_R__ dst,
+                      const float *const BQ_R__ src,
+                      const int count)
+{
+    v_copy(dst, src, count);
+}
+template<>
+inline void v_convert(double *const BQ_R__ dst,
+                      const double *const BQ_R__ src,
+                      const int count)
+{
+    v_copy(dst, src, count);
+}
+
+#if defined HAVE_IPP
+template<>
+inline void v_convert(double *const BQ_R__ dst,
+                      const float *const BQ_R__ src,
+                      const int count)
+{
+    ippsConvert_32f64f(src, dst, count);
+}
+template<>
+inline void v_convert(float *const BQ_R__ dst,
+                      const double *const BQ_R__ src,
+                      const int count)
+{
+    ippsConvert_64f32f(src, dst, count);
+}
+#elif defined HAVE_VDSP
+template<>
+inline void v_convert(double *const BQ_R__ dst,
+                      const float *const BQ_R__ src,
+                      const int count)
+{
+    vDSP_vspdp((float *)src, 1, dst, 1, count);
+}
+template<>
+inline void v_convert(float *const BQ_R__ dst,
+                      const double *const BQ_R__ src,
+                      const int count)
+{
+    vDSP_vdpsp((double *)src, 1, dst, 1, count);
+}
+#endif
+
+/**
+ * v_convert_channels
+ *
+ * Copy the contents of the individual vectors in the set \arg src to
+ * the corresponding vectors in the set \arg dst. All vectors have
+ * length \arg count, and there are \arg channels vectors in each
+ * set. The source and destination vectors may have different value
+ * types, e.g. double and float. If they have the same type, this is
+ * equivalent to v_copy_channels.
+ *
+ * Caller guarantees that all of the \arg src and \arg dst vectors are
+ * non-overlapping with each other.
+ */
+template<typename T, typename U>
+inline void v_convert_channels(U *const BQ_R__ *const BQ_R__ dst,
+                               const T *const BQ_R__ *const BQ_R__ src,
+                               const int channels,
+                               const int count)
+{
+    for (int c = 0; c < channels; ++c) {
+        v_convert(dst[c], src[c], count);
+    }
+}
+
+/**
+ * v_add
+ *
+ * Add the elements in the vector \arg src to the corresponding
+ * elements in the vector \arg dst, both of length arg \count, leaving
+ * the result in \arg dst.
+ *
+ * Caller guarantees that \arg src and \arg dst are non-overlapping.
+ */
+template<typename T>
+inline void v_add(T *const BQ_R__ dst,
+                  const T *const BQ_R__ src,
+                  const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        dst[i] += src[i];
+    }
+}
+
+/**
+ * v_add
+ *
+ * Add the constant \arg value to every element of the vector \arg
+ * dst, of length arg \count, leaving the result in \arg dst.
+ */
+template<typename T>
+inline void v_add(T *const BQ_R__ dst,
+                  const T value,
+                  const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        dst[i] += value;
+    }
+}
+
+#if defined HAVE_IPP
+template<>
+inline void v_add(float *const BQ_R__ dst,
+                  const float *const BQ_R__ src,
+                  const int count)
+{
+    ippsAdd_32f_I(src, dst, count);
+}    
+inline void v_add(double *const BQ_R__ dst,
+                  const double *const BQ_R__ src,
+                  const int count)
+{
+    ippsAdd_64f_I(src, dst, count);
+}    
+#endif
+
+/**
+ * v_add_channels
+ *
+ * Add the elements in the individual vectors in the set \arg src to
+ * the corresponding elements of the corresponding vectors in \arg
+ * dst, leaving the results in \arg dst. All vectors have length \arg
+ * count, and there are \arg channels vectors in each set.
+ *
+ * Caller guarantees that all of the \arg src and \arg dst vectors are
+ * non-overlapping with each other.
+ */
+template<typename T>
+inline void v_add_channels(T *const BQ_R__ *const BQ_R__ dst,
+                           const T *const BQ_R__ *const BQ_R__ src,
+                           const int channels, const int count)
+{
+    for (int c = 0; c < channels; ++c) {
+        v_add(dst[c], src[c], count);
+    }
+}
+
+/**
+ * v_add_with_gain
+ *
+ * Add the elements in the vector \arg src, each multiplied by the
+ * constant factor \arg gain, to the corresponding elements in the
+ * vector \arg dst, both of length arg \count, leaving the result in
+ * \arg dst.
+ *
+ * Caller guarantees that \arg src and \arg dst are non-overlapping.
+ */
+template<typename T, typename G>
+inline void v_add_with_gain(T *const BQ_R__ dst,
+                            const T *const BQ_R__ src,
+                            const G gain,
+                            const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        dst[i] += src[i] * gain;
+    }
+}
+
+/**
+ * v_add_channels_with_gain
+ *
+ * Add the elements in the individual vectors in the set \arg src,
+ * each multiplied by the constant factor \arg gain, to the
+ * corresponding elements of the corresponding vectors in \arg dst,
+ * leaving the results in \arg dst. All vectors have length \arg
+ * count, and there are \arg channels vectors in each set.
+ *
+ * Caller guarantees that all of the \arg src and \arg dst vectors are
+ * non-overlapping with each other.
+ */
+template<typename T, typename G>
+inline void v_add_channels_with_gain(T *const BQ_R__ *const BQ_R__ dst,
+                                     const T *const BQ_R__ *const BQ_R__ src,
+                                     const G gain,
+                                     const int channels,
+                                     const int count)
+{
+    for (int c = 0; c < channels; ++c) {
+        v_add_with_gain(dst[c], src[c], gain, count);
+    }
+}
+
+/**
+ * v_subtract
+ *
+ * Subtract the elements in the vector \arg src from the corresponding
+ * elements in the vector \arg dst, both of length arg \count, leaving
+ * the result in \arg dst.
+ *
+ * Caller guarantees that \arg src and \arg dst are non-overlapping.
+ */
+template<typename T>
+inline void v_subtract(T *const BQ_R__ dst,
+                       const T *const BQ_R__ src,
+                       const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        dst[i] -= src[i];
+    }
+}
+
+#if defined HAVE_IPP
+template<>
+inline void v_subtract(float *const BQ_R__ dst,
+                       const float *const BQ_R__ src,
+                       const int count)
+{
+    ippsSub_32f_I(src, dst, count);
+}    
+inline void v_subtract(double *const BQ_R__ dst,
+                       const double *const BQ_R__ src,
+                       const int count)
+{
+    ippsSub_64f_I(src, dst, count);
+}    
+#endif
+
+/**
+ * v_scale
+ *
+ * Scale the elements in the vector \arg dst, of length \arg count, by
+ * the factor \arg gain.
+ */
+template<typename T, typename G>
+inline void v_scale(T *const BQ_R__ dst,
+                    const G gain,
+                    const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        dst[i] *= gain;
+    }
+}
+
+#if defined HAVE_IPP 
+template<>
+inline void v_scale(float *const BQ_R__ dst,
+                    const float gain,
+                    const int count)
+{
+    ippsMulC_32f_I(gain, dst, count);
+}
+template<>
+inline void v_scale(double *const BQ_R__ dst,
+                    const double gain,
+                    const int count)
+{
+    ippsMulC_64f_I(gain, dst, count);
+}
+#endif
+
+/**
+ * v_multiply
+ *
+ * Multiply the elements in the vector \arg dst by the corresponding
+ * elements in the vector \arg src, both of length arg \count, leaving
+ * the result in \arg dst.
+ *
+ * Caller guarantees that \arg src and \arg dst are non-overlapping.
+ */
+template<typename T>
+inline void v_multiply(T *const BQ_R__ dst,
+                       const T *const BQ_R__ src,
+                       const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        dst[i] *= src[i];
+    }
+}
+
+#if defined HAVE_IPP 
+template<>
+inline void v_multiply(float *const BQ_R__ dst,
+                       const float *const BQ_R__ src,
+                       const int count)
+{
+    ippsMul_32f_I(src, dst, count);
+}
+template<>
+inline void v_multiply(double *const BQ_R__ dst,
+                       const double *const BQ_R__ src,
+                       const int count)
+{
+    ippsMul_64f_I(src, dst, count);
+}
+#endif
+
+/**
+ * v_multiply
+ *
+ * Multiply the corresponding elements of the vectors \arg src1 and
+ * \arg src2, both of length arg \count, and write the results into
+ * \arg dst.
+ *
+ * Caller guarantees that \arg src1, \arg src2 and \arg dst are
+ * non-overlapping.
+ */
+template<typename T>
+inline void v_multiply(T *const BQ_R__ dst,
+                       const T *const BQ_R__ src1,
+                       const T *const BQ_R__ src2,
+                       const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        dst[i] = src1[i] * src2[i];
+    }
+}
+
+#if defined HAVE_IPP 
+template<>
+inline void v_multiply(float *const BQ_R__ dst,
+                       const float *const BQ_R__ src1,
+                       const float *const BQ_R__ src2,
+                       const int count)
+{
+    ippsMul_32f(src1, src2, dst, count);
+}    
+template<>
+inline void v_multiply(double *const BQ_R__ dst,
+                       const double *const BQ_R__ src1,
+                       const double *const BQ_R__ src2,
+                       const int count)
+{
+    ippsMul_64f(src1, src2, dst, count);
+}
+#endif
+
+/**
+ * v_divide
+ *
+ * Divide the elements in the vector \arg dst by the corresponding
+ * elements in the vector \arg src, both of length arg \count, leaving
+ * the result in \arg dst.
+ *
+ * Caller guarantees that \arg src and \arg dst are non-overlapping.
+ */
+template<typename T>
+inline void v_divide(T *const BQ_R__ dst,
+                     const T *const BQ_R__ src,
+                     const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        dst[i] /= src[i];
+    }
+}
+
+#if defined HAVE_IPP 
+template<>
+inline void v_divide(float *const BQ_R__ dst,
+                     const float *const BQ_R__ src,
+                     const int count)
+{
+    ippsDiv_32f_I(src, dst, count);
+}
+template<>
+inline void v_divide(double *const BQ_R__ dst,
+                     const double *const BQ_R__ src,
+                     const int count)
+{
+    ippsDiv_64f_I(src, dst, count);
+}
+#endif
+
+/**
+ * v_multiply_and_add
+ *
+ * Multiply the corresponding elements of the vectors \arg src1 and
+ * \arg src2, both of length arg \count, and add the results to the
+ * corresponding elements of vector \arg dst.
+ *
+ * Caller guarantees that \arg src1, \arg src2 and \arg dst are
+ * non-overlapping.
+ */
+template<typename T>
+inline void v_multiply_and_add(T *const BQ_R__ dst,
+                               const T *const BQ_R__ src1,
+                               const T *const BQ_R__ src2,
+                               const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        dst[i] += src1[i] * src2[i];
+    }
+}
+
+#if defined HAVE_IPP
+template<>
+inline void v_multiply_and_add(float *const BQ_R__ dst,
+                               const float *const BQ_R__ src1,
+                               const float *const BQ_R__ src2,
+                               const int count)
+{
+    ippsAddProduct_32f(src1, src2, dst, count);
+}
+template<>
+inline void v_multiply_and_add(double *const BQ_R__ dst,
+                               const double *const BQ_R__ src1,
+                               const double *const BQ_R__ src2,
+                               const int count)
+{
+    ippsAddProduct_64f(src1, src2, dst, count);
+}
+#endif
+
+/**
+ * v_sum
+ *
+ * Return the sum of the elements in vector \arg src, of length \arg
+ * count.
+ */
+template<typename T>
+inline T v_sum(const T *const BQ_R__ src,
+               const int count)
+{
+    T result = T();
+    for (int i = 0; i < count; ++i) {
+        result += src[i];
+    }
+    return result;
+}
+
+/**
+ * v_multiply_and_sum
+ *
+ * Multiply the corresponding elements of the vectors \arg src1 and
+ * \arg src2, both of length arg \count, sum the results, and return
+ * the sum as a scalar value.
+ *
+ * Caller guarantees that \arg src1 and \arg src2 are non-overlapping.
+ */
+template<typename T>
+inline T v_multiply_and_sum(const T *const BQ_R__ src1,
+                            const T *const BQ_R__ src2,
+                            const int count)
+{
+    T result = T();
+    for (int i = 0; i < count; ++i) {
+        result += src1[i] * src2[i];
+    }
+    return result;
+}
+
+/**
+ * v_log
+ *
+ * Replace each element in vector \arg dst, of length \arg count, with
+ * its natural logarithm.
+ */
+template<typename T>
+inline void v_log(T *const BQ_R__ dst,
+                  const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        dst[i] = log(dst[i]);
+    }
+}
+
+#if defined HAVE_IPP
+template<>
+inline void v_log(float *const BQ_R__ dst,
+                  const int count)
+{
+    ippsLn_32f_I(dst, count);
+}
+template<>
+inline void v_log(double *const BQ_R__ dst,
+                  const int count)
+{
+    ippsLn_64f_I(dst, count);
+}
+#elif defined HAVE_VDSP
+// no in-place vForce functions for these -- can we use the
+// out-of-place functions with equal input and output vectors? can we
+// use an out-of-place one with temporary buffer and still be faster
+// than doing it any other way?
+template<>
+inline void v_log(float *const BQ_R__ dst,
+                  const int count)
+{
+    float tmp[count];
+    vvlogf(tmp, dst, &count);
+    v_copy(dst, tmp, count);
+}
+template<>
+inline void v_log(double *const BQ_R__ dst,
+                  const int count)
+{
+    double tmp[count];
+    vvlog(tmp, dst, &count);
+    v_copy(dst, tmp, count);
+}
+#endif
+
+/**
+ * v_exp
+ *
+ * Replace each element in vector \arg dst, of length \arg count, with
+ * its base-e exponential.
+ */
+template<typename T>
+inline void v_exp(T *const BQ_R__ dst,
+                  const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        dst[i] = exp(dst[i]);
+    }
+}
+
+#if defined HAVE_IPP
+template<>
+inline void v_exp(float *const BQ_R__ dst,
+                  const int count)
+{
+    ippsExp_32f_I(dst, count);
+}
+template<>
+inline void v_exp(double *const BQ_R__ dst,
+                  const int count)
+{
+    ippsExp_64f_I(dst, count);
+}
+#elif defined HAVE_VDSP
+// no in-place vForce functions for these -- can we use the
+// out-of-place functions with equal input and output vectors? can we
+// use an out-of-place one with temporary buffer and still be faster
+// than doing it any other way?
+template<>
+inline void v_exp(float *const BQ_R__ dst,
+                  const int count)
+{
+    float tmp[count];
+    vvexpf(tmp, dst, &count);
+    v_copy(dst, tmp, count);
+}
+template<>
+inline void v_exp(double *const BQ_R__ dst,
+                  const int count)
+{
+    double tmp[count];
+    vvexp(tmp, dst, &count);
+    v_copy(dst, tmp, count);
+}
+#endif
+
+/**
+ * v_sqrt
+ *
+ * Replace each element in vector \arg dst, of length \arg count, with
+ * its square root.
+ */
+template<typename T>
+inline void v_sqrt(T *const BQ_R__ dst,
+                   const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        dst[i] = sqrt(dst[i]);
+    }
+}
+
+#if defined HAVE_IPP
+template<>
+inline void v_sqrt(float *const BQ_R__ dst,
+                   const int count)
+{
+    ippsSqrt_32f_I(dst, count);
+}
+template<>
+inline void v_sqrt(double *const BQ_R__ dst,
+                   const int count)
+{
+    ippsSqrt_64f_I(dst, count);
+}
+#elif defined HAVE_VDSP
+// no in-place vForce functions for these -- can we use the
+// out-of-place functions with equal input and output vectors? can we
+// use an out-of-place one with temporary buffer and still be faster
+// than doing it any other way?
+template<>
+inline void v_sqrt(float *const BQ_R__ dst,
+                   const int count)
+{
+    float tmp[count];
+    vvsqrtf(tmp, dst, &count);
+    v_copy(dst, tmp, count);
+}
+template<>
+inline void v_sqrt(double *const BQ_R__ dst,
+                   const int count)
+{
+    double tmp[count];
+    vvsqrt(tmp, dst, &count);
+    v_copy(dst, tmp, count);
+}
+#endif
+
+/**
+ * v_square
+ *
+ * Replace each element in vector \arg dst, of length \arg count, with
+ * its square.
+ */
+template<typename T>
+inline void v_square(T *const BQ_R__ dst,
+                   const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        dst[i] = dst[i] * dst[i];
+    }
+}
+
+#if defined HAVE_IPP
+template<>
+inline void v_square(float *const BQ_R__ dst,
+                   const int count)
+{
+    ippsSqr_32f_I(dst, count);
+}
+template<>
+inline void v_square(double *const BQ_R__ dst,
+                   const int count)
+{
+    ippsSqr_64f_I(dst, count);
+}
+#endif
+
+/**
+ * v_abs
+ *
+ * Replace each element in vector \arg dst, of length \arg count, with
+ * its absolute value.
+ */
+template<typename T>
+inline void v_abs(T *const BQ_R__ dst,
+                  const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        dst[i] = fabs(dst[i]);
+    }
+}
+
+#if defined HAVE_IPP
+template<>
+inline void v_abs(float *const BQ_R__ dst,
+                  const int count)
+{
+    ippsAbs_32f_I(dst, count);
+}
+template<>
+inline void v_abs(double *const BQ_R__ dst,
+                  const int count)
+{
+    ippsAbs_64f_I(dst, count);
+}
+#elif defined HAVE_VDSP
+template<>
+inline void v_abs(float *const BQ_R__ dst,
+                  const int count)
+{
+    float tmp[count];
+#if (defined(MACOSX_DEPLOYMENT_TARGET) && MACOSX_DEPLOYMENT_TARGET <= 1070 && MAC_OS_X_VERSION_MIN_REQUIRED <= 1070)
+    vvfabf(tmp, dst, &count);
+#else
+    vvfabsf(tmp, dst, &count);
+#endif
+    v_copy(dst, tmp, count);
+}
+#endif
+
+/**
+ * v_interleave
+ *
+ * Interleave (zip) the \arg channels vectors in \arg src, each of
+ * length \arg count, into the single vector \arg dst of length \arg
+ * channels * \arg count.
+ *
+ * Caller guarantees that the \arg src and \arg dst vectors are
+ * non-overlapping.
+ */
+template<typename T>
+inline void v_interleave(T *const BQ_R__ dst,
+                         const T *const BQ_R__ *const BQ_R__ src,
+                         const int channels, 
+                         const int count)
+{
+    int idx = 0;
+    switch (channels) {
+    case 2:
+        // common case, may be vectorized by compiler if hardcoded
+        for (int i = 0; i < count; ++i) {
+            for (int j = 0; j < 2; ++j) {
+                dst[idx++] = src[j][i];
+            }
+        }
+        return;
+    case 1:
+        v_copy(dst, src[0], count);
+        return;
+    default:
+        for (int i = 0; i < count; ++i) {
+            for (int j = 0; j < channels; ++j) {
+                dst[idx++] = src[j][i];
+            }
+        }
+    }
+}
+
+#if defined HAVE_IPP 
+template<>
+inline void v_interleave(float *const BQ_R__ dst,
+                         const float *const BQ_R__ *const BQ_R__ src,
+                         const int channels, 
+                         const int count)
+{
+    ippsInterleave_32f((const Ipp32f **)src, channels, count, dst);
+}
+// IPP does not (currently?) provide double-precision interleave
+#endif
+
+/**
+ * v_deinterleave
+ *
+ * Deinterleave (unzip) the single vector \arg src, of length \arg
+ * channels * \arg count, into the \arg channels vectors in \arg dst,
+ * each of length \arg count.
+ *
+ * Caller guarantees that the \arg src and \arg dst vectors are
+ * non-overlapping.
+ */
+template<typename T>
+inline void v_deinterleave(T *const BQ_R__ *const BQ_R__ dst,
+                           const T *const BQ_R__ src,
+                           const int channels, 
+                           const int count)
+{
+    int idx = 0;
+    switch (channels) {
+    case 2:
+        // common case, may be vectorized by compiler if hardcoded
+        for (int i = 0; i < count; ++i) {
+            for (int j = 0; j < 2; ++j) {
+                dst[j][i] = src[idx++];
+            }
+        }
+        return;
+    case 1:
+        v_copy(dst[0], src, count);
+        return;
+    default:
+        for (int i = 0; i < count; ++i) {
+            for (int j = 0; j < channels; ++j) {
+                dst[j][i] = src[idx++];
+            }
+        }
+    }
+}
+
+#if defined HAVE_IPP
+template<>
+inline void v_deinterleave(float *const BQ_R__ *const BQ_R__ dst,
+                           const float *const BQ_R__ src,
+                           const int channels, 
+                           const int count)
+{
+    ippsDeinterleave_32f((const Ipp32f *)src, channels, count, (Ipp32f **)dst);
+}
+// IPP does not (currently?) provide double-precision deinterleave
+#endif
+
+/**
+ * v_fftshift
+ *
+ * Perform an in-place left-right shift of the vector \arg vec of
+ * length \arg count, swapping the first and second halves of the
+ * vector.
+ */
+template<typename T>
+inline void v_fftshift(T *const BQ_R__ vec,
+                       const int count)
+{
+    const int hs = count/2;
+    for (int i = 0; i < hs; ++i) {
+        T t = vec[i];
+        vec[i] = vec[i + hs];
+        vec[i + hs] = t;
+    }
+}
+
+/**
+ * v_mean
+ *
+ * Return the mean of the values in the vector \arg vec, of length
+ * \arg count.
+ */
+template<typename T>
+inline T v_mean(const T *const BQ_R__ vec, const int count)
+{
+    T t = T(0);
+    for (int i = 0; i < count; ++i) {
+        t += vec[i];
+    }
+    t /= T(count);
+    return t;
+}
+
+/**
+ * v_mean_channels
+ *
+ * Return the mean of all the values in the set of \arg channels
+ * vectors in \arg vec, each of length \arg count. (This is the single
+ * mean of all values across all channels, not one mean per channel.)
+ */
+template<typename T>
+inline T v_mean_channels(const T *const BQ_R__ *const BQ_R__ vec,
+                         const int channels,
+                         const int count)
+{
+    T t = T(0);
+    for (int c = 0; c < channels; ++c) {
+        t += v_mean(vec[c], count);
+    }
+    t /= T(channels);
+    return t;
+}
+
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqvec/bqvec/VectorOpsComplex.h	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,548 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    bqvec
+
+    A small library for vector arithmetic and allocation in C++ using
+    raw C pointer arrays.
+
+    Copyright 2007-2015 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#ifndef BQVEC_VECTOR_OPS_COMPLEX_H
+#define BQVEC_VECTOR_OPS_COMPLEX_H
+
+#include "VectorOps.h"
+#include "ComplexTypes.h"
+
+#include <cmath>
+
+namespace breakfastquay {
+
+#ifndef NO_COMPLEX_TYPES
+
+template<> 
+inline void v_zero(bq_complex_t *const BQ_R__ ptr, 
+                   const int count)
+{
+#if defined HAVE_IPP
+    if (sizeof(bq_complex_element_t) == sizeof(float)) {
+        ippsZero_32fc((Ipp32fc *)ptr, count);
+    } else {
+        ippsZero_64fc((Ipp64fc *)ptr, count);
+    }
+#elif defined HAVE_VDSP
+    if (sizeof(bq_complex_element_t) == sizeof(float)) {
+        vDSP_vclr((float *)ptr, 1, count * 2);
+    } else {
+        vDSP_vclrD((double *)ptr, 1, count * 2);
+    }
+#else
+    const bq_complex_element_t value = 0.0;
+    for (int i = 0; i < count; ++i) {
+        ptr[i].re = value;
+        ptr[i].im = value;
+    }
+#endif
+}
+
+#if defined HAVE_IPP
+template<>
+inline void v_copy(bq_complex_t *const BQ_R__ dst,
+                   const bq_complex_t *const BQ_R__ src,
+                   const int count)
+{
+    if (sizeof(bq_complex_element_t) == sizeof(float)) {
+        ippsCopy_32fc((const Ipp32fc *)src, (Ipp32fc *)dst, count);
+    } else {
+        ippsCopy_64fc((const Ipp64fc *)src, (Ipp64fc *)dst, count);
+    }
+}
+template<>
+inline void v_move(bq_complex_t *const BQ_R__ dst,
+                   const bq_complex_t *const BQ_R__ src,
+                   const int count)
+{
+    if (sizeof(bq_complex_element_t) == sizeof(float)) {
+        ippsMove_32fc((const Ipp32fc *)src, (Ipp32fc *)dst, count);
+    } else {
+        ippsMove_64fc((const Ipp64fc *)src, (Ipp64fc *)dst, count);
+    }
+}
+#endif
+
+template<>
+inline void v_convert(bq_complex_t *const BQ_R__ dst,
+                      const bq_complex_element_t *const BQ_R__ src,
+                      const int srccount)
+{
+    const int targetcount = srccount / 2;
+    int srcidx = 0;
+    for (int i = 0; i < targetcount; ++i) {
+        dst[i].re = src[srcidx++];
+        dst[i].im = src[srcidx++];
+    }
+}
+
+template<>
+inline void v_convert(bq_complex_element_t *const BQ_R__ dst,
+                      const bq_complex_t *const BQ_R__ src,
+                      const int srccount)
+{
+    int targetidx = 0;
+    for (int i = 0; i < srccount; ++i) {
+        dst[targetidx++] = src[i].re;
+        dst[targetidx++] = src[i].im;
+    }
+}
+
+inline void c_add(bq_complex_t &dst,
+                  const bq_complex_t &src)
+{
+    dst.re += src.re;
+    dst.im += src.im;
+}
+
+inline void c_add_with_gain(bq_complex_t &dst,
+                            const bq_complex_t &src,
+                            const bq_complex_element_t gain)
+{
+    dst.re += src.re * gain;
+    dst.im += src.im * gain;
+}
+
+inline void c_multiply(bq_complex_t &dst,
+                       const bq_complex_t &src1,
+                       const bq_complex_t &src2)
+{
+    // Note dst may alias src1 or src2.
+
+    // The usual formula -- four multiplies, one add and one subtract
+    // 
+    // (x1 + y1i)(x2 + y2i) = (x1x2 - y1y2) + (x1y2 + y1x2)i
+    //
+    // Alternative formula -- three multiplies, two adds, three
+    // subtracts
+    // 
+    // (x1 + y1i)(x2 + y2i) = (x1x2 - y1y2) + ((x1 + y1)(x2 + y2) - x1x2 - y1y2)i
+    //
+    // The first formulation tests marginally quicker here.
+
+    bq_complex_element_t real = src1.re * src2.re - src1.im * src2.im;
+    bq_complex_element_t imag = src1.re * src2.im + src1.im * src2.re;
+
+    dst.re = real;
+    dst.im = imag;
+}
+
+inline void c_multiply(bq_complex_t &dst,
+                       const bq_complex_t &src)
+{
+    c_multiply(dst, dst, src);
+}
+
+inline void c_multiply_and_add(bq_complex_t &dst,
+                               const bq_complex_t &src1,
+                               const bq_complex_t &src2)
+{
+    bq_complex_t tmp;
+    c_multiply(tmp, src1, src2);
+    c_add(dst, tmp);
+}
+
+template<>
+inline void v_add(bq_complex_t *const BQ_R__ dst,
+                  const bq_complex_t *const BQ_R__ src,
+                  const int count)
+{
+#if defined HAVE_IPP
+    if (sizeof(bq_complex_element_t) == sizeof(float)) {
+        ippsAdd_32fc_I((Ipp32fc *)src, (Ipp32fc *)dst, count);
+    } else {
+        ippsAdd_64fc_I((Ipp64fc *)src, (Ipp64fc *)dst, count);
+    }
+#else
+    for (int i = 0; i < count; ++i) {
+        dst[i].re += src[i].re;
+        dst[i].im += src[i].im;
+    }
+#endif
+}    
+
+template<>
+inline void v_add_with_gain(bq_complex_t *const BQ_R__ dst,
+                            const bq_complex_t *const BQ_R__ src,
+                            const bq_complex_element_t gain,
+                            const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        dst[i].re += src[i].re * gain;
+        dst[i].im += src[i].im * gain;
+    }
+}
+
+template<>
+inline void v_multiply(bq_complex_t *const BQ_R__ dst,
+                       const bq_complex_t *const BQ_R__ src,
+                       const int count)
+{
+#ifdef HAVE_IPP
+    if (sizeof(bq_complex_element_t) == sizeof(float)) {
+        ippsMul_32fc_I((const Ipp32fc *)src, (Ipp32fc *)dst, count);
+    } else {
+        ippsMul_64fc_I((const Ipp64fc *)src, (Ipp64fc *)dst, count);
+    }
+#else
+    for (int i = 0; i < count; ++i) {
+        c_multiply(dst[i], src[i]);
+    }
+#endif
+}
+
+template<>
+inline void v_multiply(bq_complex_t *const BQ_R__ dst,
+                       const bq_complex_t *const BQ_R__ src1,
+                       const bq_complex_t *const BQ_R__ src2,
+                       const int count)
+{
+#ifdef HAVE_IPP
+    if (sizeof(bq_complex_element_t) == sizeof(float)) {
+        ippsMul_32fc((const Ipp32fc *)src1, (const Ipp32fc *)src2,
+                     (Ipp32fc *)dst, count);
+    } else {
+        ippsMul_64fc((const Ipp64fc *)src1, (const Ipp64fc *)src2,
+                     (Ipp64fc *)dst, count);
+    }
+#else
+    for (int i = 0; i < count; ++i) {
+        c_multiply(dst[i], src1[i], src2[i]);
+    }
+#endif
+}
+
+template<>
+inline void v_multiply_and_add(bq_complex_t *const BQ_R__ dst,
+                               const bq_complex_t *const BQ_R__ src1,
+                               const bq_complex_t *const BQ_R__ src2,
+                               const int count)
+{
+#ifdef HAVE_IPP
+    if (sizeof(bq_complex_element_t) == sizeof(float)) {
+        ippsAddProduct_32fc((const Ipp32fc *)src1, (const Ipp32fc *)src2,
+                            (Ipp32fc *)dst, count);
+    } else {
+        ippsAddProduct_64fc((const Ipp64fc *)src1, (const Ipp64fc *)src2,
+                            (Ipp64fc *)dst, count);
+    }
+#else
+    for (int i = 0; i < count; ++i) {
+        c_multiply_and_add(dst[i], src1[i], src2[i]);
+    }
+#endif
+}
+
+#if defined( __GNUC__ ) && defined( _WIN32 )
+// MinGW doesn't appear to have sincos, so define it -- it's
+// a single x87 instruction anyway
+static inline void sincos(double x, double *sin, double *cos) {
+    __asm__ ("fsincos;" : "=t" (*cos), "=u" (*sin) : "0" (x) : "st(7)");
+}
+static inline void sincosf(float fx, float *fsin, float *fcos) {
+    double sin, cos;
+    sincos(fx, &sin, &cos);
+    *fsin = sin;
+    *fcos = cos;
+}
+#endif
+
+#endif /* !NO_COMPLEX_TYPES */
+
+template<typename T>
+inline void c_phasor(T *real, T *imag, T phase)
+{
+    //!!! IPP contains ippsSinCos_xxx in ippvm.h -- these are
+    //!!! fixed-accuracy, test and compare
+#if defined HAVE_VDSP
+    int one = 1;
+    if (sizeof(T) == sizeof(float)) {
+        vvsincosf((float *)imag, (float *)real, (const float *)&phase, &one);
+    } else {
+        vvsincos((double *)imag, (double *)real, (const double *)&phase, &one);
+    }
+#elif defined LACK_SINCOS
+    if (sizeof(T) == sizeof(float)) {
+        *real = cosf(phase);
+        *imag = sinf(phase);
+    } else {
+        *real = cos(phase);
+        *imag = sin(phase);
+    }
+#elif defined __GNUC__
+    if (sizeof(T) == sizeof(float)) {
+        sincosf(float(phase), (float *)imag, (float *)real);
+    } else {
+        sincos(phase, (double *)imag, (double *)real);
+    }
+#else
+    if (sizeof(T) == sizeof(float)) {
+        *real = cosf(phase);
+        *imag = sinf(phase);
+    } else {
+        *real = cos(phase);
+        *imag = sin(phase);
+    }
+#endif
+}
+
+template<typename T>
+inline void c_magphase(T *mag, T *phase, T real, T imag)
+{
+    *mag = sqrt(real * real + imag * imag);
+    *phase = atan2(imag, real);
+}
+
+#ifdef USE_APPROXIMATE_ATAN2
+// NB arguments in opposite order from usual for atan2f
+extern float approximate_atan2f(float real, float imag);
+template<>
+inline void c_magphase(float *mag, float *phase, float real, float imag)
+{
+    float atan = approximate_atan2f(real, imag);
+    *phase = atan;
+    *mag = sqrtf(real * real + imag * imag);
+}
+#else
+template<>
+inline void c_magphase(float *mag, float *phase, float real, float imag)
+{
+    *mag = sqrtf(real * real + imag * imag);
+    *phase = atan2f(imag, real);
+}
+#endif
+
+#ifndef NO_COMPLEX_TYPES
+
+inline bq_complex_t c_phasor(bq_complex_element_t phase)
+{
+    bq_complex_t c;
+    c_phasor<bq_complex_element_t>(&c.re, &c.im, phase);
+    return c;
+}
+
+inline void c_magphase(bq_complex_element_t *mag, bq_complex_element_t *phase,
+                       bq_complex_t c)
+{
+    c_magphase<bq_complex_element_t>(mag, phase, c.re, c.im);
+}
+
+void v_polar_to_cartesian(bq_complex_t *const BQ_R__ dst,
+                          const bq_complex_element_t *const BQ_R__ mag,
+                          const bq_complex_element_t *const BQ_R__ phase,
+                          const int count);
+
+void v_polar_interleaved_to_cartesian(bq_complex_t *const BQ_R__ dst,
+                                      const bq_complex_element_t *const BQ_R__ src,
+                                      const int count);
+
+inline void v_cartesian_to_polar(bq_complex_element_t *const BQ_R__ mag,
+                                 bq_complex_element_t *const BQ_R__ phase,
+                                 const bq_complex_t *const BQ_R__ src,
+                                 const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        c_magphase<bq_complex_element_t>(mag + i, phase + i, src[i].re, src[i].im);
+    }
+}
+
+inline void v_cartesian_to_polar_interleaved(bq_complex_element_t *const BQ_R__ dst,
+                                             const bq_complex_t *const BQ_R__ src,
+                                             const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        c_magphase<bq_complex_element_t>(&dst[i*2], &dst[i*2+1],
+                                    src[i].re, src[i].im);
+    }
+}
+
+#endif /* !NO_COMPLEX_TYPES */
+
+template<typename S, typename T> // S source, T target
+void v_polar_to_cartesian(T *const BQ_R__ real,
+                          T *const BQ_R__ imag,
+                          const S *const BQ_R__ mag,
+                          const S *const BQ_R__ phase,
+                          const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        c_phasor<T>(real + i, imag + i, phase[i]);
+    }
+    v_multiply(real, mag, count);
+    v_multiply(imag, mag, count);
+}
+
+template<typename T>
+void v_polar_interleaved_to_cartesian_inplace(T *const BQ_R__ srcdst,
+                                              const int count)
+{
+    T real, imag;
+    for (int i = 0; i < count*2; i += 2) {
+        c_phasor(&real, &imag, srcdst[i+1]);
+        real *= srcdst[i];
+        imag *= srcdst[i];
+        srcdst[i] = real;
+        srcdst[i+1] = imag;
+    }
+}
+
+template<typename S, typename T> // S source, T target
+void v_polar_to_cartesian_interleaved(T *const BQ_R__ dst,
+                                      const S *const BQ_R__ mag,
+                                      const S *const BQ_R__ phase,
+                                      const int count)
+{
+    T real, imag;
+    for (int i = 0; i < count; ++i) {
+        c_phasor<T>(&real, &imag, phase[i]);
+        real *= mag[i];
+        imag *= mag[i];
+        dst[i*2] = real;
+        dst[i*2+1] = imag;
+    }
+}    
+
+#if defined USE_POMMIER_MATHFUN
+void v_polar_to_cartesian_pommier(float *const BQ_R__ real,
+                                  float *const BQ_R__ imag,
+                                  const float *const BQ_R__ mag,
+                                  const float *const BQ_R__ phase,
+                                  const int count);
+void v_polar_interleaved_to_cartesian_inplace_pommier(float *const BQ_R__ srcdst,
+                                                      const int count);
+void v_polar_to_cartesian_interleaved_pommier(float *const BQ_R__ dst,
+                                              const float *const BQ_R__ mag,
+                                              const float *const BQ_R__ phase,
+                                              const int count);
+
+template<>
+inline void v_polar_to_cartesian(float *const BQ_R__ real,
+                                 float *const BQ_R__ imag,
+                                 const float *const BQ_R__ mag,
+                                 const float *const BQ_R__ phase,
+                                 const int count)
+{
+    v_polar_to_cartesian_pommier(real, imag, mag, phase, count);
+}
+
+template<>
+inline void v_polar_interleaved_to_cartesian_inplace(float *const BQ_R__ srcdst,
+                                                     const int count)
+{
+    v_polar_interleaved_to_cartesian_inplace_pommier(srcdst, count);
+}
+
+template<>
+inline void v_polar_to_cartesian_interleaved(float *const BQ_R__ dst,
+                                             const float *const BQ_R__ mag,
+                                             const float *const BQ_R__ phase,
+                                             const int count)
+{
+    v_polar_to_cartesian_interleaved_pommier(dst, mag, phase, count);
+}
+
+#endif
+
+template<typename S, typename T> // S source, T target
+void v_cartesian_to_polar(T *const BQ_R__ mag,
+                          T *const BQ_R__ phase,
+                          const S *const BQ_R__ real,
+                          const S *const BQ_R__ imag,
+                          const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        c_magphase<T>(mag + i, phase + i, real[i], imag[i]);
+    }
+}
+
+template<typename S, typename T> // S source, T target
+void v_cartesian_interleaved_to_polar(T *const BQ_R__ mag,
+                                      T *const BQ_R__ phase,
+                                      const S *const BQ_R__ src,
+                                      const int count)
+{
+    for (int i = 0; i < count; ++i) {
+        c_magphase<T>(mag + i, phase + i, src[i*2], src[i*2+1]);
+    }
+}
+
+#ifdef HAVE_VDSP
+template<>
+inline void v_cartesian_to_polar(float *const BQ_R__ mag,
+                                 float *const BQ_R__ phase,
+                                 const float *const BQ_R__ real,
+                                 const float *const BQ_R__ imag,
+                                 const int count)
+{
+    DSPSplitComplex c;
+    c.realp = const_cast<float *>(real);
+    c.imagp = const_cast<float *>(imag);
+    vDSP_zvmags(&c, 1, phase, 1, count); // using phase as a temporary dest
+    vvsqrtf(mag, phase, &count); // using phase as the source
+    vvatan2f(phase, imag, real, &count);
+}
+template<>
+inline void v_cartesian_to_polar(double *const BQ_R__ mag,
+                                 double *const BQ_R__ phase,
+                                 const double *const BQ_R__ real,
+                                 const double *const BQ_R__ imag,
+                                 const int count)
+{
+    // double precision, this is significantly faster than using vDSP_polar
+    DSPDoubleSplitComplex c;
+    c.realp = const_cast<double *>(real);
+    c.imagp = const_cast<double *>(imag);
+    vDSP_zvmagsD(&c, 1, phase, 1, count); // using phase as a temporary dest
+    vvsqrt(mag, phase, &count); // using phase as the source
+    vvatan2(phase, imag, real, &count);
+}
+#endif
+
+template<typename T>
+void v_cartesian_to_polar_interleaved_inplace(T *const BQ_R__ srcdst,
+                                              const int count)
+{
+    T mag, phase;
+    for (int i = 0; i < count * 2; i += 2) {
+        c_magphase(&mag, &phase, srcdst[i], srcdst[i+1]);
+        srcdst[i] = mag;
+        srcdst[i+1] = phase;
+    }
+}
+
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqvec/pommier/neon_mathfun.h	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,301 @@
+/* NEON implementation of sin, cos, exp and log
+
+   Inspired by Intel Approximate Math library, and based on the
+   corresponding algorithms of the cephes math library
+*/
+
+/* Copyright (C) 2011  Julien Pommier
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  (this is the zlib license)
+*/
+
+#include <arm_neon.h>
+
+typedef float32x4_t v4sf;  // vector of 4 float
+typedef uint32x4_t v4su;  // vector of 4 uint32
+typedef int32x4_t v4si;  // vector of 4 uint32
+
+#define c_inv_mant_mask ~0x7f800000u
+#define c_cephes_SQRTHF 0.707106781186547524
+#define c_cephes_log_p0 7.0376836292E-2
+#define c_cephes_log_p1 - 1.1514610310E-1
+#define c_cephes_log_p2 1.1676998740E-1
+#define c_cephes_log_p3 - 1.2420140846E-1
+#define c_cephes_log_p4 + 1.4249322787E-1
+#define c_cephes_log_p5 - 1.6668057665E-1
+#define c_cephes_log_p6 + 2.0000714765E-1
+#define c_cephes_log_p7 - 2.4999993993E-1
+#define c_cephes_log_p8 + 3.3333331174E-1
+#define c_cephes_log_q1 -2.12194440e-4
+#define c_cephes_log_q2 0.693359375
+
+/* natural logarithm computed for 4 simultaneous float 
+   return NaN for x <= 0
+*/
+v4sf log_ps(v4sf x) {
+  v4sf one = vdupq_n_f32(1);
+
+  x = vmaxq_f32(x, vdupq_n_f32(0)); /* force flush to zero on denormal values */
+  v4su invalid_mask = vcleq_f32(x, vdupq_n_f32(0));
+
+  v4si ux = vreinterpretq_s32_f32(x);
+  
+  v4si emm0 = vshrq_n_s32(ux, 23);
+
+  /* keep only the fractional part */
+  ux = vandq_s32(ux, vdupq_n_s32(c_inv_mant_mask));
+  ux = vorrq_s32(ux, vreinterpretq_s32_f32(vdupq_n_f32(0.5f)));
+  x = vreinterpretq_f32_s32(ux);
+
+  emm0 = vsubq_s32(emm0, vdupq_n_s32(0x7f));
+  v4sf e = vcvtq_f32_s32(emm0);
+
+  e = vaddq_f32(e, one);
+
+  /* part2: 
+     if( x < SQRTHF ) {
+       e -= 1;
+       x = x + x - 1.0;
+     } else { x = x - 1.0; }
+  */
+  v4su mask = vcltq_f32(x, vdupq_n_f32(c_cephes_SQRTHF));
+  v4sf tmp = vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(x), mask));
+  x = vsubq_f32(x, one);
+  e = vsubq_f32(e, vreinterpretq_f32_u32(vandq_u32(vreinterpretq_u32_f32(one), mask)));
+  x = vaddq_f32(x, tmp);
+
+  v4sf z = vmulq_f32(x,x);
+
+  v4sf y = vdupq_n_f32(c_cephes_log_p0);
+  y = vmulq_f32(y, x);
+  y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p1));
+  y = vmulq_f32(y, x);
+  y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p2));
+  y = vmulq_f32(y, x);
+  y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p3));
+  y = vmulq_f32(y, x);
+  y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p4));
+  y = vmulq_f32(y, x);
+  y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p5));
+  y = vmulq_f32(y, x);
+  y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p6));
+  y = vmulq_f32(y, x);
+  y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p7));
+  y = vmulq_f32(y, x);
+  y = vaddq_f32(y, vdupq_n_f32(c_cephes_log_p8));
+  y = vmulq_f32(y, x);
+
+  y = vmulq_f32(y, z);
+  
+
+  tmp = vmulq_f32(e, vdupq_n_f32(c_cephes_log_q1));
+  y = vaddq_f32(y, tmp);
+
+
+  tmp = vmulq_f32(z, vdupq_n_f32(0.5f));
+  y = vsubq_f32(y, tmp);
+
+  tmp = vmulq_f32(e, vdupq_n_f32(c_cephes_log_q2));
+  x = vaddq_f32(x, y);
+  x = vaddq_f32(x, tmp);
+  x = vreinterpretq_f32_u32(vorrq_u32(vreinterpretq_u32_f32(x), invalid_mask)); // negative arg will be NAN
+  return x;
+}
+
+#define c_exp_hi 88.3762626647949f
+#define c_exp_lo -88.3762626647949f
+
+#define c_cephes_LOG2EF 1.44269504088896341
+#define c_cephes_exp_C1 0.693359375
+#define c_cephes_exp_C2 -2.12194440e-4
+
+#define c_cephes_exp_p0 1.9875691500E-4
+#define c_cephes_exp_p1 1.3981999507E-3
+#define c_cephes_exp_p2 8.3334519073E-3
+#define c_cephes_exp_p3 4.1665795894E-2
+#define c_cephes_exp_p4 1.6666665459E-1
+#define c_cephes_exp_p5 5.0000001201E-1
+
+/* exp() computed for 4 float at once */
+v4sf exp_ps(v4sf x) {
+  v4sf tmp, fx;
+
+  v4sf one = vdupq_n_f32(1);
+  x = vminq_f32(x, vdupq_n_f32(c_exp_hi));
+  x = vmaxq_f32(x, vdupq_n_f32(c_exp_lo));
+
+  /* express exp(x) as exp(g + n*log(2)) */
+  fx = vmlaq_f32(vdupq_n_f32(0.5f), x, vdupq_n_f32(c_cephes_LOG2EF));
+
+  /* perform a floorf */
+  tmp = vcvtq_f32_s32(vcvtq_s32_f32(fx));
+
+  /* if greater, substract 1 */
+  v4su mask = vcgtq_f32(tmp, fx);    
+  mask = vandq_u32(mask, vreinterpretq_u32_f32(one));
+
+
+  fx = vsubq_f32(tmp, vreinterpretq_f32_u32(mask));
+
+  tmp = vmulq_f32(fx, vdupq_n_f32(c_cephes_exp_C1));
+  v4sf z = vmulq_f32(fx, vdupq_n_f32(c_cephes_exp_C2));
+  x = vsubq_f32(x, tmp);
+  x = vsubq_f32(x, z);
+
+  static const float32_t cephes_exp_p[6] = { c_cephes_exp_p0, c_cephes_exp_p1, c_cephes_exp_p2, c_cephes_exp_p3, c_cephes_exp_p4, c_cephes_exp_p5 };
+  v4sf y = vld1q_dup_f32(cephes_exp_p+0);
+  v4sf c1 = vld1q_dup_f32(cephes_exp_p+1); 
+  v4sf c2 = vld1q_dup_f32(cephes_exp_p+2); 
+  v4sf c3 = vld1q_dup_f32(cephes_exp_p+3); 
+  v4sf c4 = vld1q_dup_f32(cephes_exp_p+4); 
+  v4sf c5 = vld1q_dup_f32(cephes_exp_p+5);
+
+  y = vmulq_f32(y, x);
+  z = vmulq_f32(x,x);
+  y = vaddq_f32(y, c1);
+  y = vmulq_f32(y, x);
+  y = vaddq_f32(y, c2);
+  y = vmulq_f32(y, x);
+  y = vaddq_f32(y, c3);
+  y = vmulq_f32(y, x);
+  y = vaddq_f32(y, c4);
+  y = vmulq_f32(y, x);
+  y = vaddq_f32(y, c5);
+  
+  y = vmulq_f32(y, z);
+  y = vaddq_f32(y, x);
+  y = vaddq_f32(y, one);
+
+  /* build 2^n */
+  int32x4_t mm;
+  mm = vcvtq_s32_f32(fx);
+  mm = vaddq_s32(mm, vdupq_n_s32(0x7f));
+  mm = vshlq_n_s32(mm, 23);
+  v4sf pow2n = vreinterpretq_f32_s32(mm);
+
+  y = vmulq_f32(y, pow2n);
+  return y;
+}
+
+#define c_minus_cephes_DP1 -0.78515625
+#define c_minus_cephes_DP2 -2.4187564849853515625e-4
+#define c_minus_cephes_DP3 -3.77489497744594108e-8
+#define c_sincof_p0 -1.9515295891E-4
+#define c_sincof_p1  8.3321608736E-3
+#define c_sincof_p2 -1.6666654611E-1
+#define c_coscof_p0  2.443315711809948E-005
+#define c_coscof_p1 -1.388731625493765E-003
+#define c_coscof_p2  4.166664568298827E-002
+#define c_cephes_FOPI 1.27323954473516 // 4 / M_PI
+
+/* evaluation of 4 sines & cosines at once.
+
+   The code is the exact rewriting of the cephes sinf function.
+   Precision is excellent as long as x < 8192 (I did not bother to
+   take into account the special handling they have for greater values
+   -- it does not return garbage for arguments over 8192, though, but
+   the extra precision is missing).
+
+   Note that it is such that sinf((float)M_PI) = 8.74e-8, which is the
+   surprising but correct result.
+
+   Note also that when you compute sin(x), cos(x) is available at
+   almost no extra price so both sin_ps and cos_ps make use of
+   sincos_ps..
+  */
+void sincos_ps(v4sf x, v4sf *ysin, v4sf *ycos) { // any x
+  v4sf xmm1, xmm2, xmm3, y;
+
+  v4su emm2;
+  
+  v4su sign_mask_sin, sign_mask_cos;
+  sign_mask_sin = vcltq_f32(x, vdupq_n_f32(0));
+  x = vabsq_f32(x);
+
+  /* scale by 4/Pi */
+  y = vmulq_f32(x, vdupq_n_f32(c_cephes_FOPI));
+
+  /* store the integer part of y in mm0 */
+  emm2 = vcvtq_u32_f32(y);
+  /* j=(j+1) & (~1) (see the cephes sources) */
+  emm2 = vaddq_u32(emm2, vdupq_n_u32(1));
+  emm2 = vandq_u32(emm2, vdupq_n_u32(~1));
+  y = vcvtq_f32_u32(emm2);
+
+  /* get the polynom selection mask 
+     there is one polynom for 0 <= x <= Pi/4
+     and another one for Pi/4<x<=Pi/2
+
+     Both branches will be computed.
+  */
+  v4su poly_mask = vtstq_u32(emm2, vdupq_n_u32(2));
+  
+  /* The magic pass: "Extended precision modular arithmetic" 
+     x = ((x - y * DP1) - y * DP2) - y * DP3; */
+  xmm1 = vmulq_n_f32(y, c_minus_cephes_DP1);
+  xmm2 = vmulq_n_f32(y, c_minus_cephes_DP2);
+  xmm3 = vmulq_n_f32(y, c_minus_cephes_DP3);
+  x = vaddq_f32(x, xmm1);
+  x = vaddq_f32(x, xmm2);
+  x = vaddq_f32(x, xmm3);
+
+  sign_mask_sin = veorq_u32(sign_mask_sin, vtstq_u32(emm2, vdupq_n_u32(4)));
+  sign_mask_cos = vtstq_u32(vsubq_u32(emm2, vdupq_n_u32(2)), vdupq_n_u32(4));
+
+  /* Evaluate the first polynom  (0 <= x <= Pi/4) in y1, 
+     and the second polynom      (Pi/4 <= x <= 0) in y2 */
+  v4sf z = vmulq_f32(x,x);
+  v4sf y1, y2;
+
+  y1 = vmulq_n_f32(z, c_coscof_p0);
+  y2 = vmulq_n_f32(z, c_sincof_p0);
+  y1 = vaddq_f32(y1, vdupq_n_f32(c_coscof_p1));
+  y2 = vaddq_f32(y2, vdupq_n_f32(c_sincof_p1));
+  y1 = vmulq_f32(y1, z);
+  y2 = vmulq_f32(y2, z);
+  y1 = vaddq_f32(y1, vdupq_n_f32(c_coscof_p2));
+  y2 = vaddq_f32(y2, vdupq_n_f32(c_sincof_p2));
+  y1 = vmulq_f32(y1, z);
+  y2 = vmulq_f32(y2, z);
+  y1 = vmulq_f32(y1, z);
+  y2 = vmulq_f32(y2, x);
+  y1 = vsubq_f32(y1, vmulq_f32(z, vdupq_n_f32(0.5f)));
+  y2 = vaddq_f32(y2, x);
+  y1 = vaddq_f32(y1, vdupq_n_f32(1));
+
+  /* select the correct result from the two polynoms */  
+  v4sf ys = vbslq_f32(poly_mask, y1, y2);
+  v4sf yc = vbslq_f32(poly_mask, y2, y1);
+  *ysin = vbslq_f32(sign_mask_sin, vnegq_f32(ys), ys);
+  *ycos = vbslq_f32(sign_mask_cos, yc, vnegq_f32(yc));
+}
+
+v4sf sin_ps(v4sf x) {
+  v4sf ysin, ycos; 
+  sincos_ps(x, &ysin, &ycos); 
+  return ysin;
+}
+
+v4sf cos_ps(v4sf x) {
+  v4sf ysin, ycos; 
+  sincos_ps(x, &ysin, &ycos); 
+  return ycos;
+}
+
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqvec/pommier/sse_mathfun.h	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,766 @@
+
+#ifndef _POMMIER_SSE_MATHFUN_H_
+#define _POMMIER_SSE_MATHFUN_H_
+
+/* SIMD (SSE1+MMX or SSE2) implementation of sin, cos, exp and log
+
+   Inspired by Intel Approximate Math library, and based on the
+   corresponding algorithms of the cephes math library
+
+   The default is to use the SSE1 version. If you define USE_SSE2 the
+   the SSE2 intrinsics will be used in place of the MMX intrinsics. Do
+   not expect any significant performance improvement with SSE2.
+*/
+
+/* Copyright (C) 2007  Julien Pommier
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  (this is the zlib license)
+*/
+
+#include <xmmintrin.h>
+
+/* yes I know, the top of this file is quite ugly */
+
+#ifdef _MSC_VER /* visual c++ */
+# define ALIGN16_BEG __declspec(align(16))
+# define ALIGN16_END 
+#else /* gcc or icc */
+# define ALIGN16_BEG
+# define ALIGN16_END __attribute__((aligned(16)))
+#endif
+
+/* __m128 is ugly to write */
+typedef __m128 v4sf;  // vector of 4 float (sse1)
+
+#ifdef USE_SSE2
+# include <emmintrin.h>
+typedef __m128i v4si; // vector of 4 int (sse2)
+#else
+typedef __m64 v2si;   // vector of 2 int (mmx)
+#endif
+
+/* declare some SSE constants -- why can't I figure a better way to do that? */
+#define _PS_CONST(Name, Val)                                            \
+  static const ALIGN16_BEG float _ps_##Name[4] ALIGN16_END = { Val, Val, Val, Val }
+#define _PI32_CONST(Name, Val)                                            \
+  static const ALIGN16_BEG int _pi32_##Name[4] ALIGN16_END = { Val, Val, Val, Val }
+#define _PS_CONST_TYPE(Name, Type, Val)                                 \
+  static const ALIGN16_BEG Type _ps_##Name[4] ALIGN16_END = { Val, Val, Val, Val }
+
+_PS_CONST(1  , 1.0f);
+_PS_CONST(0p5, 0.5f);
+/* the smallest non denormalized float number */
+_PS_CONST_TYPE(min_norm_pos, int, 0x00800000);
+_PS_CONST_TYPE(mant_mask, int, 0x7f800000);
+_PS_CONST_TYPE(inv_mant_mask, int, ~0x7f800000);
+
+_PS_CONST_TYPE(sign_mask, int, 0x80000000);
+_PS_CONST_TYPE(inv_sign_mask, int, ~0x80000000);
+
+_PI32_CONST(1, 1);
+_PI32_CONST(inv1, ~1);
+_PI32_CONST(2, 2);
+_PI32_CONST(4, 4);
+_PI32_CONST(0x7f, 0x7f);
+
+_PS_CONST(cephes_SQRTHF, 0.707106781186547524);
+_PS_CONST(cephes_log_p0, 7.0376836292E-2);
+_PS_CONST(cephes_log_p1, - 1.1514610310E-1);
+_PS_CONST(cephes_log_p2, 1.1676998740E-1);
+_PS_CONST(cephes_log_p3, - 1.2420140846E-1);
+_PS_CONST(cephes_log_p4, + 1.4249322787E-1);
+_PS_CONST(cephes_log_p5, - 1.6668057665E-1);
+_PS_CONST(cephes_log_p6, + 2.0000714765E-1);
+_PS_CONST(cephes_log_p7, - 2.4999993993E-1);
+_PS_CONST(cephes_log_p8, + 3.3333331174E-1);
+_PS_CONST(cephes_log_q1, -2.12194440e-4);
+_PS_CONST(cephes_log_q2, 0.693359375);
+
+#if defined (__MINGW32__)
+
+/* the ugly part below: many versions of gcc used to be completely buggy with respect to some intrinsics
+   The movehl_ps is fixed in mingw 3.4.5, but I found out that all the _mm_cmp* intrinsics were completely
+   broken on my mingw gcc 3.4.5 ...
+
+   Note that the bug on _mm_cmp* does occur only at -O0 optimization level
+*/
+
+inline __m128 my_movehl_ps(__m128 a, const __m128 b) {
+	asm (
+			"movhlps %2,%0\n\t"
+			: "=x" (a)
+			: "0" (a), "x"(b)
+	    );
+	return a;                                 }
+#warning "redefined _mm_movehl_ps (see gcc bug 21179)"
+#define _mm_movehl_ps my_movehl_ps
+
+inline __m128 my_cmplt_ps(__m128 a, const __m128 b) {
+	asm (
+			"cmpltps %2,%0\n\t"
+			: "=x" (a)
+			: "0" (a), "x"(b)
+	    );
+	return a;               
+                  }
+inline __m128 my_cmpgt_ps(__m128 a, const __m128 b) {
+	asm (
+			"cmpnleps %2,%0\n\t"
+			: "=x" (a)
+			: "0" (a), "x"(b)
+	    );
+	return a;               
+}
+inline __m128 my_cmpeq_ps(__m128 a, const __m128 b) {
+	asm (
+			"cmpeqps %2,%0\n\t"
+			: "=x" (a)
+			: "0" (a), "x"(b)
+	    );
+	return a;               
+}
+#warning "redefined _mm_cmpxx_ps functions..."
+#define _mm_cmplt_ps my_cmplt_ps
+#define _mm_cmpgt_ps my_cmpgt_ps
+#define _mm_cmpeq_ps my_cmpeq_ps
+#endif
+
+#ifndef USE_SSE2
+typedef union xmm_mm_union {
+  __m128 xmm;
+  __m64 mm[2];
+} xmm_mm_union;
+
+#define COPY_XMM_TO_MM(xmm_, mm0_, mm1_) {          \
+    xmm_mm_union u; u.xmm = xmm_;                   \
+    mm0_ = u.mm[0];                                 \
+    mm1_ = u.mm[1];                                 \
+}
+
+#define COPY_MM_TO_XMM(mm0_, mm1_, xmm_) {                         \
+    xmm_mm_union u; u.mm[0]=mm0_; u.mm[1]=mm1_; xmm_ = u.xmm;      \
+  }
+
+#endif // USE_SSE2
+
+/* natural logarithm computed for 4 simultaneous float 
+   return NaN for x <= 0
+*/
+v4sf log_ps(v4sf x) {
+#ifdef USE_SSE2
+  v4si emm0;
+#else
+  v2si mm0, mm1;
+#endif
+  v4sf one = *(v4sf*)_ps_1;
+
+  v4sf invalid_mask = _mm_cmple_ps(x, _mm_setzero_ps());
+
+  x = _mm_max_ps(x, *(v4sf*)_ps_min_norm_pos);  /* cut off denormalized stuff */
+
+#ifndef USE_SSE2
+  /* part 1: x = frexpf(x, &e); */
+  COPY_XMM_TO_MM(x, mm0, mm1);
+  mm0 = _mm_srli_pi32(mm0, 23);
+  mm1 = _mm_srli_pi32(mm1, 23);
+#else
+  emm0 = _mm_srli_epi32(_mm_castps_si128(x), 23);
+#endif
+  /* keep only the fractional part */
+  x = _mm_and_ps(x, *(v4sf*)_ps_inv_mant_mask);
+  x = _mm_or_ps(x, *(v4sf*)_ps_0p5);
+
+#ifndef USE_SSE2
+  /* now e=mm0:mm1 contain the really base-2 exponent */
+  mm0 = _mm_sub_pi32(mm0, *(v2si*)_pi32_0x7f);
+  mm1 = _mm_sub_pi32(mm1, *(v2si*)_pi32_0x7f);
+  v4sf e = _mm_cvtpi32x2_ps(mm0, mm1);
+  _mm_empty(); /* bye bye mmx */
+#else
+  emm0 = _mm_sub_epi32(emm0, *(v4si*)_pi32_0x7f);
+  v4sf e = _mm_cvtepi32_ps(emm0);
+#endif
+
+  e = _mm_add_ps(e, one);
+
+  /* part2: 
+     if( x < SQRTHF ) {
+       e -= 1;
+       x = x + x - 1.0;
+     } else { x = x - 1.0; }
+  */
+  v4sf mask = _mm_cmplt_ps(x, *(v4sf*)_ps_cephes_SQRTHF);
+  v4sf tmp = _mm_and_ps(x, mask);
+  x = _mm_sub_ps(x, one);
+  e = _mm_sub_ps(e, _mm_and_ps(one, mask));
+  x = _mm_add_ps(x, tmp);
+
+
+  v4sf z = _mm_mul_ps(x,x);
+
+  v4sf y = *(v4sf*)_ps_cephes_log_p0;
+  y = _mm_mul_ps(y, x);
+  y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p1);
+  y = _mm_mul_ps(y, x);
+  y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p2);
+  y = _mm_mul_ps(y, x);
+  y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p3);
+  y = _mm_mul_ps(y, x);
+  y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p4);
+  y = _mm_mul_ps(y, x);
+  y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p5);
+  y = _mm_mul_ps(y, x);
+  y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p6);
+  y = _mm_mul_ps(y, x);
+  y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p7);
+  y = _mm_mul_ps(y, x);
+  y = _mm_add_ps(y, *(v4sf*)_ps_cephes_log_p8);
+  y = _mm_mul_ps(y, x);
+
+  y = _mm_mul_ps(y, z);
+  
+
+  tmp = _mm_mul_ps(e, *(v4sf*)_ps_cephes_log_q1);
+  y = _mm_add_ps(y, tmp);
+
+
+  tmp = _mm_mul_ps(z, *(v4sf*)_ps_0p5);
+  y = _mm_sub_ps(y, tmp);
+
+  tmp = _mm_mul_ps(e, *(v4sf*)_ps_cephes_log_q2);
+  x = _mm_add_ps(x, y);
+  x = _mm_add_ps(x, tmp);
+  x = _mm_or_ps(x, invalid_mask); // negative arg will be NAN
+  return x;
+}
+
+_PS_CONST(exp_hi,	88.3762626647949f);
+_PS_CONST(exp_lo,	-88.3762626647949f);
+
+_PS_CONST(cephes_LOG2EF, 1.44269504088896341);
+_PS_CONST(cephes_exp_C1, 0.693359375);
+_PS_CONST(cephes_exp_C2, -2.12194440e-4);
+
+_PS_CONST(cephes_exp_p0, 1.9875691500E-4);
+_PS_CONST(cephes_exp_p1, 1.3981999507E-3);
+_PS_CONST(cephes_exp_p2, 8.3334519073E-3);
+_PS_CONST(cephes_exp_p3, 4.1665795894E-2);
+_PS_CONST(cephes_exp_p4, 1.6666665459E-1);
+_PS_CONST(cephes_exp_p5, 5.0000001201E-1);
+
+v4sf exp_ps(v4sf x) {
+  v4sf tmp = _mm_setzero_ps(), fx;
+#ifdef USE_SSE2
+  v4si emm0;
+#else
+  v2si mm0, mm1;
+#endif
+  v4sf one = *(v4sf*)_ps_1;
+
+  x = _mm_min_ps(x, *(v4sf*)_ps_exp_hi);
+  x = _mm_max_ps(x, *(v4sf*)_ps_exp_lo);
+
+  /* express exp(x) as exp(g + n*log(2)) */
+  fx = _mm_mul_ps(x, *(v4sf*)_ps_cephes_LOG2EF);
+  fx = _mm_add_ps(fx, *(v4sf*)_ps_0p5);
+
+  /* how to perform a floorf with SSE: just below */
+#ifndef USE_SSE2
+  /* step 1 : cast to int */
+  tmp = _mm_movehl_ps(tmp, fx);
+  mm0 = _mm_cvttps_pi32(fx);
+  mm1 = _mm_cvttps_pi32(tmp);
+  /* step 2 : cast back to float */
+  tmp = _mm_cvtpi32x2_ps(mm0, mm1);
+#else
+  emm0 = _mm_cvttps_epi32(fx);
+  tmp  = _mm_cvtepi32_ps(emm0);
+#endif
+  /* if greater, substract 1 */
+  v4sf mask = _mm_cmpgt_ps(tmp, fx);    
+  mask = _mm_and_ps(mask, one);
+  fx = _mm_sub_ps(tmp, mask);
+
+  tmp = _mm_mul_ps(fx, *(v4sf*)_ps_cephes_exp_C1);
+  v4sf z = _mm_mul_ps(fx, *(v4sf*)_ps_cephes_exp_C2);
+  x = _mm_sub_ps(x, tmp);
+  x = _mm_sub_ps(x, z);
+
+  z = _mm_mul_ps(x,x);
+  
+  v4sf y = *(v4sf*)_ps_cephes_exp_p0;
+  y = _mm_mul_ps(y, x);
+  y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p1);
+  y = _mm_mul_ps(y, x);
+  y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p2);
+  y = _mm_mul_ps(y, x);
+  y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p3);
+  y = _mm_mul_ps(y, x);
+  y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p4);
+  y = _mm_mul_ps(y, x);
+  y = _mm_add_ps(y, *(v4sf*)_ps_cephes_exp_p5);
+  y = _mm_mul_ps(y, z);
+  y = _mm_add_ps(y, x);
+  y = _mm_add_ps(y, one);
+
+  /* build 2^n */
+#ifndef USE_SSE2
+  z = _mm_movehl_ps(z, fx);
+  mm0 = _mm_cvttps_pi32(fx);
+  mm1 = _mm_cvttps_pi32(z);
+  mm0 = _mm_add_pi32(mm0, *(v2si*)_pi32_0x7f);
+  mm1 = _mm_add_pi32(mm1, *(v2si*)_pi32_0x7f);
+  mm0 = _mm_slli_pi32(mm0, 23); 
+  mm1 = _mm_slli_pi32(mm1, 23);
+  
+  v4sf pow2n; 
+  COPY_MM_TO_XMM(mm0, mm1, pow2n);
+  _mm_empty();
+#else
+  emm0 = _mm_cvttps_epi32(fx);
+  emm0 = _mm_add_epi32(emm0, *(v4si*)_pi32_0x7f);
+  emm0 = _mm_slli_epi32(emm0, 23);
+  v4sf pow2n = _mm_castsi128_ps(emm0);
+#endif
+  y = _mm_mul_ps(y, pow2n);
+  return y;
+}
+
+_PS_CONST(minus_cephes_DP1, -0.78515625);
+_PS_CONST(minus_cephes_DP2, -2.4187564849853515625e-4);
+_PS_CONST(minus_cephes_DP3, -3.77489497744594108e-8);
+_PS_CONST(sincof_p0, -1.9515295891E-4);
+_PS_CONST(sincof_p1,  8.3321608736E-3);
+_PS_CONST(sincof_p2, -1.6666654611E-1);
+_PS_CONST(coscof_p0,  2.443315711809948E-005);
+_PS_CONST(coscof_p1, -1.388731625493765E-003);
+_PS_CONST(coscof_p2,  4.166664568298827E-002);
+_PS_CONST(cephes_FOPI, 1.27323954473516); // 4 / M_PI
+
+
+/* evaluation of 4 sines at onces, using only SSE1+MMX intrinsics so
+   it runs also on old athlons XPs and the pentium III of your grand
+   mother.
+
+   The code is the exact rewriting of the cephes sinf function.
+   Precision is excellent as long as x < 8192 (I did not bother to
+   take into account the special handling they have for greater values
+   -- it does not return garbage for arguments over 8192, though, but
+   the extra precision is missing).
+
+   Note that it is such that sinf((float)M_PI) = 8.74e-8, which is the
+   surprising but correct result.
+
+   Performance is also surprisingly good, 1.33 times faster than the
+   macos vsinf SSE2 function, and 1.5 times faster than the
+   __vrs4_sinf of amd's ACML (which is only available in 64 bits). Not
+   too bad for an SSE1 function (with no special tuning) !
+   However the latter libraries probably have a much better handling of NaN,
+   Inf, denormalized and other special arguments..
+
+   On my core 1 duo, the execution of this function takes approximately 95 cycles.
+
+   From what I have observed on the experiments with Intel AMath lib, switching to an
+   SSE2 version would improve the perf by only 10%.
+
+   Since it is based on SSE intrinsics, it has to be compiled at -O2 to
+   deliver full speed.
+*/
+v4sf sin_ps(v4sf x) { // any x
+  v4sf xmm1, xmm2 = _mm_setzero_ps(), xmm3, sign_bit, y;
+
+#ifdef USE_SSE2
+  v4si emm0, emm2;
+#else
+  v2si mm0, mm1, mm2, mm3;
+#endif
+  sign_bit = x;
+  /* take the absolute value */
+  x = _mm_and_ps(x, *(v4sf*)_ps_inv_sign_mask);
+  /* extract the sign bit (upper one) */
+  sign_bit = _mm_and_ps(sign_bit, *(v4sf*)_ps_sign_mask);
+  
+  /* scale by 4/Pi */
+  y = _mm_mul_ps(x, *(v4sf*)_ps_cephes_FOPI);
+
+  //printf("plop:"); print4(y); 
+#ifdef USE_SSE2
+  /* store the integer part of y in mm0 */
+  emm2 = _mm_cvttps_epi32(y);
+  /* j=(j+1) & (~1) (see the cephes sources) */
+  emm2 = _mm_add_epi32(emm2, *(v4si*)_pi32_1);
+  emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_inv1);
+  y = _mm_cvtepi32_ps(emm2);
+  /* get the swap sign flag */
+  emm0 = _mm_and_si128(emm2, *(v4si*)_pi32_4);
+  emm0 = _mm_slli_epi32(emm0, 29);
+  /* get the polynom selection mask 
+     there is one polynom for 0 <= x <= Pi/4
+     and another one for Pi/4<x<=Pi/2
+
+     Both branches will be computed.
+  */
+  emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_2);
+  emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
+  
+  v4sf swap_sign_bit = _mm_castsi128_ps(emm0);
+  v4sf poly_mask = _mm_castsi128_ps(emm2);
+  sign_bit = _mm_xor_ps(sign_bit, swap_sign_bit);
+#else
+  /* store the integer part of y in mm0:mm1 */
+  xmm2 = _mm_movehl_ps(xmm2, y);
+  mm2 = _mm_cvttps_pi32(y);
+  mm3 = _mm_cvttps_pi32(xmm2);
+  /* j=(j+1) & (~1) (see the cephes sources) */
+  mm2 = _mm_add_pi32(mm2, *(v2si*)_pi32_1);
+  mm3 = _mm_add_pi32(mm3, *(v2si*)_pi32_1);
+  mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_inv1);
+  mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_inv1);
+  y = _mm_cvtpi32x2_ps(mm2, mm3);
+  /* get the swap sign flag */
+  mm0 = _mm_and_si64(mm2, *(v2si*)_pi32_4);
+  mm1 = _mm_and_si64(mm3, *(v2si*)_pi32_4);
+  mm0 = _mm_slli_pi32(mm0, 29);
+  mm1 = _mm_slli_pi32(mm1, 29);
+  /* get the polynom selection mask */
+  mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_2);
+  mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_2);
+  mm2 = _mm_cmpeq_pi32(mm2, _mm_setzero_si64());
+  mm3 = _mm_cmpeq_pi32(mm3, _mm_setzero_si64());
+  v4sf swap_sign_bit, poly_mask;
+  COPY_MM_TO_XMM(mm0, mm1, swap_sign_bit);
+  COPY_MM_TO_XMM(mm2, mm3, poly_mask);
+  sign_bit = _mm_xor_ps(sign_bit, swap_sign_bit);
+  _mm_empty(); /* good-bye mmx */
+#endif
+  
+  /* The magic pass: "Extended precision modular arithmetic" 
+     x = ((x - y * DP1) - y * DP2) - y * DP3; */
+  xmm1 = *(v4sf*)_ps_minus_cephes_DP1;
+  xmm2 = *(v4sf*)_ps_minus_cephes_DP2;
+  xmm3 = *(v4sf*)_ps_minus_cephes_DP3;
+  xmm1 = _mm_mul_ps(y, xmm1);
+  xmm2 = _mm_mul_ps(y, xmm2);
+  xmm3 = _mm_mul_ps(y, xmm3);
+  x = _mm_add_ps(x, xmm1);
+  x = _mm_add_ps(x, xmm2);
+  x = _mm_add_ps(x, xmm3);
+
+  /* Evaluate the first polynom  (0 <= x <= Pi/4) */
+  y = *(v4sf*)_ps_coscof_p0;
+  v4sf z = _mm_mul_ps(x,x);
+
+  y = _mm_mul_ps(y, z);
+  y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p1);
+  y = _mm_mul_ps(y, z);
+  y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p2);
+  y = _mm_mul_ps(y, z);
+  y = _mm_mul_ps(y, z);
+  v4sf tmp = _mm_mul_ps(z, *(v4sf*)_ps_0p5);
+  y = _mm_sub_ps(y, tmp);
+  y = _mm_add_ps(y, *(v4sf*)_ps_1);
+  
+  /* Evaluate the second polynom  (Pi/4 <= x <= 0) */
+
+  v4sf y2 = *(v4sf*)_ps_sincof_p0;
+  y2 = _mm_mul_ps(y2, z);
+  y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p1);
+  y2 = _mm_mul_ps(y2, z);
+  y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p2);
+  y2 = _mm_mul_ps(y2, z);
+  y2 = _mm_mul_ps(y2, x);
+  y2 = _mm_add_ps(y2, x);
+
+  /* select the correct result from the two polynoms */  
+  xmm3 = poly_mask;
+  y2 = _mm_and_ps(xmm3, y2); //, xmm3);
+  y = _mm_andnot_ps(xmm3, y);
+  y = _mm_add_ps(y,y2);
+  /* update the sign */
+  y = _mm_xor_ps(y, sign_bit);
+
+  return y;
+}
+
+/* almost the same as sin_ps */
+v4sf cos_ps(v4sf x) { // any x
+  v4sf xmm1, xmm2 = _mm_setzero_ps(), xmm3, y;
+#ifdef USE_SSE2
+  v4si emm0, emm2;
+#else
+  v2si mm0, mm1, mm2, mm3;
+#endif
+  /* take the absolute value */
+  x = _mm_and_ps(x, *(v4sf*)_ps_inv_sign_mask);
+  
+  /* scale by 4/Pi */
+  y = _mm_mul_ps(x, *(v4sf*)_ps_cephes_FOPI);
+  
+#ifdef USE_SSE2
+  /* store the integer part of y in mm0 */
+  emm2 = _mm_cvttps_epi32(y);
+  /* j=(j+1) & (~1) (see the cephes sources) */
+  emm2 = _mm_add_epi32(emm2, *(v4si*)_pi32_1);
+  emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_inv1);
+  y = _mm_cvtepi32_ps(emm2);
+
+  emm2 = _mm_sub_epi32(emm2, *(v4si*)_pi32_2);
+  
+  /* get the swap sign flag */
+  emm0 = _mm_andnot_si128(emm2, *(v4si*)_pi32_4);
+  emm0 = _mm_slli_epi32(emm0, 29);
+  /* get the polynom selection mask */
+  emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_2);
+  emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
+  
+  v4sf sign_bit = _mm_castsi128_ps(emm0);
+  v4sf poly_mask = _mm_castsi128_ps(emm2);
+#else
+  /* store the integer part of y in mm0:mm1 */
+  xmm2 = _mm_movehl_ps(xmm2, y);
+  mm2 = _mm_cvttps_pi32(y);
+  mm3 = _mm_cvttps_pi32(xmm2);
+
+  /* j=(j+1) & (~1) (see the cephes sources) */
+  mm2 = _mm_add_pi32(mm2, *(v2si*)_pi32_1);
+  mm3 = _mm_add_pi32(mm3, *(v2si*)_pi32_1);
+  mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_inv1);
+  mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_inv1);
+
+  y = _mm_cvtpi32x2_ps(mm2, mm3);
+
+
+  mm2 = _mm_sub_pi32(mm2, *(v2si*)_pi32_2);
+  mm3 = _mm_sub_pi32(mm3, *(v2si*)_pi32_2);
+
+  /* get the swap sign flag in mm0:mm1 and the 
+     polynom selection mask in mm2:mm3 */
+
+  mm0 = _mm_andnot_si64(mm2, *(v2si*)_pi32_4);
+  mm1 = _mm_andnot_si64(mm3, *(v2si*)_pi32_4);
+  mm0 = _mm_slli_pi32(mm0, 29);
+  mm1 = _mm_slli_pi32(mm1, 29);
+
+  mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_2);
+  mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_2);
+
+  mm2 = _mm_cmpeq_pi32(mm2, _mm_setzero_si64());
+  mm3 = _mm_cmpeq_pi32(mm3, _mm_setzero_si64());
+
+  v4sf sign_bit, poly_mask;
+  COPY_MM_TO_XMM(mm0, mm1, sign_bit);
+  COPY_MM_TO_XMM(mm2, mm3, poly_mask);
+  _mm_empty(); /* good-bye mmx */
+#endif
+  /* The magic pass: "Extended precision modular arithmetic" 
+     x = ((x - y * DP1) - y * DP2) - y * DP3; */
+  xmm1 = *(v4sf*)_ps_minus_cephes_DP1;
+  xmm2 = *(v4sf*)_ps_minus_cephes_DP2;
+  xmm3 = *(v4sf*)_ps_minus_cephes_DP3;
+  xmm1 = _mm_mul_ps(y, xmm1);
+  xmm2 = _mm_mul_ps(y, xmm2);
+  xmm3 = _mm_mul_ps(y, xmm3);
+  x = _mm_add_ps(x, xmm1);
+  x = _mm_add_ps(x, xmm2);
+  x = _mm_add_ps(x, xmm3);
+  
+  /* Evaluate the first polynom  (0 <= x <= Pi/4) */
+  y = *(v4sf*)_ps_coscof_p0;
+  v4sf z = _mm_mul_ps(x,x);
+
+  y = _mm_mul_ps(y, z);
+  y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p1);
+  y = _mm_mul_ps(y, z);
+  y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p2);
+  y = _mm_mul_ps(y, z);
+  y = _mm_mul_ps(y, z);
+  v4sf tmp = _mm_mul_ps(z, *(v4sf*)_ps_0p5);
+  y = _mm_sub_ps(y, tmp);
+  y = _mm_add_ps(y, *(v4sf*)_ps_1);
+  
+  /* Evaluate the second polynom  (Pi/4 <= x <= 0) */
+
+  v4sf y2 = *(v4sf*)_ps_sincof_p0;
+  y2 = _mm_mul_ps(y2, z);
+  y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p1);
+  y2 = _mm_mul_ps(y2, z);
+  y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p2);
+  y2 = _mm_mul_ps(y2, z);
+  y2 = _mm_mul_ps(y2, x);
+  y2 = _mm_add_ps(y2, x);
+
+  /* select the correct result from the two polynoms */  
+  xmm3 = poly_mask;
+  y2 = _mm_and_ps(xmm3, y2); //, xmm3);
+  y = _mm_andnot_ps(xmm3, y);
+  y = _mm_add_ps(y,y2);
+  /* update the sign */
+  y = _mm_xor_ps(y, sign_bit);
+
+  return y;
+}
+
+/* since sin_ps and cos_ps are almost identical, sincos_ps could replace both of them..
+   it is almost as fast, and gives you a free cosine with your sine */
+void sincos_ps(v4sf x, v4sf *s, v4sf *c) {
+  v4sf xmm1, xmm2, xmm3 = _mm_setzero_ps(), sign_bit_sin, y;
+#ifdef USE_SSE2
+  v4si emm0, emm2, emm4;
+#else
+  v2si mm0, mm1, mm2, mm3, mm4, mm5;
+#endif
+  sign_bit_sin = x;
+  /* take the absolute value */
+  x = _mm_and_ps(x, *(v4sf*)_ps_inv_sign_mask);
+  /* extract the sign bit (upper one) */
+  sign_bit_sin = _mm_and_ps(sign_bit_sin, *(v4sf*)_ps_sign_mask);
+  
+  /* scale by 4/Pi */
+  y = _mm_mul_ps(x, *(v4sf*)_ps_cephes_FOPI);
+    
+#ifdef USE_SSE2
+  /* store the integer part of y in emm2 */
+  emm2 = _mm_cvttps_epi32(y);
+
+  /* j=(j+1) & (~1) (see the cephes sources) */
+  emm2 = _mm_add_epi32(emm2, *(v4si*)_pi32_1);
+  emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_inv1);
+  y = _mm_cvtepi32_ps(emm2);
+
+  emm4 = emm2;
+
+  /* get the swap sign flag for the sine */
+  emm0 = _mm_and_si128(emm2, *(v4si*)_pi32_4);
+  emm0 = _mm_slli_epi32(emm0, 29);
+  v4sf swap_sign_bit_sin = _mm_castsi128_ps(emm0);
+
+  /* get the polynom selection mask for the sine*/
+  emm2 = _mm_and_si128(emm2, *(v4si*)_pi32_2);
+  emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
+  v4sf poly_mask = _mm_castsi128_ps(emm2);
+#else
+  /* store the integer part of y in mm2:mm3 */
+  xmm3 = _mm_movehl_ps(xmm3, y);
+  mm2 = _mm_cvttps_pi32(y);
+  mm3 = _mm_cvttps_pi32(xmm3);
+
+  /* j=(j+1) & (~1) (see the cephes sources) */
+  mm2 = _mm_add_pi32(mm2, *(v2si*)_pi32_1);
+  mm3 = _mm_add_pi32(mm3, *(v2si*)_pi32_1);
+  mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_inv1);
+  mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_inv1);
+
+  y = _mm_cvtpi32x2_ps(mm2, mm3);
+
+  mm4 = mm2;
+  mm5 = mm3;
+
+  /* get the swap sign flag for the sine */
+  mm0 = _mm_and_si64(mm2, *(v2si*)_pi32_4);
+  mm1 = _mm_and_si64(mm3, *(v2si*)_pi32_4);
+  mm0 = _mm_slli_pi32(mm0, 29);
+  mm1 = _mm_slli_pi32(mm1, 29);
+  v4sf swap_sign_bit_sin;
+  COPY_MM_TO_XMM(mm0, mm1, swap_sign_bit_sin);
+
+  /* get the polynom selection mask for the sine */
+
+  mm2 = _mm_and_si64(mm2, *(v2si*)_pi32_2);
+  mm3 = _mm_and_si64(mm3, *(v2si*)_pi32_2);
+  mm2 = _mm_cmpeq_pi32(mm2, _mm_setzero_si64());
+  mm3 = _mm_cmpeq_pi32(mm3, _mm_setzero_si64());
+  v4sf poly_mask;
+  COPY_MM_TO_XMM(mm2, mm3, poly_mask);
+#endif
+
+  /* The magic pass: "Extended precision modular arithmetic" 
+     x = ((x - y * DP1) - y * DP2) - y * DP3; */
+  xmm1 = *(v4sf*)_ps_minus_cephes_DP1;
+  xmm2 = *(v4sf*)_ps_minus_cephes_DP2;
+  xmm3 = *(v4sf*)_ps_minus_cephes_DP3;
+  xmm1 = _mm_mul_ps(y, xmm1);
+  xmm2 = _mm_mul_ps(y, xmm2);
+  xmm3 = _mm_mul_ps(y, xmm3);
+  x = _mm_add_ps(x, xmm1);
+  x = _mm_add_ps(x, xmm2);
+  x = _mm_add_ps(x, xmm3);
+
+#ifdef USE_SSE2
+  emm4 = _mm_sub_epi32(emm4, *(v4si*)_pi32_2);
+  emm4 = _mm_andnot_si128(emm4, *(v4si*)_pi32_4);
+  emm4 = _mm_slli_epi32(emm4, 29);
+  v4sf sign_bit_cos = _mm_castsi128_ps(emm4);
+#else
+  /* get the sign flag for the cosine */
+  mm4 = _mm_sub_pi32(mm4, *(v2si*)_pi32_2);
+  mm5 = _mm_sub_pi32(mm5, *(v2si*)_pi32_2);
+  mm4 = _mm_andnot_si64(mm4, *(v2si*)_pi32_4);
+  mm5 = _mm_andnot_si64(mm5, *(v2si*)_pi32_4);
+  mm4 = _mm_slli_pi32(mm4, 29);
+  mm5 = _mm_slli_pi32(mm5, 29);
+  v4sf sign_bit_cos;
+  COPY_MM_TO_XMM(mm4, mm5, sign_bit_cos);
+  _mm_empty(); /* good-bye mmx */
+#endif
+
+  sign_bit_sin = _mm_xor_ps(sign_bit_sin, swap_sign_bit_sin);
+
+  
+  /* Evaluate the first polynom  (0 <= x <= Pi/4) */
+  v4sf z = _mm_mul_ps(x,x);
+  y = *(v4sf*)_ps_coscof_p0;
+
+  y = _mm_mul_ps(y, z);
+  y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p1);
+  y = _mm_mul_ps(y, z);
+  y = _mm_add_ps(y, *(v4sf*)_ps_coscof_p2);
+  y = _mm_mul_ps(y, z);
+  y = _mm_mul_ps(y, z);
+  v4sf tmp = _mm_mul_ps(z, *(v4sf*)_ps_0p5);
+  y = _mm_sub_ps(y, tmp);
+  y = _mm_add_ps(y, *(v4sf*)_ps_1);
+  
+  /* Evaluate the second polynom  (Pi/4 <= x <= 0) */
+
+  v4sf y2 = *(v4sf*)_ps_sincof_p0;
+  y2 = _mm_mul_ps(y2, z);
+  y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p1);
+  y2 = _mm_mul_ps(y2, z);
+  y2 = _mm_add_ps(y2, *(v4sf*)_ps_sincof_p2);
+  y2 = _mm_mul_ps(y2, z);
+  y2 = _mm_mul_ps(y2, x);
+  y2 = _mm_add_ps(y2, x);
+
+  /* select the correct result from the two polynoms */  
+  xmm3 = poly_mask;
+  v4sf ysin2 = _mm_and_ps(xmm3, y2);
+  v4sf ysin1 = _mm_andnot_ps(xmm3, y);
+  y2 = _mm_sub_ps(y2,ysin2);
+  y = _mm_sub_ps(y, ysin1);
+
+  xmm1 = _mm_add_ps(ysin1,ysin2);
+  xmm2 = _mm_add_ps(y,y2);
+ 
+  /* update the sign */
+  *s = _mm_xor_ps(xmm1, sign_bit_sin);
+  *c = _mm_xor_ps(xmm2, sign_bit_cos);
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqvec/src/Allocators.cpp	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,81 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    bqvec
+
+    A small library for vector arithmetic and allocation in C++ using
+    raw C pointer arrays.
+
+    Copyright 2007-2014 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#include "Allocators.h"
+
+#ifdef HAVE_IPP
+#include <ipps.h>
+#endif
+
+#include <iostream>
+using std::cerr;
+using std::endl;
+
+namespace breakfastquay {
+
+#ifdef HAVE_IPP
+
+template <>
+float *allocate(size_t count)
+{
+    float *ptr = ippsMalloc_32f(count);
+    if (!ptr) throw (std::bad_alloc());
+    return ptr;
+}
+
+template <>
+double *allocate(size_t count)
+{
+    double *ptr = ippsMalloc_64f(count);
+    if (!ptr) throw (std::bad_alloc());
+    return ptr;
+}
+
+template <>
+void deallocate(float *ptr)
+{
+    if (ptr) ippsFree((void *)ptr);
+}
+
+template <>
+void deallocate(double *ptr)
+{
+    if (ptr) ippsFree((void *)ptr);
+}
+
+#endif
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqvec/src/Barrier.cpp	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,67 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    bqvec
+
+    A small library for vector arithmetic and allocation in C++ using
+    raw C pointer arrays.
+
+    Copyright 2007-2015 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#include "Barrier.h"
+
+namespace breakfastquay {
+
+void system_memorybarrier()
+{
+#if defined __APPLE__
+
+    OSMemoryBarrier();
+    
+#elif (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)
+
+    __sync_synchronize();
+
+#elif defined _WIN32 
+
+#if defined __MSVC__    
+    MemoryBarrier();
+#else /* (mingw) */
+    LONG Barrier = 0;
+    __asm__ __volatile__("xchgl %%eax,%0 "
+                         : "=r" (Barrier));
+#endif
+
+#else
+#warning "No memory barrier defined"
+#endif
+    
+}
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqvec/src/VectorOpsComplex.cpp	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,373 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+/*
+    bqvec
+
+    A small library for vector arithmetic and allocation in C++ using
+    raw C pointer arrays.
+
+    Copyright 2007-2015 Particular Programs Ltd.
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
+    ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
+    CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+    Except as contained in this notice, the names of Chris Cannam and
+    Particular Programs Ltd shall not be used in advertising or
+    otherwise to promote the sale, use or other dealings in this
+    Software without prior written authorization.
+*/
+
+#include "VectorOpsComplex.h"
+
+#include <cassert>
+
+#ifdef __MSVC__
+#include <malloc.h>
+#define alloca _alloca
+#endif
+
+#if defined USE_POMMIER_MATHFUN
+#if defined __ARMEL__
+#include "pommier/neon_mathfun.h"
+#else
+#include "pommier/sse_mathfun.h"
+#endif
+#endif
+
+#ifdef __MSVC__
+#include <malloc.h>
+#define alloca _alloca
+#endif
+
+namespace breakfastquay {
+
+#ifdef USE_APPROXIMATE_ATAN2
+float approximate_atan2f(float real, float imag)
+{
+    static const float pi = M_PI;
+    static const float pi2 = M_PI / 2;
+
+    float atan;
+
+    if (real == 0.f) {
+
+        if (imag > 0.0f) atan = pi2;
+        else if (imag == 0.0f) atan = 0.0f;
+        else atan = -pi2;
+
+    } else {
+
+        float z = imag/real;
+
+        if (fabsf(z) < 1.f) {
+            atan = z / (1.f + 0.28f * z * z);
+            if (real < 0.f) {
+                if (imag < 0.f) atan -= pi;
+                else atan += pi;
+            }
+        } else {
+            atan = pi2 - z / (z * z + 0.28f);
+            if (imag < 0.f) atan -= pi;
+        }
+    }
+}
+#endif
+
+#if defined USE_POMMIER_MATHFUN
+
+#ifdef __ARMEL__
+typedef union {
+  float f[4];
+  int i[4];
+  v4sf  v;
+} V4SF;
+#else
+typedef ALIGN16_BEG union {
+  float f[4];
+  int i[4];
+  v4sf  v;
+} ALIGN16_END V4SF;
+#endif
+
+void
+v_polar_to_cartesian_pommier(float *const BQ_R__ real,
+                             float *const BQ_R__ imag,
+                             const float *const BQ_R__ mag,
+                             const float *const BQ_R__ phase,
+                             const int count)
+{
+    int idx = 0, tidx = 0;
+    int i = 0;
+
+    for (int i = 0; i + 4 < count; i += 4) {
+
+	V4SF fmag, fphase, fre, fim;
+
+        for (int j = 0; j < 3; ++j) {
+            fmag.f[j] = mag[idx];
+            fphase.f[j] = phase[idx++];
+        }
+
+	sincos_ps(fphase.v, &fim.v, &fre.v);
+
+        for (int j = 0; j < 3; ++j) {
+            real[tidx] = fre.f[j] * fmag.f[j];
+            imag[tidx++] = fim.f[j] * fmag.f[j];
+        }
+    }
+
+    while (i < count) {
+        float re, im;
+        c_phasor(&re, &im, phase[i]);
+        real[tidx] = re * mag[i];
+        imag[tidx++] = im * mag[i];
+        ++i;
+    }
+}    
+
+void
+v_polar_interleaved_to_cartesian_inplace_pommier(float *const BQ_R__ srcdst,
+                                                 const int count)
+{
+    int i;
+    int idx = 0, tidx = 0;
+
+    for (i = 0; i + 4 < count; i += 4) {
+
+	V4SF fmag, fphase, fre, fim;
+
+        for (int j = 0; j < 3; ++j) {
+            fmag.f[j] = srcdst[idx++];
+            fphase.f[j] = srcdst[idx++];
+        }
+
+	sincos_ps(fphase.v, &fim.v, &fre.v);
+
+        for (int j = 0; j < 3; ++j) {
+            srcdst[tidx++] = fre.f[j] * fmag.f[j];
+            srcdst[tidx++] = fim.f[j] * fmag.f[j];
+        }
+    }
+
+    while (i < count) {
+        float real, imag;
+        float mag = srcdst[idx++];
+        float phase = srcdst[idx++];
+        c_phasor(&real, &imag, phase);
+        srcdst[tidx++] = real * mag;
+        srcdst[tidx++] = imag * mag;
+        ++i;
+    }
+}    
+
+void
+v_polar_to_cartesian_interleaved_pommier(float *const BQ_R__ dst,
+                                         const float *const BQ_R__ mag,
+                                         const float *const BQ_R__ phase,
+                                         const int count)
+{
+    int i;
+    int idx = 0, tidx = 0;
+
+    for (i = 0; i + 4 <= count; i += 4) {
+
+	V4SF fmag, fphase, fre, fim;
+
+        for (int j = 0; j < 3; ++j) {
+            fmag.f[j] = mag[idx];
+            fphase.f[j] = phase[idx];
+            ++idx;
+        }
+
+	sincos_ps(fphase.v, &fim.v, &fre.v);
+
+        for (int j = 0; j < 3; ++j) {
+            dst[tidx++] = fre.f[j] * fmag.f[j];
+            dst[tidx++] = fim.f[j] * fmag.f[j];
+        }
+    }
+
+    while (i < count) {
+        float real, imag;
+        c_phasor(&real, &imag, phase[i]);
+        dst[tidx++] = real * mag[i];
+        dst[tidx++] = imag * mag[i];
+        ++i;
+    }
+}    
+
+#endif
+
+#ifndef NO_COMPLEX_TYPES
+
+#if defined HAVE_IPP
+
+void
+v_polar_to_cartesian(bq_complex_t *const BQ_R__ dst,
+		     const bq_complex_element_t *const BQ_R__ mag,
+		     const bq_complex_element_t *const BQ_R__ phase,
+		     const int count)
+{
+    if (sizeof(bq_complex_element_t) == sizeof(float)) {
+	ippsPolarToCart_32fc((const float *)mag, (const float *)phase,
+                             (Ipp32fc *)dst, count);
+    } else {
+	ippsPolarToCart_64fc((const double *)mag, (const double *)phase,
+                             (Ipp64fc *)dst, count);
+    }
+}
+
+#elif defined HAVE_VDSP
+
+void
+v_polar_to_cartesian(bq_complex_t *const BQ_R__ dst,
+		     const bq_complex_element_t *const BQ_R__ mag,
+		     const bq_complex_element_t *const BQ_R__ phase,
+		     const int count)
+{
+    bq_complex_element_t *sc = (bq_complex_element_t *)
+	alloca(count * 2 * sizeof(bq_complex_element_t));
+
+    if (sizeof(bq_complex_element_t) == sizeof(float)) {
+        vvsincosf((float *)sc, (float *)(sc + count), (float *)phase, &count);
+    } else {
+        vvsincos((double *)sc, (double *)(sc + count), (double *)phase, &count);
+    }
+
+    int sini = 0;
+    int cosi = count;
+
+    for (int i = 0; i < count; ++i) {
+	dst[i].re = mag[i] * sc[cosi++];
+	dst[i].im = mag[i] * sc[sini++];
+    }
+}    
+
+#else
+
+void
+v_polar_to_cartesian(bq_complex_t *const BQ_R__ dst,
+		     const bq_complex_element_t *const BQ_R__ mag,
+		     const bq_complex_element_t *const BQ_R__ phase,
+		     const int count)
+{
+    for (int i = 0; i < count; ++i) {
+	dst[i] = c_phasor(phase[i]);
+    }
+    for (int i = 0; i < count; ++i) {
+        dst[i].re *= mag[i];
+        dst[i].im *= mag[i];
+    }
+}    
+
+#endif
+
+#if defined USE_POMMIER_MATHFUN
+
+//!!! further tests reqd.  This is only single precision but it seems
+//!!! to be much faster than normal math library sincos.  The comments
+//!!! note that precision suffers for high arguments to sincos though,
+//!!! and that is probably a common case for us
+
+void
+v_polar_interleaved_to_cartesian(bq_complex_t *const BQ_R__ dst,
+				 const bq_complex_element_t *const BQ_R__ src,
+				 const int count)
+{
+    int idx = 0, tidx = 0;
+
+    for (int i = 0; i < count; i += 4) {
+
+	V4SF fmag, fphase, fre, fim;
+
+        for (int j = 0; j < 3; ++j) {
+            fmag.f[j] = src[idx++];
+            fphase.f[j] = src[idx++];
+        }
+
+	sincos_ps(fphase.v, &fim.v, &fre.v);
+
+        for (int j = 0; j < 3; ++j) {
+            dst[tidx].re = fre.f[j] * fmag.f[j];
+            dst[tidx++].im = fim.f[j] * fmag.f[j];
+        }
+    }
+}    
+
+#elif (defined HAVE_IPP || defined HAVE_VDSP)
+
+// with a vector library, it should be faster to deinterleave and call
+// the basic fn
+
+void
+v_polar_interleaved_to_cartesian(bq_complex_t *const BQ_R__ dst,
+				 const bq_complex_element_t *const BQ_R__ src,
+				 const int count)
+{
+    bq_complex_element_t *mag = (bq_complex_element_t *)
+	alloca(count * sizeof(bq_complex_element_t));
+    bq_complex_element_t *phase = (bq_complex_element_t *)
+	alloca(count * sizeof(bq_complex_element_t));
+    bq_complex_element_t *magphase[] = { mag, phase };
+
+    v_deinterleave(magphase, src, 2, count);
+    v_polar_to_cartesian(dst, mag, phase, count);
+}
+
+#else
+
+// without a vector library, better avoid the deinterleave step
+
+void
+v_polar_interleaved_to_cartesian(bq_complex_t *const BQ_R__ dst,
+				 const bq_complex_element_t *const BQ_R__ src,
+				 const int count)
+{
+    bq_complex_element_t mag, phase;
+    int idx = 0;
+    for (int i = 0; i < count; ++i) {
+        mag = src[idx++];
+        phase = src[idx++];
+        dst[i] = c_phasor(phase);
+        dst[i].re *= mag;
+        dst[i].im *= mag;
+    }
+}
+
+#endif
+
+void
+v_polar_interleaved_to_cartesian_inplace(bq_complex_element_t *const BQ_R__ srcdst,
+                                         const int count)
+{
+    // Not ideal
+    bq_complex_element_t mag, phase;
+    int ii = 0, io = 0;
+    for (int i = 0; i < count; ++i) {
+        mag = srcdst[ii++];
+        phase = srcdst[ii++];
+        bq_complex_t p = c_phasor(phase);
+        srcdst[io++] = mag * p.re;
+        srcdst[io++] = mag * p.im;
+    }
+}
+
+#endif
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqvec/test/TestVectorOps.cpp	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,254 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+#include "bqvec/VectorOpsComplex.h"
+
+#include <iostream>
+#include <cstdlib>
+
+#include <time.h>
+
+using namespace std;
+
+namespace breakfastquay {
+
+namespace Test {
+
+#ifdef _WIN32
+#define drand48() (-1+2*((float)rand())/RAND_MAX)
+#endif
+
+bool
+testMultiply()
+{
+    cerr << "testVectorOps: testing v_multiply complex" << endl;
+
+    const int N = 1024;
+    bq_complex_t target[N];
+    bq_complex_t src1[N];
+    bq_complex_t src2[N];
+
+    for (int i = 0; i < N; ++i) {
+	src1[i].re = drand48();
+	src1[i].im = drand48();
+	src2[i].re = drand48();
+	src2[i].im = drand48();
+    }
+
+    double mean, first, last, total = 0;
+    for (int i = 0; i < N; ++i) {
+        bq_complex_t result;
+        c_multiply(result, src1[i], src2[i]);
+	if (i == 0) first = result.re;
+	if (i == N-1) last = result.im;
+	total += result.re;
+	total += result.im;
+    }
+    mean = total / (N*2);
+    cerr << "Naive method: mean = " << mean << ", first = " << first
+	 << ", last = " << last << endl;
+
+    v_multiply(target, src1, src2, N);
+    total = 0;
+
+    for (int i = 0; i < N; ++i) {
+	if (i == 0) first = target[i].re;
+	if (i == N-1) last = target[i].im;
+	total += target[i].re;
+	total += target[i].im;
+    }
+    mean = total / (N*2);
+    cerr << "v_multiply: mean = " << mean << ", first = " << first
+	 << ", last = " << last << endl;
+
+    int iterations = 50000;
+    cerr << "Iterations: " << iterations << endl;
+	
+    cerr << "CLOCKS_PER_SEC = " << CLOCKS_PER_SEC << endl;
+    float divisor = float(CLOCKS_PER_SEC) / 1000.f;
+
+    clock_t start = clock();
+
+    for (int j = 0; j < iterations; ++j) {
+	for (int i = 0; i < N; ++i) {
+            c_multiply(target[i], src1[i], src2[i]);
+	}
+    }
+    
+    clock_t end = clock();
+
+    cerr << "Time for naive method: " << float(end - start)/divisor << endl;
+
+    start = clock();
+
+    for (int j = 0; j < iterations; ++j) {
+        v_multiply(target, src1, src2, N);
+    }
+    
+    end = clock();
+
+    cerr << "Time for v_multiply: " << float(end - start)/divisor << endl;
+
+    return true;
+}
+
+bool
+testPolarToCart()
+{
+    cerr << "testVectorOps: testing v_polar_to_cartesian" << endl;
+
+    const int N = 1024;
+    bq_complex_t target[N];
+    double mag[N];
+    double phase[N];
+
+    for (int i = 0; i < N; ++i) {
+	mag[i] = drand48();
+	phase[i] = (drand48() * M_PI * 2) - M_PI;
+    }
+
+    double mean, first, last, total = 0;
+    for (int i = 0; i < N; ++i) {
+	double real = mag[i] * cos(phase[i]);
+	double imag = mag[i] * sin(phase[i]);
+	if (i == 0) first = real;
+	if (i == N-1) last = imag;
+	total += real;
+	total += imag;
+    }
+    mean = total / (N*2);
+    cerr << "Naive method: mean = " << mean << ", first = " << first
+	 << ", last = " << last << endl;
+
+    v_polar_to_cartesian(target, mag, phase, N);
+
+    total = 0;
+
+    for (int i = 0; i < N; ++i) {
+	if (i == 0) first = target[i].re;
+	if (i == N-1) last = target[i].im;
+	total += target[i].re;
+	total += target[i].im;
+    }
+    mean = total / (N*2);
+    cerr << "v_polar_to_cartesian: mean = " << mean << ", first = " << first
+	 << ", last = " << last << endl;
+
+    int iterations = 10000;
+    cerr << "Iterations: " << iterations << endl;
+	
+    cerr << "CLOCKS_PER_SEC = " << CLOCKS_PER_SEC << endl;
+    float divisor = float(CLOCKS_PER_SEC) / 1000.f;
+
+    clock_t start = clock();
+
+    for (int j = 0; j < iterations; ++j) {
+	for (int i = 0; i < N; ++i) {
+	    target[i].re = mag[i] * cos(phase[i]);
+	    target[i].im = mag[i] * sin(phase[i]);
+	}
+    }
+    
+    clock_t end = clock();
+
+    cerr << "Time for naive method: " << float(end - start)/divisor << endl;
+
+    start = clock();
+
+    for (int j = 0; j < iterations; ++j) {
+	v_polar_to_cartesian(target, mag, phase, N);
+    }
+    
+    end = clock();
+
+    cerr << "Time for v_polar_to_cartesian: " << float(end - start)/divisor << endl;
+
+    return true;
+}
+
+bool
+testPolarToCartInterleaved()
+{
+    cerr << "testVectorOps: testing v_polar_interleaved_to_cartesian" << endl;
+
+    const int N = 1024;
+    bq_complex_t target[N];
+    double source[N*2];
+
+    for (int i = 0; i < N; ++i) {
+	source[i*2] = drand48();
+	source[i*2+1] = (drand48() * M_PI * 2) - M_PI;
+    }
+
+    double mean, first, last, total = 0;
+    for (int i = 0; i < N; ++i) {
+	double real = source[i*2] * cos(source[i*2+1]);
+	double imag = source[i*2] * sin(source[i*2+1]);
+	if (i == 0) first = real;
+	if (i == N-1) last = imag;
+	total += real;
+	total += imag;
+    }
+    mean = total / (N*2);
+    cerr << "Naive method: mean = " << mean << ", first = " << first
+	 << ", last = " << last << endl;
+
+    v_polar_interleaved_to_cartesian(target, source, N);
+
+    total = 0;
+
+    for (int i = 0; i < N; ++i) {
+	if (i == 0) first = target[i].re;
+	if (i == N-1) last = target[i].im;
+	total += target[i].re;
+	total += target[i].im;
+    }
+    mean = total / (N*2);
+    cerr << "v_polar_interleaved_to_cartesian: mean = " << mean << ", first = " << first
+	 << ", last = " << last << endl;
+
+    int iterations = 10000;
+    cerr << "Iterations: " << iterations << endl;
+	
+    cerr << "CLOCKS_PER_SEC = " << CLOCKS_PER_SEC << endl;
+    float divisor = float(CLOCKS_PER_SEC) / 1000.f;
+
+    clock_t start = clock();
+
+    for (int j = 0; j < iterations; ++j) {
+	for (int i = 0; i < N; ++i) {
+	    target[i].re = source[i*2] * cos(source[i*2+1]);
+	    target[i].im = source[i*2] * sin(source[i*2+1]);
+	}
+    }
+    
+    clock_t end = clock();
+
+    cerr << "Time for naive method: " << float(end - start)/divisor << endl;
+
+    start = clock();
+
+    for (int j = 0; j < iterations; ++j) {
+	v_polar_interleaved_to_cartesian(target, source, N);
+    }
+    
+    end = clock();
+
+    cerr << "Time for v_polar_interleaved_to_cartesian: " << float(end - start)/divisor << endl;
+
+    return true;
+}
+
+bool
+testVectorOps()
+{
+    if (!testMultiply()) return false;
+    if (!testPolarToCart()) return false;
+    if (!testPolarToCartInterleaved()) return false;
+    
+    return true;
+}
+
+}
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/bqvec/test/TestVectorOps.h	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,12 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
+
+namespace breakfastquay {
+
+namespace Test {
+
+bool testVectorOps();
+
+}
+
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/native-fft-test/native-fft-test.xcodeproj/project.pbxproj	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,380 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		A8525B8D1BD2A7A6009FCE68 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = A8525B8C1BD2A7A6009FCE68 /* main.m */; };
+		A8525B901BD2A7A6009FCE68 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = A8525B8F1BD2A7A6009FCE68 /* AppDelegate.m */; };
+		A8525B961BD2A7A6009FCE68 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A8525B941BD2A7A6009FCE68 /* Main.storyboard */; };
+		A8525B981BD2A7A6009FCE68 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A8525B971BD2A7A6009FCE68 /* Images.xcassets */; };
+		A8525B9B1BD2A7A6009FCE68 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = A8525B991BD2A7A6009FCE68 /* LaunchScreen.xib */; };
+		A8525BA71BD2A7A6009FCE68 /* native_fft_testTests.m in Sources */ = {isa = PBXBuildFile; fileRef = A8525BA61BD2A7A6009FCE68 /* native_fft_testTests.m */; };
+		A8525BB11BD2A852009FCE68 /* ViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = A8525BB01BD2A852009FCE68 /* ViewController.mm */; };
+		A8525BB31BD2AB50009FCE68 /* FFT.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A8525BB21BD2AB50009FCE68 /* FFT.cpp */; };
+		A8525BB51BD2AD16009FCE68 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A8525BB41BD2AD16009FCE68 /* Accelerate.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		A8525BA11BD2A7A6009FCE68 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = A8525B7F1BD2A7A6009FCE68 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = A8525B861BD2A7A6009FCE68;
+			remoteInfo = "native-fft-test";
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+		A8525B871BD2A7A6009FCE68 /* native-fft-test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "native-fft-test.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+		A8525B8B1BD2A7A6009FCE68 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		A8525B8C1BD2A7A6009FCE68 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+		A8525B8E1BD2A7A6009FCE68 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+		A8525B8F1BD2A7A6009FCE68 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+		A8525B911BD2A7A6009FCE68 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
+		A8525B951BD2A7A6009FCE68 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+		A8525B971BD2A7A6009FCE68 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
+		A8525B9A1BD2A7A6009FCE68 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
+		A8525BA01BD2A7A6009FCE68 /* native-fft-testTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "native-fft-testTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+		A8525BA51BD2A7A6009FCE68 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		A8525BA61BD2A7A6009FCE68 /* native_fft_testTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = native_fft_testTests.m; sourceTree = "<group>"; };
+		A8525BB01BD2A852009FCE68 /* ViewController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = ViewController.mm; path = "native-fft-test/ViewController.mm"; sourceTree = "<group>"; };
+		A8525BB21BD2AB50009FCE68 /* FFT.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = FFT.cpp; path = ../bqfft/src/FFT.cpp; sourceTree = "<group>"; };
+		A8525BB41BD2AD16009FCE68 /* Accelerate.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Accelerate.framework; path = System/Library/Frameworks/Accelerate.framework; sourceTree = SDKROOT; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		A8525B841BD2A7A6009FCE68 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				A8525BB51BD2AD16009FCE68 /* Accelerate.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		A8525B9D1BD2A7A6009FCE68 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		A8525B7E1BD2A7A6009FCE68 = {
+			isa = PBXGroup;
+			children = (
+				A8525BB41BD2AD16009FCE68 /* Accelerate.framework */,
+				A8525BB21BD2AB50009FCE68 /* FFT.cpp */,
+				A8525BB01BD2A852009FCE68 /* ViewController.mm */,
+				A8525B891BD2A7A6009FCE68 /* native-fft-test */,
+				A8525BA31BD2A7A6009FCE68 /* native-fft-testTests */,
+				A8525B881BD2A7A6009FCE68 /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		A8525B881BD2A7A6009FCE68 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				A8525B871BD2A7A6009FCE68 /* native-fft-test.app */,
+				A8525BA01BD2A7A6009FCE68 /* native-fft-testTests.xctest */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		A8525B891BD2A7A6009FCE68 /* native-fft-test */ = {
+			isa = PBXGroup;
+			children = (
+				A8525B8E1BD2A7A6009FCE68 /* AppDelegate.h */,
+				A8525B8F1BD2A7A6009FCE68 /* AppDelegate.m */,
+				A8525B911BD2A7A6009FCE68 /* ViewController.h */,
+				A8525B941BD2A7A6009FCE68 /* Main.storyboard */,
+				A8525B971BD2A7A6009FCE68 /* Images.xcassets */,
+				A8525B991BD2A7A6009FCE68 /* LaunchScreen.xib */,
+				A8525B8A1BD2A7A6009FCE68 /* Supporting Files */,
+			);
+			path = "native-fft-test";
+			sourceTree = "<group>";
+		};
+		A8525B8A1BD2A7A6009FCE68 /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				A8525B8B1BD2A7A6009FCE68 /* Info.plist */,
+				A8525B8C1BD2A7A6009FCE68 /* main.m */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+		A8525BA31BD2A7A6009FCE68 /* native-fft-testTests */ = {
+			isa = PBXGroup;
+			children = (
+				A8525BA61BD2A7A6009FCE68 /* native_fft_testTests.m */,
+				A8525BA41BD2A7A6009FCE68 /* Supporting Files */,
+			);
+			path = "native-fft-testTests";
+			sourceTree = "<group>";
+		};
+		A8525BA41BD2A7A6009FCE68 /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				A8525BA51BD2A7A6009FCE68 /* Info.plist */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		A8525B861BD2A7A6009FCE68 /* native-fft-test */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = A8525BAA1BD2A7A6009FCE68 /* Build configuration list for PBXNativeTarget "native-fft-test" */;
+			buildPhases = (
+				A8525B831BD2A7A6009FCE68 /* Sources */,
+				A8525B841BD2A7A6009FCE68 /* Frameworks */,
+				A8525B851BD2A7A6009FCE68 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = "native-fft-test";
+			productName = "native-fft-test";
+			productReference = A8525B871BD2A7A6009FCE68 /* native-fft-test.app */;
+			productType = "com.apple.product-type.application";
+		};
+		A8525B9F1BD2A7A6009FCE68 /* native-fft-testTests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = A8525BAD1BD2A7A6009FCE68 /* Build configuration list for PBXNativeTarget "native-fft-testTests" */;
+			buildPhases = (
+				A8525B9C1BD2A7A6009FCE68 /* Sources */,
+				A8525B9D1BD2A7A6009FCE68 /* Frameworks */,
+				A8525B9E1BD2A7A6009FCE68 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				A8525BA21BD2A7A6009FCE68 /* PBXTargetDependency */,
+			);
+			name = "native-fft-testTests";
+			productName = "native-fft-testTests";
+			productReference = A8525BA01BD2A7A6009FCE68 /* native-fft-testTests.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		A8525B7F1BD2A7A6009FCE68 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0610;
+				ORGANIZATIONNAME = "Chris Cannam";
+				TargetAttributes = {
+					A8525B861BD2A7A6009FCE68 = {
+						CreatedOnToolsVersion = 6.1.1;
+					};
+					A8525B9F1BD2A7A6009FCE68 = {
+						CreatedOnToolsVersion = 6.1.1;
+						TestTargetID = A8525B861BD2A7A6009FCE68;
+					};
+				};
+			};
+			buildConfigurationList = A8525B821BD2A7A6009FCE68 /* Build configuration list for PBXProject "native-fft-test" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = A8525B7E1BD2A7A6009FCE68;
+			productRefGroup = A8525B881BD2A7A6009FCE68 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				A8525B861BD2A7A6009FCE68 /* native-fft-test */,
+				A8525B9F1BD2A7A6009FCE68 /* native-fft-testTests */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		A8525B851BD2A7A6009FCE68 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				A8525B961BD2A7A6009FCE68 /* Main.storyboard in Resources */,
+				A8525B9B1BD2A7A6009FCE68 /* LaunchScreen.xib in Resources */,
+				A8525B981BD2A7A6009FCE68 /* Images.xcassets in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		A8525B9E1BD2A7A6009FCE68 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		A8525B831BD2A7A6009FCE68 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				A8525BB31BD2AB50009FCE68 /* FFT.cpp in Sources */,
+				A8525B901BD2A7A6009FCE68 /* AppDelegate.m in Sources */,
+				A8525B8D1BD2A7A6009FCE68 /* main.m in Sources */,
+				A8525BB11BD2A852009FCE68 /* ViewController.mm in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		A8525B9C1BD2A7A6009FCE68 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				A8525BA71BD2A7A6009FCE68 /* native_fft_testTests.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		A8525BA21BD2A7A6009FCE68 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = A8525B861BD2A7A6009FCE68 /* native-fft-test */;
+			targetProxy = A8525BA11BD2A7A6009FCE68 /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+		A8525B941BD2A7A6009FCE68 /* Main.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				A8525B951BD2A7A6009FCE68 /* Base */,
+			);
+			name = Main.storyboard;
+			sourceTree = "<group>";
+		};
+		A8525B991BD2A7A6009FCE68 /* LaunchScreen.xib */ = {
+			isa = PBXVariantGroup;
+			children = (
+				A8525B9A1BD2A7A6009FCE68 /* Base */,
+			);
+			name = LaunchScreen.xib;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		A8525BA91BD2A7A6009FCE68 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = YES;
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				HEADER_SEARCH_PATHS = (
+					"$(inherited)",
+					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+					"/Users/cannam/code/qm/js-dsp-test/fft/native/bqfft",
+					"/Users/cannam/code/qm/js-dsp-test/fft/native/bqvec",
+				);
+				"HEADER_SEARCH_PATHS[arch=*]" = (
+					"$(inherited)",
+					/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
+				);
+				IPHONEOS_DEPLOYMENT_TARGET = 8.1;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				OTHER_CFLAGS = (
+					"-DHAVE_VDSP",
+					"-DMALLOC_IS_ALIGNED",
+				);
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				USER_HEADER_SEARCH_PATHS = "";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		A8525BAC1BD2A7A6009FCE68 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				INFOPLIST_FILE = "native-fft-test/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
+		A8525BAF1BD2A7A6009FCE68 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(SDKROOT)/Developer/Library/Frameworks",
+					"$(inherited)",
+				);
+				INFOPLIST_FILE = "native-fft-testTests/Info.plist";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/native-fft-test.app/native-fft-test";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		A8525B821BD2A7A6009FCE68 /* Build configuration list for PBXProject "native-fft-test" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				A8525BA91BD2A7A6009FCE68 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		A8525BAA1BD2A7A6009FCE68 /* Build configuration list for PBXNativeTarget "native-fft-test" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				A8525BAC1BD2A7A6009FCE68 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		A8525BAD1BD2A7A6009FCE68 /* Build configuration list for PBXNativeTarget "native-fft-testTests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				A8525BAF1BD2A7A6009FCE68 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = A8525B7F1BD2A7A6009FCE68 /* Project object */;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/native-fft-test/native-fft-test.xcodeproj/project.xcworkspace/contents.xcworkspacedata	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:native-fft-test.xcodeproj">
+   </FileRef>
+</Workspace>
Binary file fft/native/native-fft-test/native-fft-test.xcodeproj/project.xcworkspace/xcuserdata/cannam.xcuserdatad/UserInterfaceState.xcuserstate has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/native-fft-test/native-fft-test.xcodeproj/xcuserdata/cannam.xcuserdatad/xcschemes/native-fft-test.xcscheme	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,110 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0610"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "A8525B861BD2A7A6009FCE68"
+               BuildableName = "native-fft-test.app"
+               BlueprintName = "native-fft-test"
+               ReferencedContainer = "container:native-fft-test.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "NO"
+            buildForArchiving = "NO"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "A8525B9F1BD2A7A6009FCE68"
+               BuildableName = "native-fft-testTests.xctest"
+               BlueprintName = "native-fft-testTests"
+               ReferencedContainer = "container:native-fft-test.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      buildConfiguration = "Debug">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "A8525B9F1BD2A7A6009FCE68"
+               BuildableName = "native-fft-testTests.xctest"
+               BlueprintName = "native-fft-testTests"
+               ReferencedContainer = "container:native-fft-test.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "A8525B861BD2A7A6009FCE68"
+            BuildableName = "native-fft-test.app"
+            BlueprintName = "native-fft-test"
+            ReferencedContainer = "container:native-fft-test.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </TestAction>
+   <LaunchAction
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Debug"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "A8525B861BD2A7A6009FCE68"
+            BuildableName = "native-fft-test.app"
+            BlueprintName = "native-fft-test"
+            ReferencedContainer = "container:native-fft-test.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      buildConfiguration = "Release"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "A8525B861BD2A7A6009FCE68"
+            BuildableName = "native-fft-test.app"
+            BlueprintName = "native-fft-test"
+            ReferencedContainer = "container:native-fft-test.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/native-fft-test/native-fft-test.xcodeproj/xcuserdata/cannam.xcuserdatad/xcschemes/xcschememanagement.plist	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>SchemeUserState</key>
+	<dict>
+		<key>native-fft-test.xcscheme</key>
+		<dict>
+			<key>orderHint</key>
+			<integer>0</integer>
+		</dict>
+	</dict>
+	<key>SuppressBuildableAutocreation</key>
+	<dict>
+		<key>A8525B861BD2A7A6009FCE68</key>
+		<dict>
+			<key>primary</key>
+			<true/>
+		</dict>
+		<key>A8525B9F1BD2A7A6009FCE68</key>
+		<dict>
+			<key>primary</key>
+			<true/>
+		</dict>
+	</dict>
+</dict>
+</plist>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/native-fft-test/native-fft-test/AppDelegate.h	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,17 @@
+//
+//  AppDelegate.h
+//  native-fft-test
+//
+//  Created by Chris Cannam on 17/10/2015.
+//  Copyright (c) 2015 Chris Cannam. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface AppDelegate : UIResponder <UIApplicationDelegate>
+
+@property (strong, nonatomic) UIWindow *window;
+
+
+@end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/native-fft-test/native-fft-test/AppDelegate.m	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,45 @@
+//
+//  AppDelegate.m
+//  native-fft-test
+//
+//  Created by Chris Cannam on 17/10/2015.
+//  Copyright (c) 2015 Chris Cannam. All rights reserved.
+//
+
+#import "AppDelegate.h"
+
+@interface AppDelegate ()
+
+@end
+
+@implementation AppDelegate
+
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+    // Override point for customization after application launch.
+    return YES;
+}
+
+- (void)applicationWillResignActive:(UIApplication *)application {
+    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
+    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
+}
+
+- (void)applicationDidEnterBackground:(UIApplication *)application {
+    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
+    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
+}
+
+- (void)applicationWillEnterForeground:(UIApplication *)application {
+    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
+}
+
+- (void)applicationDidBecomeActive:(UIApplication *)application {
+    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
+}
+
+- (void)applicationWillTerminate:(UIApplication *)application {
+    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
+}
+
+@end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/native-fft-test/native-fft-test/Base.lproj/LaunchScreen.xib	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6214" systemVersion="14A314h" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6207"/>
+        <capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB">
+            <rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <subviews>
+                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="  Copyright (c) 2015 Chris Cannam. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
+                    <rect key="frame" x="20" y="439" width="441" height="21"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                    <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="native-fft-test" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
+                    <rect key="frame" x="20" y="140" width="441" height="43"/>
+                    <fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
+                    <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+                    <nil key="highlightedColor"/>
+                </label>
+            </subviews>
+            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+            <constraints>
+                <constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
+                <constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
+                <constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
+                <constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
+                <constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
+                <constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
+            </constraints>
+            <nil key="simulatedStatusBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <point key="canvasLocation" x="548" y="455"/>
+        </view>
+    </objects>
+</document>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/native-fft-test/native-fft-test/Base.lproj/Main.storyboard	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6211" systemVersion="14A298i" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6204"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="tne-QT-ifu">
+            <objects>
+                <viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
+                        <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+            </objects>
+        </scene>
+    </scenes>
+</document>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/native-fft-test/native-fft-test/Images.xcassets/AppIcon.appiconset/Contents.json	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,68 @@
+{
+  "images" : [
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "2x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/native-fft-test/native-fft-test/Info.plist	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>com.breakfastquay.$(PRODUCT_NAME:rfc1034identifier)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UIMainStoryboardFile</key>
+	<string>Main</string>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>armv7</string>
+	</array>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+</dict>
+</plist>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/native-fft-test/native-fft-test/ViewController.h	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,15 @@
+//
+//  ViewController.h
+//  native-fft-test
+//
+//  Created by Chris Cannam on 17/10/2015.
+//  Copyright (c) 2015 Chris Cannam. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+@interface ViewController : UIViewController
+
+
+@end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/native-fft-test/native-fft-test/ViewController.mm	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,82 @@
+//
+//  ViewController.m
+//  native-fft-test
+//
+//  Created by Chris Cannam on 17/10/2015.
+//  Copyright (c) 2015 Chris Cannam. All rights reserved.
+//
+
+#import "ViewController.h"
+
+#include <vector>
+#include <chrono>
+#include <iostream>
+
+#include "bqfft/FFT.h"
+
+using namespace std;
+using namespace breakfastquay;
+
+@interface ViewController ()
+
+@end
+
+@implementation ViewController
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    
+    vector<int> sizes { 512, 2048 };
+    
+    int iterations = 2000;
+    int times = 100;
+    
+    for (auto size: sizes) {
+        
+        FFT fft(size);
+        double total = 0.0;
+        
+        auto start = chrono::high_resolution_clock::now();
+        
+        for (int ti = 0; ti < times; ++ti) {
+            
+            total = 0.0;
+            
+            for (int i = 0; i < iterations; ++i) {
+                
+                vector<double> ri(size), ro(size/2+1), io(size/2+1);
+                for (int j = 0; j < size; ++j) {
+                    ri[j] = (j % 2) / 4.0;
+                }
+                
+                fft.forward(ri.data(), ro.data(), io.data());
+                
+                for (int j = 0; j <= size/2; ++j) {
+                    total += sqrt(ro[j] * ro[j] + io[j] * io[j]);
+                }
+                
+                // synthesise the conjugate half
+                for (int j = 1; j < size/2; ++j) {
+                    total += sqrt(ro[j] * ro[j] + io[j] * io[j]);
+                }
+            }
+        }
+        
+        auto end = chrono::high_resolution_clock::now();
+        
+        double ms = chrono::duration<double, milli>(end - start).count() / times;
+        
+        cerr << "for " << iterations << " * size " << size << ": total = "
+        << total << ", time = " << ms
+        << " ms (" << (iterations / (ms / 1000.0)) << " itr/sec)" << endl;
+    }
+
+    // Do any additional setup after loading the view, typically from a nib.
+}
+
+- (void)didReceiveMemoryWarning {
+    [super didReceiveMemoryWarning];
+    // Dispose of any resources that can be recreated.
+}
+
+@end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/native-fft-test/native-fft-test/main.m	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,16 @@
+//
+//  main.m
+//  native-fft-test
+//
+//  Created by Chris Cannam on 17/10/2015.
+//  Copyright (c) 2015 Chris Cannam. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import "AppDelegate.h"
+
+int main(int argc, char * argv[]) {
+    @autoreleasepool {
+        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/native-fft-test/native-fft-testTests/Info.plist	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>com.breakfastquay.$(PRODUCT_NAME:rfc1034identifier)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>BNDL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/native-fft-test/native-fft-testTests/native_fft_testTests.m	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,40 @@
+//
+//  native_fft_testTests.m
+//  native-fft-testTests
+//
+//  Created by Chris Cannam on 17/10/2015.
+//  Copyright (c) 2015 Chris Cannam. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import <XCTest/XCTest.h>
+
+@interface native_fft_testTests : XCTestCase
+
+@end
+
+@implementation native_fft_testTests
+
+- (void)setUp {
+    [super setUp];
+    // Put setup code here. This method is called before the invocation of each test method in the class.
+}
+
+- (void)tearDown {
+    // Put teardown code here. This method is called after the invocation of each test method in the class.
+    [super tearDown];
+}
+
+- (void)testExample {
+    // This is an example of a functional test case.
+    XCTAssert(YES, @"Pass");
+}
+
+- (void)testPerformanceExample {
+    // This is an example of a performance test case.
+    [self measureBlock:^{
+        // Put the code you want to measure the time of here.
+    }];
+}
+
+@end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/native/native.cpp	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,59 @@
+
+#include <bqfft/FFT.h>
+
+#include <vector>
+#include <iostream>
+#include <cmath>
+#include <chrono>
+
+using namespace std;
+using namespace breakfastquay;
+
+int main(int argc, char **argv)
+{
+    vector<int> sizes { 512, 2048 };
+
+    int iterations = 2000;
+    int times = 100;
+
+    for (auto size: sizes) {
+
+	FFT fft(size);
+	double total = 0.0;
+
+	auto start = chrono::high_resolution_clock::now();
+
+	for (int ti = 0; ti < times; ++ti) {
+	    
+	    total = 0.0;
+
+	    for (int i = 0; i < iterations; ++i) {
+
+		vector<double> ri(size), ro(size/2+1), io(size/2+1);
+		for (int j = 0; j < size; ++j) {
+		    ri[j] = (j % 2) / 4.0;
+		}
+
+		fft.forward(ri.data(), ro.data(), io.data());
+
+		for (int j = 0; j <= size/2; ++j) {
+		    total += sqrt(ro[j] * ro[j] + io[j] * io[j]);
+		}
+
+		// synthesise the conjugate half
+		for (int j = 1; j < size/2; ++j) {
+		    total += sqrt(ro[j] * ro[j] + io[j] * io[j]);
+		}
+	    }
+	}
+
+	auto end = chrono::high_resolution_clock::now();
+
+	double ms = chrono::duration<double, milli>(end - start).count() / times;
+	
+	cerr << "for " << iterations << " * size " << size << ": total = "
+	     << total << ", time = " << ms
+	     << " ms (" << (iterations / (ms / 1000.0)) << " itr/sec)" << endl;
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/nayukic/FFT.js	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,74 @@
+"use strict";
+
+var nayukiCModule = NayukiCModule({});
+
+var nc_precalc = nayukiCModule.cwrap(
+    'precalc', 'number', ['number']
+);
+
+var nc_dispose = nayukiCModule.cwrap(
+    'dispose', 'void', ['number']
+);
+
+var nc_transform_radix2_precalc = nayukiCModule.cwrap(
+    'transform_radix2_precalc', 'void', ['number', 'number', 'number', 'number']
+);
+
+var nc_precalc_f = nayukiCModule.cwrap(
+    'precalc_f', 'number', ['number']
+);
+
+var nc_dispose_f = nayukiCModule.cwrap(
+    'dispose_f', 'void', ['number']
+);
+
+var nc_transform_radix2_precalc_f = nayukiCModule.cwrap(
+    'transform_radix2_precalc_f', 'void', ['number', 'number', 'number', 'number']
+);
+
+function FFTNayukiC(n) {
+
+    this.n = n;
+    this.rptr = nayukiCModule._malloc(n*8 + n*8);
+    this.iptr = this.rptr + n*8;
+    this.rarr = new Float64Array(nayukiCModule.HEAPU8.buffer, this.rptr, n);
+    this.iarr = new Float64Array(nayukiCModule.HEAPU8.buffer, this.iptr, n);
+    this.tables = nc_precalc(n);
+
+    this.forward = function(real, imag) {
+	this.rarr.set(real);
+	this.iarr.set(imag);
+	nc_transform_radix2_precalc(this.rptr, this.iptr, this.n, this.tables);
+	real.set(this.rarr);
+	imag.set(this.iarr);
+    };
+
+    this.dispose = function() {
+	nayukiCModule._free(this.rptr);
+	nc_dispose(this.tables);
+    }
+}
+
+function FFTNayukiCFloat(n) {
+
+    this.n = n;
+    this.rptr = nayukiCModule._malloc(n*4 + n*4);
+    this.iptr = this.rptr + n*4;
+    this.rarr = new Float32Array(nayukiCModule.HEAPU8.buffer, this.rptr, n);
+    this.iarr = new Float32Array(nayukiCModule.HEAPU8.buffer, this.iptr, n);
+    this.tables = nc_precalc_f(n);
+
+    this.forward = function(real, imag) {
+	this.rarr.set(real);
+	this.iarr.set(imag);
+	nc_transform_radix2_precalc_f(this.rptr, this.iptr, this.n, this.tables);
+	real.set(this.rarr);
+	imag.set(this.iarr);
+    };
+
+    this.dispose = function() {
+	nayukiCModule._free(this.rptr);
+	nc_dispose_f(this.tables);
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/nayukic/Makefile.emscripten	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,15 @@
+
+NayukiCFFT.js:	fft.c fft.h
+	emcc -O3 -I. \
+	     --memory-init-file 0 \
+	     -s NO_FILESYSTEM=1 \
+	     -s NO_BROWSER=1 \
+	     -s MODULARIZE=1 \
+	     -s EXPORT_NAME="'NayukiCModule'" \
+	     -s EXPORTED_FUNCTIONS="['_transform_radix2_precalc','_precalc','_dispose','_transform_radix2_precalc_f','_precalc_f','_dispose_f']" \
+	     -o NayukiCFFT.js \
+	     fft.c
+
+clean:
+	rm -f NayukiCFFT.js
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/nayukic/NayukiCFFT.js	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,22 @@
+var NayukiCModule = function(Module) {
+  Module = Module || {};
+
+var Module;if(!Module)Module=(typeof NayukiCModule!=="undefined"?NayukiCModule:null)||{};var moduleOverrides={};for(var key in Module){if(Module.hasOwnProperty(key)){moduleOverrides[key]=Module[key]}}var ENVIRONMENT_IS_WEB=typeof window==="object";var ENVIRONMENT_IS_WORKER=typeof importScripts==="function";var ENVIRONMENT_IS_NODE=typeof process==="object"&&typeof require==="function"&&!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_WORKER;var ENVIRONMENT_IS_SHELL=!ENVIRONMENT_IS_WEB&&!ENVIRONMENT_IS_NODE&&!ENVIRONMENT_IS_WORKER;if(ENVIRONMENT_IS_NODE){if(!Module["print"])Module["print"]=function print(x){process["stdout"].write(x+"\n")};if(!Module["printErr"])Module["printErr"]=function printErr(x){process["stderr"].write(x+"\n")};var nodeFS=require("fs");var nodePath=require("path");Module["read"]=function read(filename,binary){filename=nodePath["normalize"](filename);var ret=nodeFS["readFileSync"](filename);if(!ret&&filename!=nodePath["resolve"](filename)){filename=path.join(__dirname,"..","src",filename);ret=nodeFS["readFileSync"](filename)}if(ret&&!binary)ret=ret.toString();return ret};Module["readBinary"]=function readBinary(filename){var ret=Module["read"](filename,true);if(!ret.buffer){ret=new Uint8Array(ret)}assert(ret.buffer);return ret};Module["load"]=function load(f){globalEval(read(f))};if(!Module["thisProgram"]){if(process["argv"].length>1){Module["thisProgram"]=process["argv"][1].replace(/\\/g,"/")}else{Module["thisProgram"]="unknown-program"}}Module["arguments"]=process["argv"].slice(2);if(typeof module!=="undefined"){module["exports"]=Module}process["on"]("uncaughtException",(function(ex){if(!(ex instanceof ExitStatus)){throw ex}}));Module["inspect"]=(function(){return"[Emscripten Module object]"})}else if(ENVIRONMENT_IS_SHELL){if(!Module["print"])Module["print"]=print;if(typeof printErr!="undefined")Module["printErr"]=printErr;if(typeof read!="undefined"){Module["read"]=read}else{Module["read"]=function read(){throw"no read() available (jsc?)"}}Module["readBinary"]=function readBinary(f){if(typeof readbuffer==="function"){return new Uint8Array(readbuffer(f))}var data=read(f,"binary");assert(typeof data==="object");return data};if(typeof scriptArgs!="undefined"){Module["arguments"]=scriptArgs}else if(typeof arguments!="undefined"){Module["arguments"]=arguments}}else if(ENVIRONMENT_IS_WEB||ENVIRONMENT_IS_WORKER){Module["read"]=function read(url){var xhr=new XMLHttpRequest;xhr.open("GET",url,false);xhr.send(null);return xhr.responseText};if(typeof arguments!="undefined"){Module["arguments"]=arguments}if(typeof console!=="undefined"){if(!Module["print"])Module["print"]=function print(x){console.log(x)};if(!Module["printErr"])Module["printErr"]=function printErr(x){console.log(x)}}else{var TRY_USE_DUMP=false;if(!Module["print"])Module["print"]=TRY_USE_DUMP&&typeof dump!=="undefined"?(function(x){dump(x)}):(function(x){})}if(ENVIRONMENT_IS_WORKER){Module["load"]=importScripts}if(typeof Module["setWindowTitle"]==="undefined"){Module["setWindowTitle"]=(function(title){document.title=title})}}else{throw"Unknown runtime environment. Where are we?"}function globalEval(x){eval.call(null,x)}if(!Module["load"]&&Module["read"]){Module["load"]=function load(f){globalEval(Module["read"](f))}}if(!Module["print"]){Module["print"]=(function(){})}if(!Module["printErr"]){Module["printErr"]=Module["print"]}if(!Module["arguments"]){Module["arguments"]=[]}if(!Module["thisProgram"]){Module["thisProgram"]="./this.program"}Module.print=Module["print"];Module.printErr=Module["printErr"];Module["preRun"]=[];Module["postRun"]=[];for(var key in moduleOverrides){if(moduleOverrides.hasOwnProperty(key)){Module[key]=moduleOverrides[key]}}var Runtime={setTempRet0:(function(value){tempRet0=value}),getTempRet0:(function(){return tempRet0}),stackSave:(function(){return STACKTOP}),stackRestore:(function(stackTop){STACKTOP=stackTop}),getNativeTypeSize:(function(type){switch(type){case"i1":case"i8":return 1;case"i16":return 2;case"i32":return 4;case"i64":return 8;case"float":return 4;case"double":return 8;default:{if(type[type.length-1]==="*"){return Runtime.QUANTUM_SIZE}else if(type[0]==="i"){var bits=parseInt(type.substr(1));assert(bits%8===0);return bits/8}else{return 0}}}}),getNativeFieldSize:(function(type){return Math.max(Runtime.getNativeTypeSize(type),Runtime.QUANTUM_SIZE)}),STACK_ALIGN:16,prepVararg:(function(ptr,type){if(type==="double"||type==="i64"){if(ptr&7){assert((ptr&7)===4);ptr+=4}}else{assert((ptr&3)===0)}return ptr}),getAlignSize:(function(type,size,vararg){if(!vararg&&(type=="i64"||type=="double"))return 8;if(!type)return Math.min(size,8);return Math.min(size||(type?Runtime.getNativeFieldSize(type):0),Runtime.QUANTUM_SIZE)}),dynCall:(function(sig,ptr,args){if(args&&args.length){if(!args.splice)args=Array.prototype.slice.call(args);args.splice(0,0,ptr);return Module["dynCall_"+sig].apply(null,args)}else{return Module["dynCall_"+sig].call(null,ptr)}}),functionPointers:[],addFunction:(function(func){for(var i=0;i<Runtime.functionPointers.length;i++){if(!Runtime.functionPointers[i]){Runtime.functionPointers[i]=func;return 2*(1+i)}}throw"Finished up all reserved function pointers. Use a higher value for RESERVED_FUNCTION_POINTERS."}),removeFunction:(function(index){Runtime.functionPointers[(index-2)/2]=null}),warnOnce:(function(text){if(!Runtime.warnOnce.shown)Runtime.warnOnce.shown={};if(!Runtime.warnOnce.shown[text]){Runtime.warnOnce.shown[text]=1;Module.printErr(text)}}),funcWrappers:{},getFuncWrapper:(function(func,sig){assert(sig);if(!Runtime.funcWrappers[sig]){Runtime.funcWrappers[sig]={}}var sigCache=Runtime.funcWrappers[sig];if(!sigCache[func]){sigCache[func]=function dynCall_wrapper(){return Runtime.dynCall(sig,func,arguments)}}return sigCache[func]}),getCompilerSetting:(function(name){throw"You must build with -s RETAIN_COMPILER_SETTINGS=1 for Runtime.getCompilerSetting or emscripten_get_compiler_setting to work"}),stackAlloc:(function(size){var ret=STACKTOP;STACKTOP=STACKTOP+size|0;STACKTOP=STACKTOP+15&-16;return ret}),staticAlloc:(function(size){var ret=STATICTOP;STATICTOP=STATICTOP+size|0;STATICTOP=STATICTOP+15&-16;return ret}),dynamicAlloc:(function(size){var ret=DYNAMICTOP;DYNAMICTOP=DYNAMICTOP+size|0;DYNAMICTOP=DYNAMICTOP+15&-16;if(DYNAMICTOP>=TOTAL_MEMORY){var success=enlargeMemory();if(!success){DYNAMICTOP=ret;return 0}}return ret}),alignMemory:(function(size,quantum){var ret=size=Math.ceil(size/(quantum?quantum:16))*(quantum?quantum:16);return ret}),makeBigInt:(function(low,high,unsigned){var ret=unsigned?+(low>>>0)+ +(high>>>0)*+4294967296:+(low>>>0)+ +(high|0)*+4294967296;return ret}),GLOBAL_BASE:8,QUANTUM_SIZE:4,__dummy__:0};Module["Runtime"]=Runtime;var __THREW__=0;var ABORT=false;var EXITSTATUS=0;var undef=0;var tempValue,tempInt,tempBigInt,tempInt2,tempBigInt2,tempPair,tempBigIntI,tempBigIntR,tempBigIntS,tempBigIntP,tempBigIntD,tempDouble,tempFloat;var tempI64,tempI64b;var tempRet0,tempRet1,tempRet2,tempRet3,tempRet4,tempRet5,tempRet6,tempRet7,tempRet8,tempRet9;function assert(condition,text){if(!condition){abort("Assertion failed: "+text)}}var globalScope=this;function getCFunc(ident){var func=Module["_"+ident];if(!func){try{func=eval("_"+ident)}catch(e){}}assert(func,"Cannot call unknown function "+ident+" (perhaps LLVM optimizations or closure removed it?)");return func}var cwrap,ccall;((function(){var JSfuncs={"stackSave":(function(){Runtime.stackSave()}),"stackRestore":(function(){Runtime.stackRestore()}),"arrayToC":(function(arr){var ret=Runtime.stackAlloc(arr.length);writeArrayToMemory(arr,ret);return ret}),"stringToC":(function(str){var ret=0;if(str!==null&&str!==undefined&&str!==0){ret=Runtime.stackAlloc((str.length<<2)+1);writeStringToMemory(str,ret)}return ret})};var toC={"string":JSfuncs["stringToC"],"array":JSfuncs["arrayToC"]};ccall=function ccallFunc(ident,returnType,argTypes,args,opts){var func=getCFunc(ident);var cArgs=[];var stack=0;if(args){for(var i=0;i<args.length;i++){var converter=toC[argTypes[i]];if(converter){if(stack===0)stack=Runtime.stackSave();cArgs[i]=converter(args[i])}else{cArgs[i]=args[i]}}}var ret=func.apply(null,cArgs);if(returnType==="string")ret=Pointer_stringify(ret);if(stack!==0){if(opts&&opts.async){EmterpreterAsync.asyncFinalizers.push((function(){Runtime.stackRestore(stack)}));return}Runtime.stackRestore(stack)}return ret};var sourceRegex=/^function\s*\(([^)]*)\)\s*{\s*([^*]*?)[\s;]*(?:return\s*(.*?)[;\s]*)?}$/;function parseJSFunc(jsfunc){var parsed=jsfunc.toString().match(sourceRegex).slice(1);return{arguments:parsed[0],body:parsed[1],returnValue:parsed[2]}}var JSsource={};for(var fun in JSfuncs){if(JSfuncs.hasOwnProperty(fun)){JSsource[fun]=parseJSFunc(JSfuncs[fun])}}cwrap=function cwrap(ident,returnType,argTypes){argTypes=argTypes||[];var cfunc=getCFunc(ident);var numericArgs=argTypes.every((function(type){return type==="number"}));var numericRet=returnType!=="string";if(numericRet&&numericArgs){return cfunc}var argNames=argTypes.map((function(x,i){return"$"+i}));var funcstr="(function("+argNames.join(",")+") {";var nargs=argTypes.length;if(!numericArgs){funcstr+="var stack = "+JSsource["stackSave"].body+";";for(var i=0;i<nargs;i++){var arg=argNames[i],type=argTypes[i];if(type==="number")continue;var convertCode=JSsource[type+"ToC"];funcstr+="var "+convertCode.arguments+" = "+arg+";";funcstr+=convertCode.body+";";funcstr+=arg+"="+convertCode.returnValue+";"}}var cfuncname=parseJSFunc((function(){return cfunc})).returnValue;funcstr+="var ret = "+cfuncname+"("+argNames.join(",")+");";if(!numericRet){var strgfy=parseJSFunc((function(){return Pointer_stringify})).returnValue;funcstr+="ret = "+strgfy+"(ret);"}if(!numericArgs){funcstr+=JSsource["stackRestore"].body.replace("()","(stack)")+";"}funcstr+="return ret})";return eval(funcstr)}}))();Module["ccall"]=ccall;Module["cwrap"]=cwrap;function setValue(ptr,value,type,noSafe){type=type||"i8";if(type.charAt(type.length-1)==="*")type="i32";switch(type){case"i1":HEAP8[ptr>>0]=value;break;case"i8":HEAP8[ptr>>0]=value;break;case"i16":HEAP16[ptr>>1]=value;break;case"i32":HEAP32[ptr>>2]=value;break;case"i64":tempI64=[value>>>0,(tempDouble=value,+Math_abs(tempDouble)>=+1?tempDouble>+0?(Math_min(+Math_floor(tempDouble/+4294967296),+4294967295)|0)>>>0:~~+Math_ceil((tempDouble- +(~~tempDouble>>>0))/+4294967296)>>>0:0)],HEAP32[ptr>>2]=tempI64[0],HEAP32[ptr+4>>2]=tempI64[1];break;case"float":HEAPF32[ptr>>2]=value;break;case"double":HEAPF64[ptr>>3]=value;break;default:abort("invalid type for setValue: "+type)}}Module["setValue"]=setValue;function getValue(ptr,type,noSafe){type=type||"i8";if(type.charAt(type.length-1)==="*")type="i32";switch(type){case"i1":return HEAP8[ptr>>0];case"i8":return HEAP8[ptr>>0];case"i16":return HEAP16[ptr>>1];case"i32":return HEAP32[ptr>>2];case"i64":return HEAP32[ptr>>2];case"float":return HEAPF32[ptr>>2];case"double":return HEAPF64[ptr>>3];default:abort("invalid type for setValue: "+type)}return null}Module["getValue"]=getValue;var ALLOC_NORMAL=0;var ALLOC_STACK=1;var ALLOC_STATIC=2;var ALLOC_DYNAMIC=3;var ALLOC_NONE=4;Module["ALLOC_NORMAL"]=ALLOC_NORMAL;Module["ALLOC_STACK"]=ALLOC_STACK;Module["ALLOC_STATIC"]=ALLOC_STATIC;Module["ALLOC_DYNAMIC"]=ALLOC_DYNAMIC;Module["ALLOC_NONE"]=ALLOC_NONE;function allocate(slab,types,allocator,ptr){var zeroinit,size;if(typeof slab==="number"){zeroinit=true;size=slab}else{zeroinit=false;size=slab.length}var singleType=typeof types==="string"?types:null;var ret;if(allocator==ALLOC_NONE){ret=ptr}else{ret=[_malloc,Runtime.stackAlloc,Runtime.staticAlloc,Runtime.dynamicAlloc][allocator===undefined?ALLOC_STATIC:allocator](Math.max(size,singleType?1:types.length))}if(zeroinit){var ptr=ret,stop;assert((ret&3)==0);stop=ret+(size&~3);for(;ptr<stop;ptr+=4){HEAP32[ptr>>2]=0}stop=ret+size;while(ptr<stop){HEAP8[ptr++>>0]=0}return ret}if(singleType==="i8"){if(slab.subarray||slab.slice){HEAPU8.set(slab,ret)}else{HEAPU8.set(new Uint8Array(slab),ret)}return ret}var i=0,type,typeSize,previousType;while(i<size){var curr=slab[i];if(typeof curr==="function"){curr=Runtime.getFunctionIndex(curr)}type=singleType||types[i];if(type===0){i++;continue}if(type=="i64")type="i32";setValue(ret+i,curr,type);if(previousType!==type){typeSize=Runtime.getNativeTypeSize(type);previousType=type}i+=typeSize}return ret}Module["allocate"]=allocate;function getMemory(size){if(!staticSealed)return Runtime.staticAlloc(size);if(typeof _sbrk!=="undefined"&&!_sbrk.called||!runtimeInitialized)return Runtime.dynamicAlloc(size);return _malloc(size)}Module["getMemory"]=getMemory;function Pointer_stringify(ptr,length){if(length===0||!ptr)return"";var hasUtf=0;var t;var i=0;while(1){t=HEAPU8[ptr+i>>0];hasUtf|=t;if(t==0&&!length)break;i++;if(length&&i==length)break}if(!length)length=i;var ret="";if(hasUtf<128){var MAX_CHUNK=1024;var curr;while(length>0){curr=String.fromCharCode.apply(String,HEAPU8.subarray(ptr,ptr+Math.min(length,MAX_CHUNK)));ret=ret?ret+curr:curr;ptr+=MAX_CHUNK;length-=MAX_CHUNK}return ret}return Module["UTF8ToString"](ptr)}Module["Pointer_stringify"]=Pointer_stringify;function AsciiToString(ptr){var str="";while(1){var ch=HEAP8[ptr++>>0];if(!ch)return str;str+=String.fromCharCode(ch)}}Module["AsciiToString"]=AsciiToString;function stringToAscii(str,outPtr){return writeAsciiToMemory(str,outPtr,false)}Module["stringToAscii"]=stringToAscii;function UTF8ArrayToString(u8Array,idx){var u0,u1,u2,u3,u4,u5;var str="";while(1){u0=u8Array[idx++];if(!u0)return str;if(!(u0&128)){str+=String.fromCharCode(u0);continue}u1=u8Array[idx++]&63;if((u0&224)==192){str+=String.fromCharCode((u0&31)<<6|u1);continue}u2=u8Array[idx++]&63;if((u0&240)==224){u0=(u0&15)<<12|u1<<6|u2}else{u3=u8Array[idx++]&63;if((u0&248)==240){u0=(u0&7)<<18|u1<<12|u2<<6|u3}else{u4=u8Array[idx++]&63;if((u0&252)==248){u0=(u0&3)<<24|u1<<18|u2<<12|u3<<6|u4}else{u5=u8Array[idx++]&63;u0=(u0&1)<<30|u1<<24|u2<<18|u3<<12|u4<<6|u5}}}if(u0<65536){str+=String.fromCharCode(u0)}else{var ch=u0-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}}}Module["UTF8ArrayToString"]=UTF8ArrayToString;function UTF8ToString(ptr){return UTF8ArrayToString(HEAPU8,ptr)}Module["UTF8ToString"]=UTF8ToString;function stringToUTF8Array(str,outU8Array,outIdx,maxBytesToWrite){if(!(maxBytesToWrite>0))return 0;var startIdx=outIdx;var endIdx=outIdx+maxBytesToWrite-1;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127){if(outIdx>=endIdx)break;outU8Array[outIdx++]=u}else if(u<=2047){if(outIdx+1>=endIdx)break;outU8Array[outIdx++]=192|u>>6;outU8Array[outIdx++]=128|u&63}else if(u<=65535){if(outIdx+2>=endIdx)break;outU8Array[outIdx++]=224|u>>12;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else if(u<=2097151){if(outIdx+3>=endIdx)break;outU8Array[outIdx++]=240|u>>18;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else if(u<=67108863){if(outIdx+4>=endIdx)break;outU8Array[outIdx++]=248|u>>24;outU8Array[outIdx++]=128|u>>18&63;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}else{if(outIdx+5>=endIdx)break;outU8Array[outIdx++]=252|u>>30;outU8Array[outIdx++]=128|u>>24&63;outU8Array[outIdx++]=128|u>>18&63;outU8Array[outIdx++]=128|u>>12&63;outU8Array[outIdx++]=128|u>>6&63;outU8Array[outIdx++]=128|u&63}}outU8Array[outIdx]=0;return outIdx-startIdx}Module["stringToUTF8Array"]=stringToUTF8Array;function stringToUTF8(str,outPtr,maxBytesToWrite){return stringToUTF8Array(str,HEAPU8,outPtr,maxBytesToWrite)}Module["stringToUTF8"]=stringToUTF8;function lengthBytesUTF8(str){var len=0;for(var i=0;i<str.length;++i){var u=str.charCodeAt(i);if(u>=55296&&u<=57343)u=65536+((u&1023)<<10)|str.charCodeAt(++i)&1023;if(u<=127){++len}else if(u<=2047){len+=2}else if(u<=65535){len+=3}else if(u<=2097151){len+=4}else if(u<=67108863){len+=5}else{len+=6}}return len}Module["lengthBytesUTF8"]=lengthBytesUTF8;function UTF16ToString(ptr){var i=0;var str="";while(1){var codeUnit=HEAP16[ptr+i*2>>1];if(codeUnit==0)return str;++i;str+=String.fromCharCode(codeUnit)}}Module["UTF16ToString"]=UTF16ToString;function stringToUTF16(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<2)return 0;maxBytesToWrite-=2;var startPtr=outPtr;var numCharsToWrite=maxBytesToWrite<str.length*2?maxBytesToWrite/2:str.length;for(var i=0;i<numCharsToWrite;++i){var codeUnit=str.charCodeAt(i);HEAP16[outPtr>>1]=codeUnit;outPtr+=2}HEAP16[outPtr>>1]=0;return outPtr-startPtr}Module["stringToUTF16"]=stringToUTF16;function lengthBytesUTF16(str){return str.length*2}Module["lengthBytesUTF16"]=lengthBytesUTF16;function UTF32ToString(ptr){var i=0;var str="";while(1){var utf32=HEAP32[ptr+i*4>>2];if(utf32==0)return str;++i;if(utf32>=65536){var ch=utf32-65536;str+=String.fromCharCode(55296|ch>>10,56320|ch&1023)}else{str+=String.fromCharCode(utf32)}}}Module["UTF32ToString"]=UTF32ToString;function stringToUTF32(str,outPtr,maxBytesToWrite){if(maxBytesToWrite===undefined){maxBytesToWrite=2147483647}if(maxBytesToWrite<4)return 0;var startPtr=outPtr;var endPtr=startPtr+maxBytesToWrite-4;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343){var trailSurrogate=str.charCodeAt(++i);codeUnit=65536+((codeUnit&1023)<<10)|trailSurrogate&1023}HEAP32[outPtr>>2]=codeUnit;outPtr+=4;if(outPtr+4>endPtr)break}HEAP32[outPtr>>2]=0;return outPtr-startPtr}Module["stringToUTF32"]=stringToUTF32;function lengthBytesUTF32(str){var len=0;for(var i=0;i<str.length;++i){var codeUnit=str.charCodeAt(i);if(codeUnit>=55296&&codeUnit<=57343)++i;len+=4}return len}Module["lengthBytesUTF32"]=lengthBytesUTF32;function demangle(func){var hasLibcxxabi=!!Module["___cxa_demangle"];if(hasLibcxxabi){try{var buf=_malloc(func.length);writeStringToMemory(func.substr(1),buf);var status=_malloc(4);var ret=Module["___cxa_demangle"](buf,0,0,status);if(getValue(status,"i32")===0&&ret){return Pointer_stringify(ret)}}catch(e){}finally{if(buf)_free(buf);if(status)_free(status);if(ret)_free(ret)}}var i=3;var basicTypes={"v":"void","b":"bool","c":"char","s":"short","i":"int","l":"long","f":"float","d":"double","w":"wchar_t","a":"signed char","h":"unsigned char","t":"unsigned short","j":"unsigned int","m":"unsigned long","x":"long long","y":"unsigned long long","z":"..."};var subs=[];var first=true;function dump(x){if(x)Module.print(x);Module.print(func);var pre="";for(var a=0;a<i;a++)pre+=" ";Module.print(pre+"^")}function parseNested(){i++;if(func[i]==="K")i++;var parts=[];while(func[i]!=="E"){if(func[i]==="S"){i++;var next=func.indexOf("_",i);var num=func.substring(i,next)||0;parts.push(subs[num]||"?");i=next+1;continue}if(func[i]==="C"){parts.push(parts[parts.length-1]);i+=2;continue}var size=parseInt(func.substr(i));var pre=size.toString().length;if(!size||!pre){i--;break}var curr=func.substr(i+pre,size);parts.push(curr);subs.push(curr);i+=pre+size}i++;return parts}function parse(rawList,limit,allowVoid){limit=limit||Infinity;var ret="",list=[];function flushList(){return"("+list.join(", ")+")"}var name;if(func[i]==="N"){name=parseNested().join("::");limit--;if(limit===0)return rawList?[name]:name}else{if(func[i]==="K"||first&&func[i]==="L")i++;var size=parseInt(func.substr(i));if(size){var pre=size.toString().length;name=func.substr(i+pre,size);i+=pre+size}}first=false;if(func[i]==="I"){i++;var iList=parse(true);var iRet=parse(true,1,true);ret+=iRet[0]+" "+name+"<"+iList.join(", ")+">"}else{ret=name}paramLoop:while(i<func.length&&limit-->0){var c=func[i++];if(c in basicTypes){list.push(basicTypes[c])}else{switch(c){case"P":list.push(parse(true,1,true)[0]+"*");break;case"R":list.push(parse(true,1,true)[0]+"&");break;case"L":{i++;var end=func.indexOf("E",i);var size=end-i;list.push(func.substr(i,size));i+=size+2;break};case"A":{var size=parseInt(func.substr(i));i+=size.toString().length;if(func[i]!=="_")throw"?";i++;list.push(parse(true,1,true)[0]+" ["+size+"]");break};case"E":break paramLoop;default:ret+="?"+c;break paramLoop}}}if(!allowVoid&&list.length===1&&list[0]==="void")list=[];if(rawList){if(ret){list.push(ret+"?")}return list}else{return ret+flushList()}}var parsed=func;try{if(func=="Object._main"||func=="_main"){return"main()"}if(typeof func==="number")func=Pointer_stringify(func);if(func[0]!=="_")return func;if(func[1]!=="_")return func;if(func[2]!=="Z")return func;switch(func[3]){case"n":return"operator new()";case"d":return"operator delete()"}parsed=parse()}catch(e){parsed+="?"}if(parsed.indexOf("?")>=0&&!hasLibcxxabi){Runtime.warnOnce("warning: a problem occurred in builtin C++ name demangling; build with  -s DEMANGLE_SUPPORT=1  to link in libcxxabi demangling")}return parsed}function demangleAll(text){return text.replace(/__Z[\w\d_]+/g,(function(x){var y=demangle(x);return x===y?x:x+" ["+y+"]"}))}function jsStackTrace(){var err=new Error;if(!err.stack){try{throw new Error(0)}catch(e){err=e}if(!err.stack){return"(no stack trace available)"}}return err.stack.toString()}function stackTrace(){return demangleAll(jsStackTrace())}Module["stackTrace"]=stackTrace;var PAGE_SIZE=4096;function alignMemoryPage(x){if(x%4096>0){x+=4096-x%4096}return x}var HEAP;var HEAP8,HEAPU8,HEAP16,HEAPU16,HEAP32,HEAPU32,HEAPF32,HEAPF64;var STATIC_BASE=0,STATICTOP=0,staticSealed=false;var STACK_BASE=0,STACKTOP=0,STACK_MAX=0;var DYNAMIC_BASE=0,DYNAMICTOP=0;function abortOnCannotGrowMemory(){abort("Cannot enlarge memory arrays. Either (1) compile with  -s TOTAL_MEMORY=X  with X higher than the current value "+TOTAL_MEMORY+", (2) compile with  -s ALLOW_MEMORY_GROWTH=1  which adjusts the size at runtime but prevents some optimizations, (3) set Module.TOTAL_MEMORY to a higher value before the program runs, or if you want malloc to return NULL (0) instead of this abort, compile with  -s ABORTING_MALLOC=0 ")}function enlargeMemory(){abortOnCannotGrowMemory()}var TOTAL_STACK=Module["TOTAL_STACK"]||5242880;var TOTAL_MEMORY=Module["TOTAL_MEMORY"]||16777216;var totalMemory=64*1024;while(totalMemory<TOTAL_MEMORY||totalMemory<2*TOTAL_STACK){if(totalMemory<16*1024*1024){totalMemory*=2}else{totalMemory+=16*1024*1024}}if(totalMemory!==TOTAL_MEMORY){TOTAL_MEMORY=totalMemory}assert(typeof Int32Array!=="undefined"&&typeof Float64Array!=="undefined"&&!!(new Int32Array(1))["subarray"]&&!!(new Int32Array(1))["set"],"JS engine does not provide full typed array support");var buffer;buffer=new ArrayBuffer(TOTAL_MEMORY);HEAP8=new Int8Array(buffer);HEAP16=new Int16Array(buffer);HEAP32=new Int32Array(buffer);HEAPU8=new Uint8Array(buffer);HEAPU16=new Uint16Array(buffer);HEAPU32=new Uint32Array(buffer);HEAPF32=new Float32Array(buffer);HEAPF64=new Float64Array(buffer);HEAP32[0]=255;assert(HEAPU8[0]===255&&HEAPU8[3]===0,"Typed arrays 2 must be run on a little-endian system");Module["HEAP"]=HEAP;Module["buffer"]=buffer;Module["HEAP8"]=HEAP8;Module["HEAP16"]=HEAP16;Module["HEAP32"]=HEAP32;Module["HEAPU8"]=HEAPU8;Module["HEAPU16"]=HEAPU16;Module["HEAPU32"]=HEAPU32;Module["HEAPF32"]=HEAPF32;Module["HEAPF64"]=HEAPF64;function callRuntimeCallbacks(callbacks){while(callbacks.length>0){var callback=callbacks.shift();if(typeof callback=="function"){callback();continue}var func=callback.func;if(typeof func==="number"){if(callback.arg===undefined){Runtime.dynCall("v",func)}else{Runtime.dynCall("vi",func,[callback.arg])}}else{func(callback.arg===undefined?null:callback.arg)}}}var __ATPRERUN__=[];var __ATINIT__=[];var __ATMAIN__=[];var __ATEXIT__=[];var __ATPOSTRUN__=[];var runtimeInitialized=false;var runtimeExited=false;function preRun(){if(Module["preRun"]){if(typeof Module["preRun"]=="function")Module["preRun"]=[Module["preRun"]];while(Module["preRun"].length){addOnPreRun(Module["preRun"].shift())}}callRuntimeCallbacks(__ATPRERUN__)}function ensureInitRuntime(){if(runtimeInitialized)return;runtimeInitialized=true;callRuntimeCallbacks(__ATINIT__)}function preMain(){callRuntimeCallbacks(__ATMAIN__)}function exitRuntime(){callRuntimeCallbacks(__ATEXIT__);runtimeExited=true}function postRun(){if(Module["postRun"]){if(typeof Module["postRun"]=="function")Module["postRun"]=[Module["postRun"]];while(Module["postRun"].length){addOnPostRun(Module["postRun"].shift())}}callRuntimeCallbacks(__ATPOSTRUN__)}function addOnPreRun(cb){__ATPRERUN__.unshift(cb)}Module["addOnPreRun"]=addOnPreRun;function addOnInit(cb){__ATINIT__.unshift(cb)}Module["addOnInit"]=addOnInit;function addOnPreMain(cb){__ATMAIN__.unshift(cb)}Module["addOnPreMain"]=addOnPreMain;function addOnExit(cb){__ATEXIT__.unshift(cb)}Module["addOnExit"]=addOnExit;function addOnPostRun(cb){__ATPOSTRUN__.unshift(cb)}Module["addOnPostRun"]=addOnPostRun;function intArrayFromString(stringy,dontAddNull,length){var len=length>0?length:lengthBytesUTF8(stringy)+1;var u8array=new Array(len);var numBytesWritten=stringToUTF8Array(stringy,u8array,0,u8array.length);if(dontAddNull)u8array.length=numBytesWritten;return u8array}Module["intArrayFromString"]=intArrayFromString;function intArrayToString(array){var ret=[];for(var i=0;i<array.length;i++){var chr=array[i];if(chr>255){chr&=255}ret.push(String.fromCharCode(chr))}return ret.join("")}Module["intArrayToString"]=intArrayToString;function writeStringToMemory(string,buffer,dontAddNull){var array=intArrayFromString(string,dontAddNull);var i=0;while(i<array.length){var chr=array[i];HEAP8[buffer+i>>0]=chr;i=i+1}}Module["writeStringToMemory"]=writeStringToMemory;function writeArrayToMemory(array,buffer){for(var i=0;i<array.length;i++){HEAP8[buffer++>>0]=array[i]}}Module["writeArrayToMemory"]=writeArrayToMemory;function writeAsciiToMemory(str,buffer,dontAddNull){for(var i=0;i<str.length;++i){HEAP8[buffer++>>0]=str.charCodeAt(i)}if(!dontAddNull)HEAP8[buffer>>0]=0}Module["writeAsciiToMemory"]=writeAsciiToMemory;function unSign(value,bits,ignore){if(value>=0){return value}return bits<=32?2*Math.abs(1<<bits-1)+value:Math.pow(2,bits)+value}function reSign(value,bits,ignore){if(value<=0){return value}var half=bits<=32?Math.abs(1<<bits-1):Math.pow(2,bits-1);if(value>=half&&(bits<=32||value>half)){value=-2*half+value}return value}if(!Math["imul"]||Math["imul"](4294967295,5)!==-5)Math["imul"]=function imul(a,b){var ah=a>>>16;var al=a&65535;var bh=b>>>16;var bl=b&65535;return al*bl+(ah*bl+al*bh<<16)|0};Math.imul=Math["imul"];if(!Math["clz32"])Math["clz32"]=(function(x){x=x>>>0;for(var i=0;i<32;i++){if(x&1<<31-i)return i}return 32});Math.clz32=Math["clz32"];var Math_abs=Math.abs;var Math_cos=Math.cos;var Math_sin=Math.sin;var Math_tan=Math.tan;var Math_acos=Math.acos;var Math_asin=Math.asin;var Math_atan=Math.atan;var Math_atan2=Math.atan2;var Math_exp=Math.exp;var Math_log=Math.log;var Math_sqrt=Math.sqrt;var Math_ceil=Math.ceil;var Math_floor=Math.floor;var Math_pow=Math.pow;var Math_imul=Math.imul;var Math_fround=Math.fround;var Math_min=Math.min;var Math_clz32=Math.clz32;var runDependencies=0;var runDependencyWatcher=null;var dependenciesFulfilled=null;function getUniqueRunDependency(id){return id}function addRunDependency(id){runDependencies++;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}}Module["addRunDependency"]=addRunDependency;function removeRunDependency(id){runDependencies--;if(Module["monitorRunDependencies"]){Module["monitorRunDependencies"](runDependencies)}if(runDependencies==0){if(runDependencyWatcher!==null){clearInterval(runDependencyWatcher);runDependencyWatcher=null}if(dependenciesFulfilled){var callback=dependenciesFulfilled;dependenciesFulfilled=null;callback()}}}Module["removeRunDependency"]=removeRunDependency;Module["preloadedImages"]={};Module["preloadedAudios"]={};var memoryInitializer=null;var ASM_CONSTS=[];STATIC_BASE=8;STATICTOP=STATIC_BASE+560;__ATINIT__.push();allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"i8",ALLOC_NONE,Runtime.GLOBAL_BASE);var tempDoublePtr=Runtime.alignMemory(allocate(12,"i8",ALLOC_STATIC),8);assert(tempDoublePtr%8==0);function copyTempFloat(ptr){HEAP8[tempDoublePtr]=HEAP8[ptr];HEAP8[tempDoublePtr+1]=HEAP8[ptr+1];HEAP8[tempDoublePtr+2]=HEAP8[ptr+2];HEAP8[tempDoublePtr+3]=HEAP8[ptr+3]}function copyTempDouble(ptr){HEAP8[tempDoublePtr]=HEAP8[ptr];HEAP8[tempDoublePtr+1]=HEAP8[ptr+1];HEAP8[tempDoublePtr+2]=HEAP8[ptr+2];HEAP8[tempDoublePtr+3]=HEAP8[ptr+3];HEAP8[tempDoublePtr+4]=HEAP8[ptr+4];HEAP8[tempDoublePtr+5]=HEAP8[ptr+5];HEAP8[tempDoublePtr+6]=HEAP8[ptr+6];HEAP8[tempDoublePtr+7]=HEAP8[ptr+7]}var _cos=Math_cos;function _sbrk(bytes){var self=_sbrk;if(!self.called){DYNAMICTOP=alignMemoryPage(DYNAMICTOP);self.called=true;assert(Runtime.dynamicAlloc);self.alloc=Runtime.dynamicAlloc;Runtime.dynamicAlloc=(function(){abort("cannot dynamically allocate, sbrk now has control")})}var ret=DYNAMICTOP;if(bytes!=0){var success=self.alloc(bytes);if(!success)return-1>>>0}return ret}function ___setErrNo(value){if(Module["___errno_location"])HEAP32[Module["___errno_location"]()>>2]=value;return value}var ERRNO_CODES={EPERM:1,ENOENT:2,ESRCH:3,EINTR:4,EIO:5,ENXIO:6,E2BIG:7,ENOEXEC:8,EBADF:9,ECHILD:10,EAGAIN:11,EWOULDBLOCK:11,ENOMEM:12,EACCES:13,EFAULT:14,ENOTBLK:15,EBUSY:16,EEXIST:17,EXDEV:18,ENODEV:19,ENOTDIR:20,EISDIR:21,EINVAL:22,ENFILE:23,EMFILE:24,ENOTTY:25,ETXTBSY:26,EFBIG:27,ENOSPC:28,ESPIPE:29,EROFS:30,EMLINK:31,EPIPE:32,EDOM:33,ERANGE:34,ENOMSG:42,EIDRM:43,ECHRNG:44,EL2NSYNC:45,EL3HLT:46,EL3RST:47,ELNRNG:48,EUNATCH:49,ENOCSI:50,EL2HLT:51,EDEADLK:35,ENOLCK:37,EBADE:52,EBADR:53,EXFULL:54,ENOANO:55,EBADRQC:56,EBADSLT:57,EDEADLOCK:35,EBFONT:59,ENOSTR:60,ENODATA:61,ETIME:62,ENOSR:63,ENONET:64,ENOPKG:65,EREMOTE:66,ENOLINK:67,EADV:68,ESRMNT:69,ECOMM:70,EPROTO:71,EMULTIHOP:72,EDOTDOT:73,EBADMSG:74,ENOTUNIQ:76,EBADFD:77,EREMCHG:78,ELIBACC:79,ELIBBAD:80,ELIBSCN:81,ELIBMAX:82,ELIBEXEC:83,ENOSYS:38,ENOTEMPTY:39,ENAMETOOLONG:36,ELOOP:40,EOPNOTSUPP:95,EPFNOSUPPORT:96,ECONNRESET:104,ENOBUFS:105,EAFNOSUPPORT:97,EPROTOTYPE:91,ENOTSOCK:88,ENOPROTOOPT:92,ESHUTDOWN:108,ECONNREFUSED:111,EADDRINUSE:98,ECONNABORTED:103,ENETUNREACH:101,ENETDOWN:100,ETIMEDOUT:110,EHOSTDOWN:112,EHOSTUNREACH:113,EINPROGRESS:115,EALREADY:114,EDESTADDRREQ:89,EMSGSIZE:90,EPROTONOSUPPORT:93,ESOCKTNOSUPPORT:94,EADDRNOTAVAIL:99,ENETRESET:102,EISCONN:106,ENOTCONN:107,ETOOMANYREFS:109,EUSERS:87,EDQUOT:122,ESTALE:116,ENOTSUP:95,ENOMEDIUM:123,EILSEQ:84,EOVERFLOW:75,ECANCELED:125,ENOTRECOVERABLE:131,EOWNERDEAD:130,ESTRPIPE:86};function _sysconf(name){switch(name){case 30:return PAGE_SIZE;case 85:return totalMemory/PAGE_SIZE;case 132:case 133:case 12:case 137:case 138:case 15:case 235:case 16:case 17:case 18:case 19:case 20:case 149:case 13:case 10:case 236:case 153:case 9:case 21:case 22:case 159:case 154:case 14:case 77:case 78:case 139:case 80:case 81:case 82:case 68:case 67:case 164:case 11:case 29:case 47:case 48:case 95:case 52:case 51:case 46:return 200809;case 79:return 0;case 27:case 246:case 127:case 128:case 23:case 24:case 160:case 161:case 181:case 182:case 242:case 183:case 184:case 243:case 244:case 245:case 165:case 178:case 179:case 49:case 50:case 168:case 169:case 175:case 170:case 171:case 172:case 97:case 76:case 32:case 173:case 35:return-1;case 176:case 177:case 7:case 155:case 8:case 157:case 125:case 126:case 92:case 93:case 129:case 130:case 131:case 94:case 91:return 1;case 74:case 60:case 69:case 70:case 4:return 1024;case 31:case 42:case 72:return 32;case 87:case 26:case 33:return 2147483647;case 34:case 1:return 47839;case 38:case 36:return 99;case 43:case 37:return 2048;case 0:return 2097152;case 3:return 65536;case 28:return 32768;case 44:return 32767;case 75:return 16384;case 39:return 1e3;case 89:return 700;case 71:return 256;case 40:return 255;case 2:return 100;case 180:return 64;case 25:return 20;case 5:return 16;case 6:return 6;case 73:return 4;case 84:{if(typeof navigator==="object")return navigator["hardwareConcurrency"]||1;return 1}}___setErrNo(ERRNO_CODES.EINVAL);return-1}Module["_memset"]=_memset;function _emscripten_memcpy_big(dest,src,num){HEAPU8.set(HEAPU8.subarray(src,src+num),dest);return dest}Module["_memcpy"]=_memcpy;function _abort(){Module["abort"]()}function _time(ptr){var ret=Date.now()/1e3|0;if(ptr){HEAP32[ptr>>2]=ret}return ret}function _pthread_self(){return 0}var _sin=Math_sin;STACK_BASE=STACKTOP=Runtime.alignMemory(STATICTOP);staticSealed=true;STACK_MAX=STACK_BASE+TOTAL_STACK;DYNAMIC_BASE=DYNAMICTOP=Runtime.alignMemory(STACK_MAX);assert(DYNAMIC_BASE<TOTAL_MEMORY,"TOTAL_MEMORY not big enough for stack");Module.asmGlobalArg={"Math":Math,"Int8Array":Int8Array,"Int16Array":Int16Array,"Int32Array":Int32Array,"Uint8Array":Uint8Array,"Uint16Array":Uint16Array,"Uint32Array":Uint32Array,"Float32Array":Float32Array,"Float64Array":Float64Array,"NaN":NaN,"Infinity":Infinity};Module.asmLibraryArg={"abort":abort,"assert":assert,"_sin":_sin,"_cos":_cos,"_pthread_self":_pthread_self,"_abort":_abort,"___setErrNo":___setErrNo,"_sbrk":_sbrk,"_time":_time,"_emscripten_memcpy_big":_emscripten_memcpy_big,"_sysconf":_sysconf,"STACKTOP":STACKTOP,"STACK_MAX":STACK_MAX,"tempDoublePtr":tempDoublePtr,"ABORT":ABORT};// EMSCRIPTEN_START_ASM
+var asm=(function(global,env,buffer) {
+"use asm";var a=new global.Int8Array(buffer);var b=new global.Int16Array(buffer);var c=new global.Int32Array(buffer);var d=new global.Uint8Array(buffer);var e=new global.Uint16Array(buffer);var f=new global.Uint32Array(buffer);var g=new global.Float32Array(buffer);var h=new global.Float64Array(buffer);var i=env.STACKTOP|0;var j=env.STACK_MAX|0;var k=env.tempDoublePtr|0;var l=env.ABORT|0;var m=0;var n=0;var o=0;var p=0;var q=global.NaN,r=global.Infinity;var s=0,t=0,u=0,v=0,w=0.0,x=0,y=0,z=0,A=0.0;var B=0;var C=0;var D=0;var E=0;var F=0;var G=0;var H=0;var I=0;var J=0;var K=0;var L=global.Math.floor;var M=global.Math.abs;var N=global.Math.sqrt;var O=global.Math.pow;var P=global.Math.cos;var Q=global.Math.sin;var R=global.Math.tan;var S=global.Math.acos;var T=global.Math.asin;var U=global.Math.atan;var V=global.Math.atan2;var W=global.Math.exp;var X=global.Math.log;var Y=global.Math.ceil;var Z=global.Math.imul;var _=global.Math.min;var $=global.Math.clz32;var aa=env.abort;var ba=env.assert;var ca=env._sin;var da=env._cos;var ea=env._pthread_self;var fa=env._abort;var ga=env.___setErrNo;var ha=env._sbrk;var ia=env._time;var ja=env._emscripten_memcpy_big;var ka=env._sysconf;var la=0.0;
+// EMSCRIPTEN_START_FUNCS
+function ma(a){a=a|0;var b=0;b=i;i=i+a|0;i=i+15&-16;return b|0}function na(){return i|0}function oa(a){a=a|0;i=a}function pa(a,b){a=a|0;b=b|0;i=a;j=b}function qa(a,b){a=a|0;b=b|0;if(!m){m=a;n=b}}function ra(b){b=b|0;a[k>>0]=a[b>>0];a[k+1>>0]=a[b+1>>0];a[k+2>>0]=a[b+2>>0];a[k+3>>0]=a[b+3>>0]}function sa(b){b=b|0;a[k>>0]=a[b>>0];a[k+1>>0]=a[b+1>>0];a[k+2>>0]=a[b+2>>0];a[k+3>>0]=a[b+3>>0];a[k+4>>0]=a[b+4>>0];a[k+5>>0]=a[b+5>>0];a[k+6>>0]=a[b+6>>0];a[k+7>>0]=a[b+7>>0]}function ta(a){a=a|0;B=a}function ua(){return B|0}function va(a){a=a|0;var b=0,d=0,e=0.0,f=0,g=0,i=0,j=0.0;if(a>>>0>1){b=0;d=a;while(1){b=b+1|0;if(d>>>0>3)d=d>>>1;else{d=b;break}}}else d=0;if((1<<d|0)!=(a|0)){a=0;return a|0}i=a>>>1;if(a>>>0>1073741823){a=0;return a|0}b=Ca(4)|0;if(!b){a=b;return a|0}c[b+8>>2]=d;d=i<<3;g=Ca(d)|0;c[b>>2]=g;if(!g){Da(b);a=0;return a|0}f=Ca(d)|0;c[b+4>>2]=f;if(!f){Da(g);Da(b);a=0;return a|0}if(!i){a=b;return a|0}e=+(a>>>0);d=0;do{j=+(d|0)*6.283185307179586/e;h[g+(d<<3)>>3]=+P(+j);h[f+(d<<3)>>3]=+Q(+j);d=d+1|0}while((d|0)!=(i|0));return b|0}function wa(a){a=a|0;var b=0,d=0,e=0.0,f=0,h=0,i=0,j=0.0;if(a>>>0>1){b=0;d=a;while(1){b=b+1|0;if(d>>>0>3)d=d>>>1;else{d=b;break}}}else d=0;if((1<<d|0)!=(a|0)){a=0;return a|0}i=a>>>1;if((a|0)<0){a=0;return a|0}b=Ca(12)|0;if(!b){a=b;return a|0}c[b+8>>2]=d;d=i<<2;h=Ca(d)|0;c[b>>2]=h;if(!h){Da(b);a=0;return a|0}f=Ca(d)|0;c[b+4>>2]=f;if(!f){Da(h);Da(b);a=0;return a|0}if(!i){a=b;return a|0}e=+(a>>>0);d=0;do{j=+(d|0)*6.283185307179586/e;g[h+(d<<2)>>2]=+P(+j);g[f+(d<<2)>>2]=+Q(+j);d=d+1|0}while((d|0)!=(i|0));return b|0}function xa(a){a=a|0;if(!a)return;Da(c[a>>2]|0);Da(c[a+4>>2]|0);Da(a);return}function ya(a){a=a|0;if(!a)return;Da(c[a>>2]|0);Da(c[a+4>>2]|0);Da(a);return}function za(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,g=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0.0,q=0,r=0,s=0.0,t=0,u=0.0,v=0.0,w=0.0;n=c[e>>2]|0;o=c[e+4>>2]|0;if((d|0)<=0)return;i=c[e+8>>2]|0;if(!i){e=0;do e=e+1|0;while((e|0)!=(d|0))}else{j=0;do{f=j;g=0;e=0;while(1){e=f&1|e<<1;g=g+1|0;if((g|0)==(i|0))break;else f=f>>>1}if((e|0)>(j|0)){m=a+(j<<3)|0;p=+h[m>>3];l=a+(e<<3)|0;h[m>>3]=+h[l>>3];h[l>>3]=p;l=b+(j<<3)|0;p=+h[l>>3];m=b+(e<<3)|0;h[l>>3]=+h[m>>3];h[m>>3]=p}j=j+1|0}while((j|0)!=(d|0))}if((d|0)<2)return;else m=2;do{e=(m|0)/2|0;f=(d|0)/(m|0)|0;i=(m|0)>1;j=0;do{g=j+e|0;if(i){k=j;l=0;while(1){t=k+e|0;q=a+(t<<3)|0;u=+h[q>>3];w=+h[n+(l<<3)>>3];t=b+(t<<3)|0;v=+h[t>>3];p=+h[o+(l<<3)>>3];s=u*w+v*p;p=w*v-u*p;r=a+(k<<3)|0;h[q>>3]=+h[r>>3]-s;q=b+(k<<3)|0;h[t>>3]=+h[q>>3]-p;h[r>>3]=s+ +h[r>>3];h[q>>3]=p+ +h[q>>3];k=k+1|0;if((k|0)>=(g|0))break;else l=l+f|0}}j=j+m|0}while((j|0)<(d|0));t=m;m=m<<1}while(!((t|0)==(d|0)|(m|0)>(d|0)));return}function Aa(a,b,d,e){a=a|0;b=b|0;d=d|0;e=e|0;var f=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0.0,r=0,s=0.0,t=0,u=0.0,v=0.0,w=0.0;n=c[e>>2]|0;o=c[e+4>>2]|0;if((d|0)<=0)return;i=c[e+8>>2]|0;if(!i){e=0;do e=e+1|0;while((e|0)!=(d|0))}else{j=0;do{f=j;h=0;e=0;while(1){e=f&1|e<<1;h=h+1|0;if((h|0)==(i|0))break;else f=f>>>1}if((e|0)>(j|0)){m=a+(j<<2)|0;l=c[m>>2]|0;k=a+(e<<2)|0;c[m>>2]=c[k>>2];c[k>>2]=l;k=b+(j<<2)|0;l=c[k>>2]|0;m=b+(e<<2)|0;c[k>>2]=c[m>>2];c[m>>2]=l}j=j+1|0}while((j|0)!=(d|0))}if((d|0)<2)return;else m=2;do{e=(m|0)/2|0;f=(d|0)/(m|0)|0;i=(m|0)>1;j=0;do{h=j+e|0;if(i){k=j;l=0;while(1){t=k+e|0;p=a+(t<<2)|0;u=+g[p>>2];w=+g[n+(l<<2)>>2];t=b+(t<<2)|0;v=+g[t>>2];q=+g[o+(l<<2)>>2];s=u*w+v*q;q=w*v-u*q;r=a+(k<<2)|0;g[p>>2]=+g[r>>2]-s;p=b+(k<<2)|0;g[t>>2]=+g[p>>2]-q;g[r>>2]=s+ +g[r>>2];g[p>>2]=q+ +g[p>>2];k=k+1|0;if((k|0)>=(h|0))break;else l=l+f|0}}j=j+m|0}while((j|0)<(d|0));t=m;m=m<<1}while(!((t|0)==(d|0)|(m|0)>(d|0)));return}function Ba(){var a=0;if(!(c[2]|0))a=52;else a=c[(ea()|0)+60>>2]|0;return a|0}function Ca(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0,r=0,s=0,t=0,u=0,v=0,w=0,x=0,y=0,z=0,A=0,B=0,C=0,D=0,E=0,F=0,G=0,H=0,I=0,J=0,K=0,L=0;do if(a>>>0<245){o=a>>>0<11?16:a+11&-8;a=o>>>3;j=c[14]|0;b=j>>>a;if(b&3){b=(b&1^1)+a|0;d=96+(b<<1<<2)|0;e=d+8|0;f=c[e>>2]|0;g=f+8|0;h=c[g>>2]|0;do if((d|0)!=(h|0)){if(h>>>0<(c[18]|0)>>>0)fa();a=h+12|0;if((c[a>>2]|0)==(f|0)){c[a>>2]=d;c[e>>2]=h;break}else fa()}else c[14]=j&~(1<<b);while(0);L=b<<3;c[f+4>>2]=L|3;L=f+L+4|0;c[L>>2]=c[L>>2]|1;L=g;return L|0}h=c[16]|0;if(o>>>0>h>>>0){if(b){d=2<<a;d=b<<a&(d|0-d);d=(d&0-d)+-1|0;i=d>>>12&16;d=d>>>i;f=d>>>5&8;d=d>>>f;g=d>>>2&4;d=d>>>g;e=d>>>1&2;d=d>>>e;b=d>>>1&1;b=(f|i|g|e|b)+(d>>>b)|0;d=96+(b<<1<<2)|0;e=d+8|0;g=c[e>>2]|0;i=g+8|0;f=c[i>>2]|0;do if((d|0)!=(f|0)){if(f>>>0<(c[18]|0)>>>0)fa();a=f+12|0;if((c[a>>2]|0)==(g|0)){c[a>>2]=d;c[e>>2]=f;k=c[16]|0;break}else fa()}else{c[14]=j&~(1<<b);k=h}while(0);h=(b<<3)-o|0;c[g+4>>2]=o|3;e=g+o|0;c[e+4>>2]=h|1;c[e+h>>2]=h;if(k){f=c[19]|0;b=k>>>3;d=96+(b<<1<<2)|0;a=c[14]|0;b=1<<b;if(a&b){a=d+8|0;b=c[a>>2]|0;if(b>>>0<(c[18]|0)>>>0)fa();else{l=a;m=b}}else{c[14]=a|b;l=d+8|0;m=d}c[l>>2]=f;c[m+12>>2]=f;c[f+8>>2]=m;c[f+12>>2]=d}c[16]=h;c[19]=e;L=i;return L|0}a=c[15]|0;if(a){d=(a&0-a)+-1|0;K=d>>>12&16;d=d>>>K;J=d>>>5&8;d=d>>>J;L=d>>>2&4;d=d>>>L;b=d>>>1&2;d=d>>>b;e=d>>>1&1;e=c[360+((J|K|L|b|e)+(d>>>e)<<2)>>2]|0;d=(c[e+4>>2]&-8)-o|0;b=e;while(1){a=c[b+16>>2]|0;if(!a){a=c[b+20>>2]|0;if(!a){j=e;break}}b=(c[a+4>>2]&-8)-o|0;L=b>>>0<d>>>0;d=L?b:d;b=a;e=L?a:e}g=c[18]|0;if(j>>>0<g>>>0)fa();i=j+o|0;if(j>>>0>=i>>>0)fa();h=c[j+24>>2]|0;e=c[j+12>>2]|0;do if((e|0)==(j|0)){b=j+20|0;a=c[b>>2]|0;if(!a){b=j+16|0;a=c[b>>2]|0;if(!a){n=0;break}}while(1){e=a+20|0;f=c[e>>2]|0;if(f){a=f;b=e;continue}e=a+16|0;f=c[e>>2]|0;if(!f)break;else{a=f;b=e}}if(b>>>0<g>>>0)fa();else{c[b>>2]=0;n=a;break}}else{f=c[j+8>>2]|0;if(f>>>0<g>>>0)fa();a=f+12|0;if((c[a>>2]|0)!=(j|0))fa();b=e+8|0;if((c[b>>2]|0)==(j|0)){c[a>>2]=e;c[b>>2]=f;n=e;break}else fa()}while(0);do if(h){a=c[j+28>>2]|0;b=360+(a<<2)|0;if((j|0)==(c[b>>2]|0)){c[b>>2]=n;if(!n){c[15]=c[15]&~(1<<a);break}}else{if(h>>>0<(c[18]|0)>>>0)fa();a=h+16|0;if((c[a>>2]|0)==(j|0))c[a>>2]=n;else c[h+20>>2]=n;if(!n)break}b=c[18]|0;if(n>>>0<b>>>0)fa();c[n+24>>2]=h;a=c[j+16>>2]|0;do if(a)if(a>>>0<b>>>0)fa();else{c[n+16>>2]=a;c[a+24>>2]=n;break}while(0);a=c[j+20>>2]|0;if(a)if(a>>>0<(c[18]|0)>>>0)fa();else{c[n+20>>2]=a;c[a+24>>2]=n;break}}while(0);if(d>>>0<16){L=d+o|0;c[j+4>>2]=L|3;L=j+L+4|0;c[L>>2]=c[L>>2]|1}else{c[j+4>>2]=o|3;c[i+4>>2]=d|1;c[i+d>>2]=d;a=c[16]|0;if(a){f=c[19]|0;b=a>>>3;e=96+(b<<1<<2)|0;a=c[14]|0;b=1<<b;if(a&b){a=e+8|0;b=c[a>>2]|0;if(b>>>0<(c[18]|0)>>>0)fa();else{p=a;q=b}}else{c[14]=a|b;p=e+8|0;q=e}c[p>>2]=f;c[q+12>>2]=f;c[f+8>>2]=q;c[f+12>>2]=e}c[16]=d;c[19]=i}L=j+8|0;return L|0}}}else if(a>>>0<=4294967231){a=a+11|0;o=a&-8;j=c[15]|0;if(j){d=0-o|0;a=a>>>8;if(a)if(o>>>0>16777215)i=31;else{q=(a+1048320|0)>>>16&8;E=a<<q;p=(E+520192|0)>>>16&4;E=E<<p;i=(E+245760|0)>>>16&2;i=14-(p|q|i)+(E<<i>>>15)|0;i=o>>>(i+7|0)&1|i<<1}else i=0;b=c[360+(i<<2)>>2]|0;a:do if(!b){a=0;b=0;E=86}else{f=d;a=0;g=o<<((i|0)==31?0:25-(i>>>1)|0);h=b;b=0;while(1){e=c[h+4>>2]&-8;d=e-o|0;if(d>>>0<f>>>0)if((e|0)==(o|0)){a=h;b=h;E=90;break a}else b=h;else d=f;e=c[h+20>>2]|0;h=c[h+16+(g>>>31<<2)>>2]|0;a=(e|0)==0|(e|0)==(h|0)?a:e;e=(h|0)==0;if(e){E=86;break}else{f=d;g=g<<(e&1^1)}}}while(0);if((E|0)==86){if((a|0)==0&(b|0)==0){a=2<<i;a=j&(a|0-a);if(!a)break;q=(a&0-a)+-1|0;m=q>>>12&16;q=q>>>m;l=q>>>5&8;q=q>>>l;n=q>>>2&4;q=q>>>n;p=q>>>1&2;q=q>>>p;a=q>>>1&1;a=c[360+((l|m|n|p|a)+(q>>>a)<<2)>>2]|0}if(!a){i=d;j=b}else E=90}if((E|0)==90)while(1){E=0;q=(c[a+4>>2]&-8)-o|0;e=q>>>0<d>>>0;d=e?q:d;b=e?a:b;e=c[a+16>>2]|0;if(e){a=e;E=90;continue}a=c[a+20>>2]|0;if(!a){i=d;j=b;break}else E=90}if((j|0)!=0?i>>>0<((c[16]|0)-o|0)>>>0:0){f=c[18]|0;if(j>>>0<f>>>0)fa();h=j+o|0;if(j>>>0>=h>>>0)fa();g=c[j+24>>2]|0;d=c[j+12>>2]|0;do if((d|0)==(j|0)){b=j+20|0;a=c[b>>2]|0;if(!a){b=j+16|0;a=c[b>>2]|0;if(!a){s=0;break}}while(1){d=a+20|0;e=c[d>>2]|0;if(e){a=e;b=d;continue}d=a+16|0;e=c[d>>2]|0;if(!e)break;else{a=e;b=d}}if(b>>>0<f>>>0)fa();else{c[b>>2]=0;s=a;break}}else{e=c[j+8>>2]|0;if(e>>>0<f>>>0)fa();a=e+12|0;if((c[a>>2]|0)!=(j|0))fa();b=d+8|0;if((c[b>>2]|0)==(j|0)){c[a>>2]=d;c[b>>2]=e;s=d;break}else fa()}while(0);do if(g){a=c[j+28>>2]|0;b=360+(a<<2)|0;if((j|0)==(c[b>>2]|0)){c[b>>2]=s;if(!s){c[15]=c[15]&~(1<<a);break}}else{if(g>>>0<(c[18]|0)>>>0)fa();a=g+16|0;if((c[a>>2]|0)==(j|0))c[a>>2]=s;else c[g+20>>2]=s;if(!s)break}b=c[18]|0;if(s>>>0<b>>>0)fa();c[s+24>>2]=g;a=c[j+16>>2]|0;do if(a)if(a>>>0<b>>>0)fa();else{c[s+16>>2]=a;c[a+24>>2]=s;break}while(0);a=c[j+20>>2]|0;if(a)if(a>>>0<(c[18]|0)>>>0)fa();else{c[s+20>>2]=a;c[a+24>>2]=s;break}}while(0);do if(i>>>0>=16){c[j+4>>2]=o|3;c[h+4>>2]=i|1;c[h+i>>2]=i;a=i>>>3;if(i>>>0<256){d=96+(a<<1<<2)|0;b=c[14]|0;a=1<<a;if(b&a){a=d+8|0;b=c[a>>2]|0;if(b>>>0<(c[18]|0)>>>0)fa();else{u=a;v=b}}else{c[14]=b|a;u=d+8|0;v=d}c[u>>2]=h;c[v+12>>2]=h;c[h+8>>2]=v;c[h+12>>2]=d;break}a=i>>>8;if(a)if(i>>>0>16777215)d=31;else{K=(a+1048320|0)>>>16&8;L=a<<K;J=(L+520192|0)>>>16&4;L=L<<J;d=(L+245760|0)>>>16&2;d=14-(J|K|d)+(L<<d>>>15)|0;d=i>>>(d+7|0)&1|d<<1}else d=0;e=360+(d<<2)|0;c[h+28>>2]=d;a=h+16|0;c[a+4>>2]=0;c[a>>2]=0;a=c[15]|0;b=1<<d;if(!(a&b)){c[15]=a|b;c[e>>2]=h;c[h+24>>2]=e;c[h+12>>2]=h;c[h+8>>2]=h;break}f=i<<((d|0)==31?0:25-(d>>>1)|0);a=c[e>>2]|0;while(1){if((c[a+4>>2]&-8|0)==(i|0)){d=a;E=148;break}b=a+16+(f>>>31<<2)|0;d=c[b>>2]|0;if(!d){E=145;break}else{f=f<<1;a=d}}if((E|0)==145)if(b>>>0<(c[18]|0)>>>0)fa();else{c[b>>2]=h;c[h+24>>2]=a;c[h+12>>2]=h;c[h+8>>2]=h;break}else if((E|0)==148){a=d+8|0;b=c[a>>2]|0;L=c[18]|0;if(b>>>0>=L>>>0&d>>>0>=L>>>0){c[b+12>>2]=h;c[a>>2]=h;c[h+8>>2]=b;c[h+12>>2]=d;c[h+24>>2]=0;break}else fa()}}else{L=i+o|0;c[j+4>>2]=L|3;L=j+L+4|0;c[L>>2]=c[L>>2]|1}while(0);L=j+8|0;return L|0}}}else o=-1;while(0);d=c[16]|0;if(d>>>0>=o>>>0){a=d-o|0;b=c[19]|0;if(a>>>0>15){L=b+o|0;c[19]=L;c[16]=a;c[L+4>>2]=a|1;c[L+a>>2]=a;c[b+4>>2]=o|3}else{c[16]=0;c[19]=0;c[b+4>>2]=d|3;L=b+d+4|0;c[L>>2]=c[L>>2]|1}L=b+8|0;return L|0}a=c[17]|0;if(a>>>0>o>>>0){J=a-o|0;c[17]=J;L=c[20]|0;K=L+o|0;c[20]=K;c[K+4>>2]=J|1;c[L+4>>2]=o|3;L=L+8|0;return L|0}do if(!(c[132]|0)){a=ka(30)|0;if(!(a+-1&a)){c[134]=a;c[133]=a;c[135]=-1;c[136]=-1;c[137]=0;c[125]=0;c[132]=(ia(0)|0)&-16^1431655768;break}else fa()}while(0);h=o+48|0;g=c[134]|0;i=o+47|0;f=g+i|0;g=0-g|0;j=f&g;if(j>>>0<=o>>>0){L=0;return L|0}a=c[124]|0;if((a|0)!=0?(u=c[122]|0,v=u+j|0,v>>>0<=u>>>0|v>>>0>a>>>0):0){L=0;return L|0}b:do if(!(c[125]&4)){a=c[20]|0;c:do if(a){d=504;while(1){b=c[d>>2]|0;if(b>>>0<=a>>>0?(r=d+4|0,(b+(c[r>>2]|0)|0)>>>0>a>>>0):0){e=d;d=r;break}d=c[d+8>>2]|0;if(!d){E=173;break c}}a=f-(c[17]|0)&g;if(a>>>0<2147483647){b=ha(a|0)|0;if((b|0)==((c[e>>2]|0)+(c[d>>2]|0)|0)){if((b|0)!=(-1|0)){h=b;f=a;E=193;break b}}else E=183}}else E=173;while(0);do if((E|0)==173?(t=ha(0)|0,(t|0)!=(-1|0)):0){a=t;b=c[133]|0;d=b+-1|0;if(!(d&a))a=j;else a=j-a+(d+a&0-b)|0;b=c[122]|0;d=b+a|0;if(a>>>0>o>>>0&a>>>0<2147483647){v=c[124]|0;if((v|0)!=0?d>>>0<=b>>>0|d>>>0>v>>>0:0)break;b=ha(a|0)|0;if((b|0)==(t|0)){h=t;f=a;E=193;break b}else E=183}}while(0);d:do if((E|0)==183){d=0-a|0;do if(h>>>0>a>>>0&(a>>>0<2147483647&(b|0)!=(-1|0))?(w=c[134]|0,w=i-a+w&0-w,w>>>0<2147483647):0)if((ha(w|0)|0)==(-1|0)){ha(d|0)|0;break d}else{a=w+a|0;break}while(0);if((b|0)!=(-1|0)){h=b;f=a;E=193;break b}}while(0);c[125]=c[125]|4;E=190}else E=190;while(0);if((((E|0)==190?j>>>0<2147483647:0)?(x=ha(j|0)|0,y=ha(0)|0,x>>>0<y>>>0&((x|0)!=(-1|0)&(y|0)!=(-1|0))):0)?(z=y-x|0,z>>>0>(o+40|0)>>>0):0){h=x;f=z;E=193}if((E|0)==193){a=(c[122]|0)+f|0;c[122]=a;if(a>>>0>(c[123]|0)>>>0)c[123]=a;i=c[20]|0;do if(i){e=504;do{a=c[e>>2]|0;b=e+4|0;d=c[b>>2]|0;if((h|0)==(a+d|0)){A=a;B=b;C=d;D=e;E=203;break}e=c[e+8>>2]|0}while((e|0)!=0);if(((E|0)==203?(c[D+12>>2]&8|0)==0:0)?i>>>0<h>>>0&i>>>0>=A>>>0:0){c[B>>2]=C+f;L=i+8|0;L=(L&7|0)==0?0:0-L&7;K=i+L|0;L=f-L+(c[17]|0)|0;c[20]=K;c[17]=L;c[K+4>>2]=L|1;c[K+L+4>>2]=40;c[21]=c[136];break}a=c[18]|0;if(h>>>0<a>>>0){c[18]=h;j=h}else j=a;d=h+f|0;a=504;while(1){if((c[a>>2]|0)==(d|0)){b=a;E=211;break}a=c[a+8>>2]|0;if(!a){b=504;break}}if((E|0)==211)if(!(c[a+12>>2]&8)){c[b>>2]=h;l=a+4|0;c[l>>2]=(c[l>>2]|0)+f;l=h+8|0;l=h+((l&7|0)==0?0:0-l&7)|0;a=d+8|0;a=d+((a&7|0)==0?0:0-a&7)|0;k=l+o|0;g=a-l-o|0;c[l+4>>2]=o|3;do if((a|0)!=(i|0)){if((a|0)==(c[19]|0)){L=(c[16]|0)+g|0;c[16]=L;c[19]=k;c[k+4>>2]=L|1;c[k+L>>2]=L;break}b=c[a+4>>2]|0;if((b&3|0)==1){i=b&-8;f=b>>>3;e:do if(b>>>0>=256){h=c[a+24>>2]|0;e=c[a+12>>2]|0;do if((e|0)==(a|0)){d=a+16|0;e=d+4|0;b=c[e>>2]|0;if(!b){b=c[d>>2]|0;if(!b){J=0;break}}else d=e;while(1){e=b+20|0;f=c[e>>2]|0;if(f){b=f;d=e;continue}e=b+16|0;f=c[e>>2]|0;if(!f)break;else{b=f;d=e}}if(d>>>0<j>>>0)fa();else{c[d>>2]=0;J=b;break}}else{f=c[a+8>>2]|0;if(f>>>0<j>>>0)fa();b=f+12|0;if((c[b>>2]|0)!=(a|0))fa();d=e+8|0;if((c[d>>2]|0)==(a|0)){c[b>>2]=e;c[d>>2]=f;J=e;break}else fa()}while(0);if(!h)break;b=c[a+28>>2]|0;d=360+(b<<2)|0;do if((a|0)!=(c[d>>2]|0)){if(h>>>0<(c[18]|0)>>>0)fa();b=h+16|0;if((c[b>>2]|0)==(a|0))c[b>>2]=J;else c[h+20>>2]=J;if(!J)break e}else{c[d>>2]=J;if(J)break;c[15]=c[15]&~(1<<b);break e}while(0);e=c[18]|0;if(J>>>0<e>>>0)fa();c[J+24>>2]=h;b=a+16|0;d=c[b>>2]|0;do if(d)if(d>>>0<e>>>0)fa();else{c[J+16>>2]=d;c[d+24>>2]=J;break}while(0);b=c[b+4>>2]|0;if(!b)break;if(b>>>0<(c[18]|0)>>>0)fa();else{c[J+20>>2]=b;c[b+24>>2]=J;break}}else{d=c[a+8>>2]|0;e=c[a+12>>2]|0;b=96+(f<<1<<2)|0;do if((d|0)!=(b|0)){if(d>>>0<j>>>0)fa();if((c[d+12>>2]|0)==(a|0))break;fa()}while(0);if((e|0)==(d|0)){c[14]=c[14]&~(1<<f);break}do if((e|0)==(b|0))G=e+8|0;else{if(e>>>0<j>>>0)fa();b=e+8|0;if((c[b>>2]|0)==(a|0)){G=b;break}fa()}while(0);c[d+12>>2]=e;c[G>>2]=d}while(0);a=a+i|0;g=i+g|0}a=a+4|0;c[a>>2]=c[a>>2]&-2;c[k+4>>2]=g|1;c[k+g>>2]=g;a=g>>>3;if(g>>>0<256){d=96+(a<<1<<2)|0;b=c[14]|0;a=1<<a;do if(!(b&a)){c[14]=b|a;K=d+8|0;L=d}else{a=d+8|0;b=c[a>>2]|0;if(b>>>0>=(c[18]|0)>>>0){K=a;L=b;break}fa()}while(0);c[K>>2]=k;c[L+12>>2]=k;c[k+8>>2]=L;c[k+12>>2]=d;break}a=g>>>8;do if(!a)d=0;else{if(g>>>0>16777215){d=31;break}K=(a+1048320|0)>>>16&8;L=a<<K;J=(L+520192|0)>>>16&4;L=L<<J;d=(L+245760|0)>>>16&2;d=14-(J|K|d)+(L<<d>>>15)|0;d=g>>>(d+7|0)&1|d<<1}while(0);e=360+(d<<2)|0;c[k+28>>2]=d;a=k+16|0;c[a+4>>2]=0;c[a>>2]=0;a=c[15]|0;b=1<<d;if(!(a&b)){c[15]=a|b;c[e>>2]=k;c[k+24>>2]=e;c[k+12>>2]=k;c[k+8>>2]=k;break}f=g<<((d|0)==31?0:25-(d>>>1)|0);a=c[e>>2]|0;while(1){if((c[a+4>>2]&-8|0)==(g|0)){d=a;E=281;break}b=a+16+(f>>>31<<2)|0;d=c[b>>2]|0;if(!d){E=278;break}else{f=f<<1;a=d}}if((E|0)==278)if(b>>>0<(c[18]|0)>>>0)fa();else{c[b>>2]=k;c[k+24>>2]=a;c[k+12>>2]=k;c[k+8>>2]=k;break}else if((E|0)==281){a=d+8|0;b=c[a>>2]|0;L=c[18]|0;if(b>>>0>=L>>>0&d>>>0>=L>>>0){c[b+12>>2]=k;c[a>>2]=k;c[k+8>>2]=b;c[k+12>>2]=d;c[k+24>>2]=0;break}else fa()}}else{L=(c[17]|0)+g|0;c[17]=L;c[20]=k;c[k+4>>2]=L|1}while(0);L=l+8|0;return L|0}else b=504;while(1){a=c[b>>2]|0;if(a>>>0<=i>>>0?(F=a+(c[b+4>>2]|0)|0,F>>>0>i>>>0):0){b=F;break}b=c[b+8>>2]|0}g=b+-47|0;d=g+8|0;d=g+((d&7|0)==0?0:0-d&7)|0;g=i+16|0;d=d>>>0<g>>>0?i:d;a=d+8|0;e=h+8|0;e=(e&7|0)==0?0:0-e&7;L=h+e|0;e=f+-40-e|0;c[20]=L;c[17]=e;c[L+4>>2]=e|1;c[L+e+4>>2]=40;c[21]=c[136];e=d+4|0;c[e>>2]=27;c[a>>2]=c[126];c[a+4>>2]=c[127];c[a+8>>2]=c[128];c[a+12>>2]=c[129];c[126]=h;c[127]=f;c[129]=0;c[128]=a;a=d+24|0;do{a=a+4|0;c[a>>2]=7}while((a+4|0)>>>0<b>>>0);if((d|0)!=(i|0)){h=d-i|0;c[e>>2]=c[e>>2]&-2;c[i+4>>2]=h|1;c[d>>2]=h;a=h>>>3;if(h>>>0<256){d=96+(a<<1<<2)|0;b=c[14]|0;a=1<<a;if(b&a){a=d+8|0;b=c[a>>2]|0;if(b>>>0<(c[18]|0)>>>0)fa();else{H=a;I=b}}else{c[14]=b|a;H=d+8|0;I=d}c[H>>2]=i;c[I+12>>2]=i;c[i+8>>2]=I;c[i+12>>2]=d;break}a=h>>>8;if(a)if(h>>>0>16777215)d=31;else{K=(a+1048320|0)>>>16&8;L=a<<K;J=(L+520192|0)>>>16&4;L=L<<J;d=(L+245760|0)>>>16&2;d=14-(J|K|d)+(L<<d>>>15)|0;d=h>>>(d+7|0)&1|d<<1}else d=0;f=360+(d<<2)|0;c[i+28>>2]=d;c[i+20>>2]=0;c[g>>2]=0;a=c[15]|0;b=1<<d;if(!(a&b)){c[15]=a|b;c[f>>2]=i;c[i+24>>2]=f;c[i+12>>2]=i;c[i+8>>2]=i;break}e=h<<((d|0)==31?0:25-(d>>>1)|0);a=c[f>>2]|0;while(1){if((c[a+4>>2]&-8|0)==(h|0)){d=a;E=307;break}b=a+16+(e>>>31<<2)|0;d=c[b>>2]|0;if(!d){E=304;break}else{e=e<<1;a=d}}if((E|0)==304)if(b>>>0<(c[18]|0)>>>0)fa();else{c[b>>2]=i;c[i+24>>2]=a;c[i+12>>2]=i;c[i+8>>2]=i;break}else if((E|0)==307){a=d+8|0;b=c[a>>2]|0;L=c[18]|0;if(b>>>0>=L>>>0&d>>>0>=L>>>0){c[b+12>>2]=i;c[a>>2]=i;c[i+8>>2]=b;c[i+12>>2]=d;c[i+24>>2]=0;break}else fa()}}}else{L=c[18]|0;if((L|0)==0|h>>>0<L>>>0)c[18]=h;c[126]=h;c[127]=f;c[129]=0;c[23]=c[132];c[22]=-1;a=0;do{L=96+(a<<1<<2)|0;c[L+12>>2]=L;c[L+8>>2]=L;a=a+1|0}while((a|0)!=32);L=h+8|0;L=(L&7|0)==0?0:0-L&7;K=h+L|0;L=f+-40-L|0;c[20]=K;c[17]=L;c[K+4>>2]=L|1;c[K+L+4>>2]=40;c[21]=c[136]}while(0);a=c[17]|0;if(a>>>0>o>>>0){J=a-o|0;c[17]=J;L=c[20]|0;K=L+o|0;c[20]=K;c[K+4>>2]=J|1;c[L+4>>2]=o|3;L=L+8|0;return L|0}}c[(Ba()|0)>>2]=12;L=0;return L|0}function Da(a){a=a|0;var b=0,d=0,e=0,f=0,g=0,h=0,i=0,j=0,k=0,l=0,m=0,n=0,o=0,p=0,q=0;if(!a)return;d=a+-8|0;h=c[18]|0;if(d>>>0<h>>>0)fa();a=c[a+-4>>2]|0;b=a&3;if((b|0)==1)fa();e=a&-8;m=d+e|0;do if(!(a&1)){a=c[d>>2]|0;if(!b)return;k=d+(0-a)|0;j=a+e|0;if(k>>>0<h>>>0)fa();if((k|0)==(c[19]|0)){a=m+4|0;b=c[a>>2]|0;if((b&3|0)!=3){q=k;g=j;break}c[16]=j;c[a>>2]=b&-2;c[k+4>>2]=j|1;c[k+j>>2]=j;return}e=a>>>3;if(a>>>0<256){b=c[k+8>>2]|0;d=c[k+12>>2]|0;a=96+(e<<1<<2)|0;if((b|0)!=(a|0)){if(b>>>0<h>>>0)fa();if((c[b+12>>2]|0)!=(k|0))fa()}if((d|0)==(b|0)){c[14]=c[14]&~(1<<e);q=k;g=j;break}if((d|0)!=(a|0)){if(d>>>0<h>>>0)fa();a=d+8|0;if((c[a>>2]|0)==(k|0))f=a;else fa()}else f=d+8|0;c[b+12>>2]=d;c[f>>2]=b;q=k;g=j;break}f=c[k+24>>2]|0;d=c[k+12>>2]|0;do if((d|0)==(k|0)){b=k+16|0;d=b+4|0;a=c[d>>2]|0;if(!a){a=c[b>>2]|0;if(!a){i=0;break}}else b=d;while(1){d=a+20|0;e=c[d>>2]|0;if(e){a=e;b=d;continue}d=a+16|0;e=c[d>>2]|0;if(!e)break;else{a=e;b=d}}if(b>>>0<h>>>0)fa();else{c[b>>2]=0;i=a;break}}else{e=c[k+8>>2]|0;if(e>>>0<h>>>0)fa();a=e+12|0;if((c[a>>2]|0)!=(k|0))fa();b=d+8|0;if((c[b>>2]|0)==(k|0)){c[a>>2]=d;c[b>>2]=e;i=d;break}else fa()}while(0);if(f){a=c[k+28>>2]|0;b=360+(a<<2)|0;if((k|0)==(c[b>>2]|0)){c[b>>2]=i;if(!i){c[15]=c[15]&~(1<<a);q=k;g=j;break}}else{if(f>>>0<(c[18]|0)>>>0)fa();a=f+16|0;if((c[a>>2]|0)==(k|0))c[a>>2]=i;else c[f+20>>2]=i;if(!i){q=k;g=j;break}}d=c[18]|0;if(i>>>0<d>>>0)fa();c[i+24>>2]=f;a=k+16|0;b=c[a>>2]|0;do if(b)if(b>>>0<d>>>0)fa();else{c[i+16>>2]=b;c[b+24>>2]=i;break}while(0);a=c[a+4>>2]|0;if(a)if(a>>>0<(c[18]|0)>>>0)fa();else{c[i+20>>2]=a;c[a+24>>2]=i;q=k;g=j;break}else{q=k;g=j}}else{q=k;g=j}}else{q=d;g=e}while(0);if(q>>>0>=m>>>0)fa();a=m+4|0;b=c[a>>2]|0;if(!(b&1))fa();if(!(b&2)){if((m|0)==(c[20]|0)){p=(c[17]|0)+g|0;c[17]=p;c[20]=q;c[q+4>>2]=p|1;if((q|0)!=(c[19]|0))return;c[19]=0;c[16]=0;return}if((m|0)==(c[19]|0)){p=(c[16]|0)+g|0;c[16]=p;c[19]=q;c[q+4>>2]=p|1;c[q+p>>2]=p;return}g=(b&-8)+g|0;e=b>>>3;do if(b>>>0>=256){f=c[m+24>>2]|0;a=c[m+12>>2]|0;do if((a|0)==(m|0)){b=m+16|0;d=b+4|0;a=c[d>>2]|0;if(!a){a=c[b>>2]|0;if(!a){n=0;break}}else b=d;while(1){d=a+20|0;e=c[d>>2]|0;if(e){a=e;b=d;continue}d=a+16|0;e=c[d>>2]|0;if(!e)break;else{a=e;b=d}}if(b>>>0<(c[18]|0)>>>0)fa();else{c[b>>2]=0;n=a;break}}else{b=c[m+8>>2]|0;if(b>>>0<(c[18]|0)>>>0)fa();d=b+12|0;if((c[d>>2]|0)!=(m|0))fa();e=a+8|0;if((c[e>>2]|0)==(m|0)){c[d>>2]=a;c[e>>2]=b;n=a;break}else fa()}while(0);if(f){a=c[m+28>>2]|0;b=360+(a<<2)|0;if((m|0)==(c[b>>2]|0)){c[b>>2]=n;if(!n){c[15]=c[15]&~(1<<a);break}}else{if(f>>>0<(c[18]|0)>>>0)fa();a=f+16|0;if((c[a>>2]|0)==(m|0))c[a>>2]=n;else c[f+20>>2]=n;if(!n)break}d=c[18]|0;if(n>>>0<d>>>0)fa();c[n+24>>2]=f;a=m+16|0;b=c[a>>2]|0;do if(b)if(b>>>0<d>>>0)fa();else{c[n+16>>2]=b;c[b+24>>2]=n;break}while(0);a=c[a+4>>2]|0;if(a)if(a>>>0<(c[18]|0)>>>0)fa();else{c[n+20>>2]=a;c[a+24>>2]=n;break}}}else{b=c[m+8>>2]|0;d=c[m+12>>2]|0;a=96+(e<<1<<2)|0;if((b|0)!=(a|0)){if(b>>>0<(c[18]|0)>>>0)fa();if((c[b+12>>2]|0)!=(m|0))fa()}if((d|0)==(b|0)){c[14]=c[14]&~(1<<e);break}if((d|0)!=(a|0)){if(d>>>0<(c[18]|0)>>>0)fa();a=d+8|0;if((c[a>>2]|0)==(m|0))l=a;else fa()}else l=d+8|0;c[b+12>>2]=d;c[l>>2]=b}while(0);c[q+4>>2]=g|1;c[q+g>>2]=g;if((q|0)==(c[19]|0)){c[16]=g;return}}else{c[a>>2]=b&-2;c[q+4>>2]=g|1;c[q+g>>2]=g}a=g>>>3;if(g>>>0<256){d=96+(a<<1<<2)|0;b=c[14]|0;a=1<<a;if(b&a){a=d+8|0;b=c[a>>2]|0;if(b>>>0<(c[18]|0)>>>0)fa();else{o=a;p=b}}else{c[14]=b|a;o=d+8|0;p=d}c[o>>2]=q;c[p+12>>2]=q;c[q+8>>2]=p;c[q+12>>2]=d;return}a=g>>>8;if(a)if(g>>>0>16777215)d=31;else{o=(a+1048320|0)>>>16&8;p=a<<o;n=(p+520192|0)>>>16&4;p=p<<n;d=(p+245760|0)>>>16&2;d=14-(n|o|d)+(p<<d>>>15)|0;d=g>>>(d+7|0)&1|d<<1}else d=0;e=360+(d<<2)|0;c[q+28>>2]=d;c[q+20>>2]=0;c[q+16>>2]=0;a=c[15]|0;b=1<<d;do if(a&b){f=g<<((d|0)==31?0:25-(d>>>1)|0);a=c[e>>2]|0;while(1){if((c[a+4>>2]&-8|0)==(g|0)){d=a;e=130;break}b=a+16+(f>>>31<<2)|0;d=c[b>>2]|0;if(!d){e=127;break}else{f=f<<1;a=d}}if((e|0)==127)if(b>>>0<(c[18]|0)>>>0)fa();else{c[b>>2]=q;c[q+24>>2]=a;c[q+12>>2]=q;c[q+8>>2]=q;break}else if((e|0)==130){a=d+8|0;b=c[a>>2]|0;p=c[18]|0;if(b>>>0>=p>>>0&d>>>0>=p>>>0){c[b+12>>2]=q;c[a>>2]=q;c[q+8>>2]=b;c[q+12>>2]=d;c[q+24>>2]=0;break}else fa()}}else{c[15]=a|b;c[e>>2]=q;c[q+24>>2]=e;c[q+12>>2]=q;c[q+8>>2]=q}while(0);q=(c[22]|0)+-1|0;c[22]=q;if(!q)a=512;else return;while(1){a=c[a>>2]|0;if(!a)break;else a=a+8|0}c[22]=-1;return}function Ea(){}function Fa(b,d,e){b=b|0;d=d|0;e=e|0;var f=0,g=0,h=0,i=0;f=b+e|0;if((e|0)>=20){d=d&255;h=b&3;i=d|d<<8|d<<16|d<<24;g=f&~3;if(h){h=b+4-h|0;while((b|0)<(h|0)){a[b>>0]=d;b=b+1|0}}while((b|0)<(g|0)){c[b>>2]=i;b=b+4|0}}while((b|0)<(f|0)){a[b>>0]=d;b=b+1|0}return b-e|0}function Ga(b,d,e){b=b|0;d=d|0;e=e|0;var f=0;if((e|0)>=4096)return ja(b|0,d|0,e|0)|0;f=b|0;if((b&3)==(d&3)){while(b&3){if(!e)return f|0;a[b>>0]=a[d>>0]|0;b=b+1|0;d=d+1|0;e=e-1|0}while((e|0)>=4){c[b>>2]=c[d>>2];b=b+4|0;d=d+4|0;e=e-4|0}}while((e|0)>0){a[b>>0]=a[d>>0]|0;b=b+1|0;d=d+1|0;e=e-1|0}return f|0}
+
+// EMSCRIPTEN_END_FUNCS
+return{_malloc:Ca,_free:Da,_transform_radix2_precalc_f:Aa,_memset:Fa,_precalc_f:wa,_precalc:va,_dispose:xa,_memcpy:Ga,_transform_radix2_precalc:za,_dispose_f:ya,runPostSets:Ea,stackAlloc:ma,stackSave:na,stackRestore:oa,establishStackSpace:pa,setThrew:qa,setTempRet0:ta,getTempRet0:ua}})
+
+
+// EMSCRIPTEN_END_ASM
+(Module.asmGlobalArg,Module.asmLibraryArg,buffer);var _precalc_f=Module["_precalc_f"]=asm["_precalc_f"];var _free=Module["_free"]=asm["_free"];var runPostSets=Module["runPostSets"]=asm["runPostSets"];var _transform_radix2_precalc_f=Module["_transform_radix2_precalc_f"]=asm["_transform_radix2_precalc_f"];var _memset=Module["_memset"]=asm["_memset"];var _malloc=Module["_malloc"]=asm["_malloc"];var _precalc=Module["_precalc"]=asm["_precalc"];var _dispose=Module["_dispose"]=asm["_dispose"];var _memcpy=Module["_memcpy"]=asm["_memcpy"];var _transform_radix2_precalc=Module["_transform_radix2_precalc"]=asm["_transform_radix2_precalc"];var _dispose_f=Module["_dispose_f"]=asm["_dispose_f"];Runtime.stackAlloc=asm["stackAlloc"];Runtime.stackSave=asm["stackSave"];Runtime.stackRestore=asm["stackRestore"];Runtime.establishStackSpace=asm["establishStackSpace"];Runtime.setTempRet0=asm["setTempRet0"];Runtime.getTempRet0=asm["getTempRet0"];function ExitStatus(status){this.name="ExitStatus";this.message="Program terminated with exit("+status+")";this.status=status}ExitStatus.prototype=new Error;ExitStatus.prototype.constructor=ExitStatus;var initialStackTop;var preloadStartTime=null;var calledMain=false;dependenciesFulfilled=function runCaller(){if(!Module["calledRun"])run();if(!Module["calledRun"])dependenciesFulfilled=runCaller};Module["callMain"]=Module.callMain=function callMain(args){assert(runDependencies==0,"cannot call main when async dependencies remain! (listen on __ATMAIN__)");assert(__ATPRERUN__.length==0,"cannot call main when preRun functions remain to be called");args=args||[];ensureInitRuntime();var argc=args.length+1;function pad(){for(var i=0;i<4-1;i++){argv.push(0)}}var argv=[allocate(intArrayFromString(Module["thisProgram"]),"i8",ALLOC_NORMAL)];pad();for(var i=0;i<argc-1;i=i+1){argv.push(allocate(intArrayFromString(args[i]),"i8",ALLOC_NORMAL));pad()}argv.push(0);argv=allocate(argv,"i32",ALLOC_NORMAL);try{var ret=Module["_main"](argc,argv,0);exit(ret,true)}catch(e){if(e instanceof ExitStatus){return}else if(e=="SimulateInfiniteLoop"){Module["noExitRuntime"]=true;return}else{if(e&&typeof e==="object"&&e.stack)Module.printErr("exception thrown: "+[e,e.stack]);throw e}}finally{calledMain=true}};function run(args){args=args||Module["arguments"];if(preloadStartTime===null)preloadStartTime=Date.now();if(runDependencies>0){return}preRun();if(runDependencies>0)return;if(Module["calledRun"])return;function doRun(){if(Module["calledRun"])return;Module["calledRun"]=true;if(ABORT)return;ensureInitRuntime();preMain();if(Module["onRuntimeInitialized"])Module["onRuntimeInitialized"]();if(Module["_main"]&&shouldRunNow)Module["callMain"](args);postRun()}if(Module["setStatus"]){Module["setStatus"]("Running...");setTimeout((function(){setTimeout((function(){Module["setStatus"]("")}),1);doRun()}),1)}else{doRun()}}Module["run"]=Module.run=run;function exit(status,implicit){if(implicit&&Module["noExitRuntime"]){return}if(Module["noExitRuntime"]){}else{ABORT=true;EXITSTATUS=status;STACKTOP=initialStackTop;exitRuntime();if(Module["onExit"])Module["onExit"](status)}if(ENVIRONMENT_IS_NODE){process["stdout"]["once"]("drain",(function(){process["exit"](status)}));console.log(" ");setTimeout((function(){process["exit"](status)}),500)}else if(ENVIRONMENT_IS_SHELL&&typeof quit==="function"){quit(status)}throw new ExitStatus(status)}Module["exit"]=Module.exit=exit;var abortDecorators=[];function abort(what){if(what!==undefined){Module.print(what);Module.printErr(what);what=JSON.stringify(what)}else{what=""}ABORT=true;EXITSTATUS=1;var extra="\nIf this abort() is unexpected, build with -s ASSERTIONS=1 which can give more information.";var output="abort("+what+") at "+stackTrace()+extra;if(abortDecorators){abortDecorators.forEach((function(decorator){output=decorator(output,what)}))}throw output}Module["abort"]=Module.abort=abort;if(Module["preInit"]){if(typeof Module["preInit"]=="function")Module["preInit"]=[Module["preInit"]];while(Module["preInit"].length>0){Module["preInit"].pop()()}}var shouldRunNow=true;if(Module["noInitialRun"]){shouldRunNow=false}run()
+
+
+
+
+
+  return Module;
+};
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/nayukic/fft.c	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,461 @@
+/* 
+ * Free FFT and convolution (C)
+ * 
+ * Copyright (c) 2014 Project Nayuki
+ * http://www.nayuki.io/page/free-small-fft-in-multiple-languages
+ * 
+ * (MIT License)
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ * - The above copyright notice and this permission notice shall be included in
+ *   all copies or substantial portions of the Software.
+ * - The Software is provided "as is", without warranty of any kind, express or
+ *   implied, including but not limited to the warranties of merchantability,
+ *   fitness for a particular purpose and noninfringement. In no event shall the
+ *   authors or copyright holders be liable for any claim, damages or other
+ *   liability, whether in an action of contract, tort or otherwise, arising from,
+ *   out of or in connection with the Software or the use or other dealings in the
+ *   Software.
+ */
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "fft.h"
+
+
+// Private function prototypes
+static size_t reverse_bits(size_t x, unsigned int n);
+static void *memdup(const void *src, size_t n);
+
+#define SIZE_MAX ((size_t)-1)
+
+
+int transform(double real[], double imag[], size_t n) {
+	if (n == 0)
+		return 1;
+	else if ((n & (n - 1)) == 0)  // Is power of 2
+		return transform_radix2(real, imag, n);
+	else  // More complicated algorithm for arbitrary sizes
+		return transform_bluestein(real, imag, n);
+}
+
+
+int inverse_transform(double real[], double imag[], size_t n) {
+	return transform(imag, real, n);
+}
+
+tables *precalc(size_t n) {
+    unsigned int levels;
+    // Compute levels = floor(log2(n))
+    {
+	size_t temp = n;
+	levels = 0;
+	while (temp > 1) {
+	    levels++;
+	    temp >>= 1;
+	}
+	if (1u << levels != n)
+	    return 0;  // n is not a power of 2
+    }
+    if (SIZE_MAX / sizeof(double) < n / 2) return 0;
+    tables *tables = malloc(sizeof(tables));
+    if (!tables) return tables;
+    tables->levels = levels;
+    size_t size = (n / 2) * sizeof(double);
+    tables->cos = malloc(size);
+    if (!tables->cos) {
+	free(tables);
+	return 0;
+    }
+    tables->sin = malloc(size);
+    if (!tables->sin) {
+	free(tables->cos);
+	free(tables);
+	return 0;
+    }
+    int i;
+    for (i = 0; i < n / 2; i++) {
+	tables->cos[i] = cos(2 * M_PI * i / n);
+	tables->sin[i] = sin(2 * M_PI * i / n);
+    }
+    return tables;
+}
+
+tables_f *precalc_f(size_t n) {
+    unsigned int levels;
+    // Compute levels = floor(log2(n))
+    {
+	size_t temp = n;
+	levels = 0;
+	while (temp > 1) {
+	    levels++;
+	    temp >>= 1;
+	}
+	if (1u << levels != n)
+	    return 0;  // n is not a power of 2
+    }
+    if (SIZE_MAX / sizeof(float) < n / 2) return 0;
+    tables_f *tables = malloc(sizeof(tables_f));
+    if (!tables) return tables;
+    tables->levels = levels;
+    size_t size = (n / 2) * sizeof(float);
+    tables->cos = malloc(size);
+    if (!tables->cos) {
+	free(tables);
+	return 0;
+    }
+    tables->sin = malloc(size);
+    if (!tables->sin) {
+	free(tables->cos);
+	free(tables);
+	return 0;
+    }
+    int i;
+    for (i = 0; i < n / 2; i++) {
+	tables->cos[i] = cos(2 * M_PI * i / n);
+	tables->sin[i] = sin(2 * M_PI * i / n);
+    }
+    return tables;
+}
+
+void dispose(tables *tables) {
+    if (!tables) return;
+    free(tables->cos);
+    free(tables->sin);
+    free(tables);
+}
+
+void dispose_f(tables_f *tables) {
+    if (!tables) return;
+    free(tables->cos);
+    free(tables->sin);
+    free(tables);
+}
+
+void transform_radix2_precalc(double real[], double imag[], int n, tables *tables) {
+	double *cos_table, *sin_table;
+	int size;
+	int i;
+	
+	// Trignometric tables
+	cos_table = tables->cos;
+	sin_table = tables->sin;
+
+	// Bit-reversed addressing permutation
+	for (i = 0; i < n; i++) {
+		int j = reverse_bits(i, tables->levels);
+		if (j > i) {
+			double temp = real[i];
+			real[i] = real[j];
+			real[j] = temp;
+			temp = imag[i];
+			imag[i] = imag[j];
+			imag[j] = temp;
+		}
+	}
+	
+	// Cooley-Tukey decimation-in-time radix-2 FFT
+	for (size = 2; size <= n; size *= 2) {
+		int halfsize = size / 2;
+		int tablestep = n / size;
+		for (i = 0; i < n; i += size) {
+			int j;
+			int k;
+			for (j = i, k = 0; j < i + halfsize; j++, k += tablestep) {
+				double tpre =  real[j+halfsize] * cos_table[k] + imag[j+halfsize] * sin_table[k];
+				double tpim = -real[j+halfsize] * sin_table[k] + imag[j+halfsize] * cos_table[k];
+				real[j + halfsize] = real[j] - tpre;
+				imag[j + halfsize] = imag[j] - tpim;
+				real[j] += tpre;
+				imag[j] += tpim;
+			}
+		}
+		if (size == n)  // Prevent overflow in 'size *= 2'
+			break;
+	}
+}
+
+void transform_radix2_precalc_f(float real[], float imag[], int n, tables_f *tables) {
+	float *cos_table, *sin_table;
+	int size;
+	int i;
+	
+	// Trignometric tables
+	cos_table = tables->cos;
+	sin_table = tables->sin;
+
+	// Bit-reversed addressing permutation
+	for (i = 0; i < n; i++) {
+		int j = reverse_bits(i, tables->levels);
+		if (j > i) {
+			float temp = real[i];
+			real[i] = real[j];
+			real[j] = temp;
+			temp = imag[i];
+			imag[i] = imag[j];
+			imag[j] = temp;
+		}
+	}
+	
+	// Cooley-Tukey decimation-in-time radix-2 FFT
+	for (size = 2; size <= n; size *= 2) {
+		int halfsize = size / 2;
+		int tablestep = n / size;
+		for (i = 0; i < n; i += size) {
+			int j;
+			int k;
+			for (j = i, k = 0; j < i + halfsize; j++, k += tablestep) {
+				float tpre =  real[j+halfsize] * cos_table[k] + imag[j+halfsize] * sin_table[k];
+				float tpim = -real[j+halfsize] * sin_table[k] + imag[j+halfsize] * cos_table[k];
+				real[j + halfsize] = real[j] - tpre;
+				imag[j + halfsize] = imag[j] - tpim;
+				real[j] += tpre;
+				imag[j] += tpim;
+			}
+		}
+		if (size == n)  // Prevent overflow in 'size *= 2'
+			break;
+	}
+}
+
+int transform_radix2(double real[], double imag[], size_t n) {
+	// Variables
+	int status = 0;
+	unsigned int levels;
+	double *cos_table, *sin_table;
+	size_t size;
+	size_t i;
+	
+	// Compute levels = floor(log2(n))
+	{
+		size_t temp = n;
+		levels = 0;
+		while (temp > 1) {
+			levels++;
+			temp >>= 1;
+		}
+		if (1u << levels != n)
+			return 0;  // n is not a power of 2
+	}
+	
+	// Trignometric tables
+	if (SIZE_MAX / sizeof(double) < n / 2)
+		return 0;
+	size = (n / 2) * sizeof(double);
+	cos_table = malloc(size);
+	sin_table = malloc(size);
+	if (cos_table == NULL || sin_table == NULL)
+		goto cleanup;
+	for (i = 0; i < n / 2; i++) {
+		cos_table[i] = cos(2 * M_PI * i / n);
+		sin_table[i] = sin(2 * M_PI * i / n);
+	}
+	
+	// Bit-reversed addressing permutation
+	for (i = 0; i < n; i++) {
+		size_t j = reverse_bits(i, levels);
+		if (j > i) {
+			double temp = real[i];
+			real[i] = real[j];
+			real[j] = temp;
+			temp = imag[i];
+			imag[i] = imag[j];
+			imag[j] = temp;
+		}
+	}
+	
+	// Cooley-Tukey decimation-in-time radix-2 FFT
+	for (size = 2; size <= n; size *= 2) {
+		size_t halfsize = size / 2;
+		size_t tablestep = n / size;
+		for (i = 0; i < n; i += size) {
+			size_t j;
+			size_t k;
+			for (j = i, k = 0; j < i + halfsize; j++, k += tablestep) {
+				double tpre =  real[j+halfsize] * cos_table[k] + imag[j+halfsize] * sin_table[k];
+				double tpim = -real[j+halfsize] * sin_table[k] + imag[j+halfsize] * cos_table[k];
+				real[j + halfsize] = real[j] - tpre;
+				imag[j + halfsize] = imag[j] - tpim;
+				real[j] += tpre;
+				imag[j] += tpim;
+			}
+		}
+		if (size == n)  // Prevent overflow in 'size *= 2'
+			break;
+	}
+	status = 1;
+	
+cleanup:
+	free(sin_table);
+	free(cos_table);
+	return status;
+}
+
+
+int transform_bluestein(double real[], double imag[], size_t n) {
+	// Variables
+	int status = 0;
+	double *cos_table, *sin_table;
+	double *areal, *aimag;
+	double *breal, *bimag;
+	double *creal, *cimag;
+	size_t m;
+	size_t size_n, size_m;
+	size_t i;
+	
+	// Find a power-of-2 convolution length m such that m >= n * 2 + 1
+	{
+		size_t target;
+		if (n > (SIZE_MAX - 1) / 2)
+			return 0;
+		target = n * 2 + 1;
+		for (m = 1; m < target; m *= 2) {
+			if (SIZE_MAX / 2 < m)
+				return 0;
+		}
+	}
+	
+	// Allocate memory
+	if (SIZE_MAX / sizeof(double) < n || SIZE_MAX / sizeof(double) < m)
+		return 0;
+	size_n = n * sizeof(double);
+	size_m = m * sizeof(double);
+	cos_table = malloc(size_n);
+	sin_table = malloc(size_n);
+	areal = calloc(m, sizeof(double));
+	aimag = calloc(m, sizeof(double));
+	breal = calloc(m, sizeof(double));
+	bimag = calloc(m, sizeof(double));
+	creal = malloc(size_m);
+	cimag = malloc(size_m);
+	if (cos_table == NULL || sin_table == NULL
+			|| areal == NULL || aimag == NULL
+			|| breal == NULL || bimag == NULL
+			|| creal == NULL || cimag == NULL)
+		goto cleanup;
+	
+	// Trignometric tables
+	for (i = 0; i < n; i++) {
+		double temp = M_PI * (size_t)((unsigned long long)i * i % ((unsigned long long)n * 2)) / n;
+		// Less accurate version if long long is unavailable: double temp = M_PI * i * i / n;
+		cos_table[i] = cos(temp);
+		sin_table[i] = sin(temp);
+	}
+	
+	// Temporary vectors and preprocessing
+	for (i = 0; i < n; i++) {
+		areal[i] =  real[i] * cos_table[i] + imag[i] * sin_table[i];
+		aimag[i] = -real[i] * sin_table[i] + imag[i] * cos_table[i];
+	}
+	breal[0] = cos_table[0];
+	bimag[0] = sin_table[0];
+	for (i = 1; i < n; i++) {
+		breal[i] = breal[m - i] = cos_table[i];
+		bimag[i] = bimag[m - i] = sin_table[i];
+	}
+	
+	// Convolution
+	if (!convolve_complex(areal, aimag, breal, bimag, creal, cimag, m))
+		goto cleanup;
+	
+	// Postprocessing
+	for (i = 0; i < n; i++) {
+		real[i] =  creal[i] * cos_table[i] + cimag[i] * sin_table[i];
+		imag[i] = -creal[i] * sin_table[i] + cimag[i] * cos_table[i];
+	}
+	status = 1;
+	
+	// Deallocation
+cleanup:
+	free(cimag);
+	free(creal);
+	free(bimag);
+	free(breal);
+	free(aimag);
+	free(areal);
+	free(sin_table);
+	free(cos_table);
+	return status;
+}
+
+
+int convolve_real(const double x[], const double y[], double out[], size_t n) {
+	double *ximag, *yimag, *zimag;
+	int status = 0;
+	ximag = calloc(n, sizeof(double));
+	yimag = calloc(n, sizeof(double));
+	zimag = calloc(n, sizeof(double));
+	if (ximag == NULL || yimag == NULL || zimag == NULL)
+		goto cleanup;
+	
+	status = convolve_complex(x, ximag, y, yimag, out, zimag, n);
+cleanup:
+	free(zimag);
+	free(yimag);
+	free(ximag);
+	return status;
+}
+
+
+int convolve_complex(const double xreal[], const double ximag[], const double yreal[], const double yimag[], double outreal[], double outimag[], size_t n) {
+	int status = 0;
+	size_t size;
+	size_t i;
+	double *xr, *xi, *yr, *yi;
+	if (SIZE_MAX / sizeof(double) < n)
+		return 0;
+	size = n * sizeof(double);
+	xr = memdup(xreal, size);
+	xi = memdup(ximag, size);
+	yr = memdup(yreal, size);
+	yi = memdup(yimag, size);
+	if (xr == NULL || xi == NULL || yr == NULL || yi == NULL)
+		goto cleanup;
+	
+	if (!transform(xr, xi, n))
+		goto cleanup;
+	if (!transform(yr, yi, n))
+		goto cleanup;
+	for (i = 0; i < n; i++) {
+		double temp = xr[i] * yr[i] - xi[i] * yi[i];
+		xi[i] = xi[i] * yr[i] + xr[i] * yi[i];
+		xr[i] = temp;
+	}
+	if (!inverse_transform(xr, xi, n))
+		goto cleanup;
+	for (i = 0; i < n; i++) {  // Scaling (because this FFT implementation omits it)
+		outreal[i] = xr[i] / n;
+		outimag[i] = xi[i] / n;
+	}
+	status = 1;
+	
+cleanup:
+	free(yi);
+	free(yr);
+	free(xi);
+	free(xr);
+	return status;
+}
+
+
+static size_t reverse_bits(size_t x, unsigned int n) {
+	size_t result = 0;
+	unsigned int i;
+	for (i = 0; i < n; i++, x >>= 1)
+		result = (result << 1) | (x & 1);
+	return result;
+}
+
+
+static void *memdup(const void *src, size_t n) {
+	void *dest = malloc(n);
+	if (dest != NULL)
+		memcpy(dest, src, n);
+	return dest;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/nayukic/fft.h	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,86 @@
+/* 
+ * Free FFT and convolution (C)
+ * 
+ * Copyright (c) 2014 Project Nayuki
+ * http://www.nayuki.io/page/free-small-fft-in-multiple-languages
+ * 
+ * (MIT License)
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ * - The above copyright notice and this permission notice shall be included in
+ *   all copies or substantial portions of the Software.
+ * - The Software is provided "as is", without warranty of any kind, express or
+ *   implied, including but not limited to the warranties of merchantability,
+ *   fitness for a particular purpose and noninfringement. In no event shall the
+ *   authors or copyright holders be liable for any claim, damages or other
+ *   liability, whether in an action of contract, tort or otherwise, arising from,
+ *   out of or in connection with the Software or the use or other dealings in the
+ *   Software.
+ */
+
+
+/* 
+ * Computes the discrete Fourier transform (DFT) of the given complex vector, storing the result back into the vector.
+ * The vector can have any length. This is a wrapper function. Returns 1 (true) if successful, 0 (false) otherwise (out of memory).
+ */
+int transform(double real[], double imag[], size_t n);
+
+/* 
+ * Computes the inverse discrete Fourier transform (IDFT) of the given complex vector, storing the result back into the vector.
+ * The vector can have any length. This is a wrapper function. This transform does not perform scaling, so the inverse is not a true inverse.
+ * Returns 1 (true) if successful, 0 (false) otherwise (out of memory).
+ */
+int inverse_transform(double real[], double imag[], size_t n);
+
+/* 
+ * Computes the discrete Fourier transform (DFT) of the given complex vector, storing the result back into the vector.
+ * The vector's length must be a power of 2. Uses the Cooley-Tukey decimation-in-time radix-2 algorithm.
+ * Returns 1 (true) if successful, 0 (false) otherwise (n is not a power of 2, or out of memory).
+ */
+int transform_radix2(double real[], double imag[], size_t n);
+
+/* Test versions with precalculated structures -- this API is
+   absolutely not for production use! */
+typedef struct {
+    double *cos;
+    double *sin;
+    int levels;
+} tables;
+
+tables *precalc(size_t n);
+void dispose(tables *);
+void transform_radix2_precalc(double real[], double imag[], int n, tables *tables);
+
+typedef struct {
+    float *cos;
+    float *sin;
+    int levels;
+} tables_f;
+
+tables_f *precalc_f(size_t n);
+void dispose_f(tables_f *);
+void transform_radix2_precalc_f(float real[], float imag[], int n, tables_f *tables);
+
+/* 
+ * Computes the discrete Fourier transform (DFT) of the given complex vector, storing the result back into the vector.
+ * The vector can have any length. This requires the convolution function, which in turn requires the radix-2 FFT function.
+ * Uses Bluestein's chirp z-transform algorithm. Returns 1 (true) if successful, 0 (false) otherwise (out of memory).
+ */
+int transform_bluestein(double real[], double imag[], size_t n);
+
+/* 
+ * Computes the circular convolution of the given real vectors. Each vector's length must be the same.
+ * Returns 1 (true) if successful, 0 (false) otherwise (out of memory).
+ */
+int convolve_real(const double x[], const double y[], double out[], size_t n);
+
+/* 
+ * Computes the circular convolution of the given complex vectors. Each vector's length must be the same.
+ * Returns 1 (true) if successful, 0 (false) otherwise (out of memory).
+ */
+int convolve_complex(const double xreal[], const double ximag[], const double yreal[], const double yimag[], double outreal[], double outimag[], size_t n);
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fft/nayukic/ffttest.c	Tue Nov 10 07:30:50 2015 +0000
@@ -0,0 +1,218 @@
+/* 
+ * FFT and convolution test (C)
+ * 
+ * Copyright (c) 2014 Project Nayuki
+ * http://www.nayuki.io/page/free-small-fft-in-multiple-languages
+ * 
+ * (MIT License)
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+ * the Software, and to permit persons to whom the Software is furnished to do so,
+ * subject to the following conditions:
+ * - The above copyright notice and this permission notice shall be included in
+ *   all copies or substantial portions of the Software.
+ * - The Software is provided "as is", without warranty of any kind, express or
+ *   implied, including but not limited to the warranties of merchantability,
+ *   fitness for a particular purpose and noninfringement. In no event shall the
+ *   authors or copyright holders be liable for any claim, damages or other
+ *   liability, whether in an action of contract, tort or otherwise, arising from,
+ *   out of or in connection with the Software or the use or other dealings in the
+ *   Software.
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "fft.h"
+
+
+// Private function prototypes
+static void test_fft(int n);
+static void test_convolution(int n);
+static void naive_dft(const double *inreal, const double *inimag, double *outreal, double *outimag, int inverse, int n);
+static void naive_convolve(const double *xreal, const double *ximag, const double *yreal, const double *yimag, double *outreal, double *outimag, int n);
+static double log10_rms_err(const double *xreal, const double *ximag, const double *yreal, const double *yimag, int n);
+static double *random_reals(int n);
+static void *memdup(const void *src, size_t n);
+
+static double max_log_error = -INFINITY;
+
+
+/* Main and test functions */
+
+int main(int argc, char **argv) {
+	int i;
+	int prev;
+	srand(time(NULL));
+	
+	// Test power-of-2 size FFTs
+	for (i = 0; i <= 12; i++)
+		test_fft(1 << i);
+	
+	// Test small size FFTs
+	for (i = 0; i < 30; i++)
+		test_fft(i);
+	
+	// Test diverse size FFTs
+	prev = 0;
+	for (i = 0; i <= 100; i++) {
+		int n = (int)lround(pow(1500, i / 100.0));
+		if (n > prev) {
+			test_fft(n);
+			prev = n;
+		}
+	}
+	
+	// Test power-of-2 size convolutions
+	for (i = 0; i <= 12; i++)
+		test_convolution(1 << i);
+	
+	// Test diverse size convolutions
+	prev = 0;
+	for (i = 0; i <= 100; i++) {
+		int n = (int)lround(pow(1500, i / 100.0));
+		if (n > prev) {
+			test_convolution(n);
+			prev = n;
+		}
+	}
+	
+	printf("\n");
+	printf("Max log err = %.1f\n", max_log_error);
+	printf("Test %s\n", max_log_error < -10 ? "passed" : "failed");
+	return 0;
+}
+
+
+static void test_fft(int n) {
+	double *inputreal, *inputimag;
+	double *refoutreal, *refoutimag;
+	double *actualoutreal, *actualoutimag;
+	
+	inputreal = random_reals(n);
+	inputimag = random_reals(n);
+	
+	refoutreal = malloc(n * sizeof(double));
+	refoutimag = malloc(n * sizeof(double));
+	naive_dft(inputreal, inputimag, refoutreal, refoutimag, 0, n);
+	
+	actualoutreal = memdup(inputreal, n * sizeof(double));
+	actualoutimag = memdup(inputimag, n * sizeof(double));
+	transform(actualoutreal, actualoutimag, n);
+	
+	printf("fftsize=%4d  logerr=%5.1f\n", n, log10_rms_err(refoutreal, refoutimag, actualoutreal, actualoutimag, n));
+	
+	free(inputreal);
+	free(inputimag);
+	free(refoutreal);
+	free(refoutimag);
+	free(actualoutreal);
+	free(actualoutimag);
+}
+
+
+static void test_convolution(int n) {
+	double *input0real, *input0imag;
+	double *input1real, *input1imag;
+	double *refoutreal, *refoutimag;
+	double *actualoutreal, *actualoutimag;
+	
+	input0real = random_reals(n);
+	input0imag = random_reals(n);
+	input1real = random_reals(n);
+	input1imag = random_reals(n);
+	
+	refoutreal = malloc(n * sizeof(double));
+	refoutimag = malloc(n * sizeof(double));
+	naive_convolve(input0real, input0imag, input1real, input1imag, refoutreal, refoutimag, n);
+	
+	actualoutreal = malloc(n * sizeof(double));
+	actualoutimag = malloc(n * sizeof(double));
+	convolve_complex(input0real, input0imag, input1real, input1imag, actualoutreal, actualoutimag, n);
+	
+	printf("convsize=%4d  logerr=%5.1f\n", n, log10_rms_err(refoutreal, refoutimag, actualoutreal, actualoutimag, n));
+	
+	free(input0real);
+	free(input0imag);
+	free(input1real);
+	free(input1imag);
+	free(refoutreal);
+	free(refoutimag);
+	free(actualoutreal);
+	free(actualoutimag);
+}
+
+
+/* Naive reference computation functions */
+
+static void naive_dft(const double *inreal, const double *inimag, double *outreal, double *outimag, int inverse, int n) {
+	double coef = (inverse ? 2 : -2) * M_PI;
+	int k;
+	for (k = 0; k < n; k++) {  // For each output element
+		double sumreal = 0;
+		double sumimag = 0;
+		int t;
+		for (t = 0; t < n; t++) {  // For each input element
+			double angle = coef * ((long long)t * k % n) / n;
+			sumreal += inreal[t]*cos(angle) - inimag[t]*sin(angle);
+			sumimag += inreal[t]*sin(angle) + inimag[t]*cos(angle);
+		}
+		outreal[k] = sumreal;
+		outimag[k] = sumimag;
+	}
+}
+
+
+static void naive_convolve(const double *xreal, const double *ximag, const double *yreal, const double *yimag, double *outreal, double *outimag, int n) {
+	int i;
+	for (i = 0; i < n; i++) {
+		double sumreal = 0;
+		double sumimag = 0;
+		int j;
+		for (j = 0; j < n; j++) {
+			int k = (i - j + n) % n;
+			sumreal += xreal[k] * yreal[j] - ximag[k] * yimag[j];
+			sumimag += xreal[k] * yimag[j] + ximag[k] * yreal[j];
+		}
+		outreal[i] = sumreal;
+		outimag[i] = sumimag;
+	}
+}
+
+
+/* Utility functions */
+
+static double log10_rms_err(const double *xreal, const double *ximag, const double *yreal, const double *yimag, int n) {
+	double err = 0;
+	int i;
+	for (i = 0; i < n; i++)
+		err += (xreal[i] - yreal[i]) * (xreal[i] - yreal[i]) + (ximag[i] - yimag[i]) * (ximag[i] - yimag[i]);
+	
+	err /= n > 0 ? n : 1;
+	err = sqrt(err);  // Now this is a root mean square (RMS) error
+	err = err > 0 ? log10(err) : -99.0;
+	if (err > max_log_error)
+		max_log_error = err;
+	return err;
+}
+
+
+static double *random_reals(int n) {
+	double *result = malloc(n * sizeof(double));
+	int i;
+	for (i = 0; i < n; i++)
+		result[i] = (rand() / (RAND_MAX + 1.0)) * 2 - 1;
+	return result;
+}
+
+
+static void *memdup(const void *src, size_t n) {
+	void *dest = malloc(n);
+	if (dest != NULL)
+		memcpy(dest, src, n);
+	return dest;
+}
--- a/fft/test.js	Fri Oct 16 17:18:45 2015 +0100
+++ b/fft/test.js	Tue Nov 10 07:30:50 2015 +0000
@@ -103,6 +103,35 @@
     report("nayukiobj", start, middle, end, total);
 }
 
+function testNayukiC(size) {
+
+    var fft = new FFTNayukiC(size);
+    
+    var start = performance.now();
+    var middle = start;
+    var end = start;
+
+    total = 0.0;
+
+    for (var i = 0; i < 2*iterations; ++i) {
+	if (i == iterations) {
+	    middle = performance.now();
+	}
+	var real = inputReal64s(size);
+	var imag = zeroReal64s(size);
+	fft.forward(real, imag);
+	for (var j = 0; j < size; ++j) {
+	    total += Math.sqrt(real[j] * real[j] + imag[j] * imag[j]);
+	}
+    }
+    
+    var end = performance.now();
+
+    fft.dispose();
+    
+    report("nayukic", start, middle, end, total);
+}
+
 function testNockert(size) {
     
     var fft = new FFT.complex(size, false);
@@ -252,7 +281,8 @@
 }
 
 var sizes = [ 512, 2048 ];
-var tests = [ testNayuki, testNayukiObj, testKissFFT, testCross, testFFTW, testNockert, testDntj ];
+var tests = [ testNayuki, testNayukiObj, testNayukiC, testKissFFT,
+	      testCross, testFFTW, testNockert, testDntj ];
 var nextTest = 0;
 var nextSize = 0;
 var interval;