/********************************************************************
 *                                                                  *
 * THIS FILE IS PART OF THE OggVorbis SOFTWARE CODEC SOURCE CODE.   *
 * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
 * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
 * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
 *                                                                  *
 * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002             *
 * by the Xiph.Org Foundation http://www.xiph.org/                  *
 *                                                                  *
 ********************************************************************/

package org.xiph.libvorbis;

import org.xiph.libogg.*;
import static org.xiph.libvorbis.vorbis_constants.integer_constants.*;


public class vorbis_block {

	// necessary stream state for linking to the framing abstraction

	float[][] pcm;		 // float **pcm // this is a pointer into local storage
	int pcm_offset;
	oggpack_buffer opb;
  
	int lW;			// long  
	int W;			// long  
	int nW;			// long
	int pcmend;
	int mode;

	int eofflag;
	int granulepos;		// ogg_int64_t 
	int sequence;		// ogg_int64_t
	vorbis_dsp_state vd; 	// *vd // For read-only access of configuration 

	// local storage to avoid remallocing; it's up to the mapping to structure it
	
	Object[] localstore;
	int localtop;		// long
	int localalloc;		// long
	int totaluse;		// long

	alloc_chain reap;

	// bitmetrics for the frame
	int glue_bits;		// long
	int time_bits;		// long
	int floor_bits;		// long
	int res_bits;		// long

	vorbis_block_internal internal;	// void *internal;
	
	// vorbis_window used to hold window arrays and functions
	vorbis_window window;

	
	public vorbis_block( vorbis_dsp_state v ) {

		vd = v;
		localalloc = 0;
		opb = new oggpack_buffer();

		if ( v.analysisp > 0 ) {

			internal = new vorbis_block_internal();
			internal.ampmax = -9999.0f;
			
			for ( int i=0; i < PACKETBLOBS; i++ ) {
				if ( i == PACKETBLOBS/2 ) {
					internal.packetblob[i] = opb;
				}
				else {
					internal.packetblob[i] = new oggpack_buffer();
				}
			}
		}
		
		window = new vorbis_window();
	}
	
	public int _vorbis_block_alloc( int bytes ) {
		
		bytes = (bytes+(WORD_ALIGN-1)) & ~(WORD_ALIGN-1);
		
		if ( bytes+localtop > localalloc ) {
			
		    // can't just _ogg_realloc... there are outstanding pointers 
			if ( localstore != null ) {
				alloc_chain link = new alloc_chain();
				totaluse += localtop;
				link.next = reap;
				// link.ptr = localstore;
				link.ptr = new Object[ localtop ];
				System.arraycopy( localstore, 0, link.ptr, 0, localtop );
				reap = link;
			}
		    // highly conservative
			localalloc = bytes;
			// localstore = _ogg_malloc(vb->localalloc);
			localstore = new Object[ localalloc ];
			localtop = 0;
		}
			
		// void *ret=(void *)(((char *)vb->localstore)+vb->localtop);
		int ret = localtop;
		localtop += bytes;
	
		return ret;
	}
	
	public void _vorbis_block_ripcord() {
		
		// reap the chain
		while ( reap != null ) {
			
			alloc_chain next = reap.next;
			// _ogg_free(reap->ptr);
			// memset(reap,0,sizeof(*reap));
			// _ogg_free(reap);
			reap = null;
		    reap = next;
		}

		// consolidate storage
		if ( totaluse > 0 ) {
			// localstore =_ogg_realloc(vb->localstore,vb->totaluse+vb->localalloc);
			Object[] temp = new Object[ totaluse + localalloc ];
			System.arraycopy( localstore, 0, temp, 0, localalloc );
			localstore = temp;
			localalloc += totaluse;
			totaluse = 0;
		}
		
		// pull the ripcord
		localtop = 0;
		reap = null;
	}
	
	public boolean vorbis_analysis_blockout( vorbis_dsp_state v ) {
		
		int i;
		codec_setup_info ci = v.vi.codec_setup;
		private_state b = v.backend_state;
		vorbis_look_psy_global g = b.psy_g_look;
		int beginW = v.centerW-ci.blocksizes[v.W]/2;
		int centerNext;
		vorbis_block_internal vbi = internal;
		
		// check to see if we're started...
		if ( v.preextrapolate <= 0 )
			return false;
		
		// check to see if we're done...
		if ( v.eofflag == -1 )
			return false;
		
		// By our invariant, we have lW, W and centerW set.  Search for
		// the next boundary so we can determine nW (the next window size)
		// which lets us compute the shape of the current block's window
		
		// we do an envelope search even on a single blocksize; we may still be throwing 
		// more bits at impulses, and envelope search handles  marking impulses too
		
		int bp = v._ve_envelope_search();
		if ( bp == -1 ) {
			
			if ( v.eofflag == 0 )
		    	  return false; // not enough data currently to search for a full long block
			v.nW=0;
		} else {
			
			if ( ci.blocksizes[0] == ci.blocksizes[1] )
				v.nW = 0;
			else
				v.nW = bp;
		}
		
		centerNext = v.centerW+ci.blocksizes[v.W]/4+ci.blocksizes[v.nW]/4;
		
		// center of next block + next block maximum right side.
		
		int blockbound = centerNext+ci.blocksizes[v.nW]/2;
		
		// not enough data yet; although this check is
		// less strict that the _ve_envelope_search, the 
		// search is not run if we only use one block size
		
		if ( v.pcm_current < blockbound )
			return false;
		
		// fill in the block.  Note that for a short window, lW and nW are *short* regardless of actual settings in the stream
		
		_vorbis_block_ripcord();
		lW = v.lW;
		W = v.W;
		nW = v.nW;
		
		if ( v.W > 0 ){
			if ( (v.lW <= 0) || (v.nW <= 0) ) {
				vbi.blocktype = BLOCKTYPE_TRANSITION;
			} else {
				vbi.blocktype = BLOCKTYPE_LONG;
			}
		} else {
			if ( v._ve_envelope_mark() ) {
				vbi.blocktype = BLOCKTYPE_IMPULSE;
			} else {
				vbi.blocktype = BLOCKTYPE_PADDING;
			}
		}
		
		vd = v;
		sequence = v.sequence++;
		granulepos = v.granulepos;
		pcmend = ci.blocksizes[v.W];
		
		// copy the vectors; this uses the local storage in vb
		
		// this tracks 'strongest peak' for later psychoacoustics
		// moved to the global psy state; clean this mess up
		if ( vbi.ampmax > g.ampmax )
			g.ampmax = vbi.ampmax;
		g.ampmax = v._vp_ampmax_decay( g.ampmax );
		vbi.ampmax = g.ampmax;
		
// TODO - CRITICAL - memory arrays
// we have some duplicate memory arrays probably increasing memory footprint
// would need to work on offset variables for pcmdelay
		
//		pcm = _vorbis_block_alloc(vb,sizeof(*pcm)*v.vi.channels);
		pcm = new float[ v.vi.channels ][];

//		vbi.pcmdelay = _vorbis_block_alloc(vb,sizeof(*vbi.pcmdelay)*v.vi.channels);
		vbi.pcmdelay = new float[ v.vi.channels ][];
		
		for ( i=0; i < v.vi.channels; i++ ) {
			
//			vbi.pcmdelay[i] = _vorbis_block_alloc(vb,(pcmend+beginW)*sizeof(*vbi.pcmdelay[i]));
			vbi.pcmdelay[i] = new float[ pcmend+beginW ];
			
//			memcpy( vbi.pcmdelay[i], v.pcm[i], (pcmend+beginW)*sizeof(*vbi.pcmdelay[i]) );
			System.arraycopy( v.pcm[i], 0, vbi.pcmdelay[i], 0, pcmend+beginW );
			
//			pcm[i] = vbi.pcmdelay[i]+beginW;
			pcm[i] = new float[ vbi.pcmdelay[i].length - beginW ];
			System.arraycopy( vbi.pcmdelay[i], beginW, pcm[i], 0, vbi.pcmdelay[i].length - beginW );
		}
		
		// handle eof detection: 
		//     eof==0 means that we've not yet received EOF
		//     eof>0  marks the last 'real' sample in pcm[]
		//     eof<0  'no more to do'; doesn't get here */
		
		if ( v.eofflag > 0) {
			if ( v.centerW >= v.eofflag ) {
				v.eofflag=-1;
				eofflag=1;
				return true;
			}
		}
		
		// advance storage vectors and clean up
		
		int new_centerNext = ci.blocksizes[1]/2;
		int movementW = centerNext-new_centerNext;
		
		if ( movementW > 0 ) {
			
			b.ve._ve_envelope_shift( movementW );
			v.pcm_current -= movementW;
			
			for (i=0; i < v.vi.channels; i++ ) {
				// memmove(v.pcm[i],v.pcm[i]+movementW,v.pcm_current*sizeof(*v.pcm[i]));
				System.arraycopy( v.pcm[i], movementW, v.pcm[i], 0, v.pcm_current );
			}
			
			v.lW = v.W;
			v.W = v.nW;
			v.centerW = new_centerNext;
			
			if ( v.eofflag > 0 ) {
				v.eofflag -= movementW;
				if ( v.eofflag <= 0 )
					v.eofflag = -1;
				// do not add padding to end of stream!
				if ( v.centerW >= v.eofflag ) {
					v.granulepos += movementW-(v.centerW-v.eofflag);
				} else {
					v.granulepos += movementW;
				}
			} else {
				v.granulepos += movementW;
			}
		}
		
		return true;
	}
	
	public boolean vorbis_bitrate_managed() {
		
		// if(bm->queue_binned)return(1);
		
		if ( vd.backend_state.bms.managed > 0 )
			return true;
		
		return false;
	}
	
	public boolean vorbis_analysis( ogg_packet op ) {
						
		glue_bits = 0;
		time_bits = 0;
		floor_bits = 0;
		res_bits = 0;
		
		// first things first.  Make sure encode is ready
		 for ( int i=0; i < PACKETBLOBS; i++ )
			 internal.packetblob[i].oggpack_reset();		  
		
		// we only have one mapping type (0), and we let the mapping code itself
		// figure out what soft mode to use.  This allows easier bitrate management

		mapping0_forward();
		
		if ( op != null ) {
			
			if ( vorbis_bitrate_managed() )
			      return false;	// The app is using a bitmanaged mode... but not using the bitrate management interface.

			op.packet = opb.buffer;
			op.bytes = opb.oggpack_bytes();
			op.b_o_s = 0;
			op.e_o_s = eofflag;
			op.granulepos = granulepos;
			op.packetno = sequence; // for sake of completeness
		}
		
		return true;
	}

	// the floor has already been filtered to only include relevant sections
	static int accumulate_fit( float[] flr, int mdct, int x0, int x1, lsfit_acc a, int n, vorbis_info_floor1 info ) {
		
		int i;
		// int quantized = vorbis_dBquant(flr[x0]);
		
		int xa=0, ya=0, x2a=0, y2a=0, xya=0, na=0, xb=0, yb=0, x2b=0, y2b=0, xyb=0, nb=0;

		a.x0=x0;
		a.x1=x1;
		if ( x1 >= n )
			x1 = n-1;
		
		for ( i=x0; i <= x1; i++ ) {
			
			int quantized = vorbis_dBquant(flr[i]);
			if (quantized > 0 ) {
				
				if ( flr[mdct+i]+info.twofitatten >= flr[i] ) {
					
					xa  += i;
					ya  += quantized;
					x2a += i*i;
					y2a += quantized*quantized;
					xya += i*quantized;
					na++;
					
				} else {
					
					xb  += i;
					yb  += quantized;
					x2b += i*i;
					y2b += quantized*quantized;
					xyb += i*quantized;
					nb++;
				}
			}
		}
		
		xb += xa;
		yb += ya;
		x2b += x2a;
		y2b += y2a;
		xyb += xya;
		nb += na;
		
		// weight toward the actually used frequencies if we meet the threshhold
		
		int weight = new Float( nb*info.twofitweight/(na+1) ).intValue();
		
		a.xa = xa*weight+xb;
		a.ya = ya*weight+yb;
		a.x2a = x2a*weight+x2b;
		a.y2a = y2a*weight+y2b;
		a.xya = xya*weight+xyb;
		a.an = na*weight+nb;
		
		return na;
	}
	
	static int[] fit_line( lsfit_acc[] a, int offset, int fits, int y0, int y1 ) {
		
		int x=0, y=0, x2=0, y2=0, xy=0, an=0, i;
		int x0 = a[offset].x0;
		int x1 = a[offset+fits-1].x1;
		
		for ( i=0; i < fits; i++ ) {
			
			x += a[offset+i].xa;
			y += a[offset+i].ya;
			x2 += a[offset+i].x2a;
			y2 += a[offset+i].y2a;
			xy += a[offset+i].xya;
			an += a[offset+i].an;
		}
		
		// if (*y0 >= 0 ) {
		if ( y0 >= 0 ) {
			
			x += x0;
		    y +=  y0;			// y +=  *y0;
		    x2 +=  x0 *  x0;
		    y2 += y0 * y0;	// y2 += *y0 * *y0;
		    xy += y0 *  x0;		// xy += *y0 *  x0;
		    an++;
		}

		// if(*y1>=0){
		if ( y1 >= 0 ) {
			x += x1;
			y +=  y1;			// y+=  *y1;
			x2 +=  x1 *  x1;
			y2 += y1 * y1;		// y2+= *y1 * *y1;
			xy += y1 *  x1;		// xy+= *y1 *  x1;
			an++;
		}
		
		if ( an > 0) {
			
			// need 64 bit multiplies, which C doesn't give portably as int
			double fx = x;
			double fy = y;
			double fx2 = x2;
			double fxy = xy;
			double denom = 1./(an*fx2-fx*fx);
			double a_local = (fy*fx2-fxy*fx)*denom;
			double b = (an*fxy-fx*fy)*denom;
		    y0 = (int)Math.rint(a_local+b*x0);		// *y0=rint(a_local+b*x0);
		    y1 = (int)Math.rint(a_local+b*x1);		// *y1=rint(a_local+b*x1);
		    
		    // limit to our range!
		    if ( y0 > 1023 )		// if(*y0>1023)*y0=1023;
		    	y0=1023;
		    if ( y1 > 1023 )		// if(*y1>1023)*y1=1023;
		    	y1=1023;
		    if ( y0 < 0 )		// if(*y0<0)*y0=0;
		    	y0=0;
		    if ( y1 < 0 )		// if(*y1<0)*y1=0;
		    	y1=0;
		} else {
		    y0 = 0;		// *y0=0;
		    y1 = 0;		// *y1=0;
		}
		
		int[] return_buffer = { y0, y1 };
		return return_buffer;
	}
	
	static int inspect_error( int x0, int x1, int y0, int y1, float[] mask, int mdct, vorbis_info_floor1 info ) {
		
		int dy = y1-y0;
		int adx = x1-x0;
		int ady = Math.abs(dy);
		int base = dy/adx;
		int sy = (dy<0?base-1:base+1);
		int x = x0;
		int y = y0;
		int err = 0;
		int val = vorbis_dBquant(mask[x]);
		int mse = 0;
		int n = 0;
		
		ady -= Math.abs(base*adx);
		
		mse = (y-val);
		mse *= mse;
		n++;
		
		if ( mask[mdct+x]+info.twofitatten >= mask[x] ) {
			if ( y+info.maxover < val )
		    	return(1);
			if ( y-info.maxunder > val )
				return(1);
		}
		
		while ( ++x < x1 ) {
			
			err = err+ady;
			if ( err >= adx ) {
				err-=adx;
				y+=sy;
			} else {
				y += base;
			}
			
			val = vorbis_dBquant(mask[x]);
			mse += ((y-val)*(y-val));
			n++;
			if ( mask[mdct+x]+info.twofitatten >= mask[x] ) {
				if ( val > 0 ) {
					if (y+info.maxover<val )
						return(1);
					if (y-info.maxunder>val )
						return(1);
				}
			}
		}
		
		if ( info.maxover*info.maxover/n > info.maxerr)
			return(0);
		if ( info.maxunder*info.maxunder/n > info.maxerr)
			return(0);
		if ( mse/n > info.maxerr )
			return(1);
		return(0);
	}
	
	static int post_Y( int[] A, int[] B, int pos ) {
		
		if ( A[pos] < 0 )
			return B[pos];
		if ( B[pos] < 0 )
			return A[pos];
		
		return (A[pos]+B[pos])>>>1;
	}

	public int[] floor1_fit( vorbis_look_floor1 look, int logmdct, float[] logmask) {
		
		  int i,j;
		  
		  vorbis_info_floor1 info = look.vi;
		  int n = look.n;
		  int posts = look.posts;
		  int nonzero = 0;
		  
		  // lsfit_acc fits[VIF_POSIT+1];
		  // int fit_valueA[VIF_POSIT+2]; // index by range list position
		  // int fit_valueB[VIF_POSIT+2]; // index by range list position
		  // int loneighbor[VIF_POSIT+2]; // sorted index of range list position (+2)
		  // int hineighbor[VIF_POSIT+2]; 
		  // int *output=NULL;
		  // int memo[VIF_POSIT+2];

		  lsfit_acc[] fits = new lsfit_acc[VIF_POSIT+1];
		  for ( i=0; i < VIF_POSIT+1; i++ )
			  fits[i] = new lsfit_acc();
		  
		  int[] fit_valueA = new int[VIF_POSIT+2]; // index by range list position
		  int[] fit_valueB = new int[VIF_POSIT+2]; // index by range list position

		  int[] loneighbor = new int[VIF_POSIT+2]; // sorted index of range list position (+2)
		  int[] hineighbor = new int[VIF_POSIT+2]; 
		  int[] output = null;
		  int[] memo= new int[VIF_POSIT+2];
		  
		  int[] return_buffer = new int[2];

		  for (i=0;i<posts;i++)
			  fit_valueA[i]=-200; // mark all unused
		  for (i=0;i<posts;i++)
			  fit_valueB[i]=-200; // mark all unused
		  for (i=0;i<posts;i++)
			  loneighbor[i]=0; // 0 for the implicit 0 post
		  for (i=0;i<posts;i++)
			  hineighbor[i]=1; // 1 for the implicit post at n
		  for (i=0;i<posts;i++)
			  memo[i]=-1;      // no neighbor yet
		  
		  // quantize the relevant floor points and collect them into line fit
		  // structures (one per minimal division) at the same time
		  
		  if ( posts == 0 ) {
			  nonzero += accumulate_fit( logmask, logmdct, 0, n, fits[0], n, info );
		  } else {
			  for ( i=0; i < posts-1; i++ )
				  nonzero += accumulate_fit( logmask, logmdct, look.sorted_index[i], look.sorted_index[i+1], fits[i], n, info );
		  }
		  
		  if ( nonzero > 0 ) {
			  
			  // start by fitting the implicit base case....
			  int y0 = -200;
			  int y1 = -200;
			  return_buffer = fit_line( fits, 0, posts-1, y0, y1 );
			  y0 = return_buffer[0];
			  y1 = return_buffer[1];
			  
			  fit_valueA[0] = y0;
			  fit_valueB[0] = y0;
			  fit_valueB[1] = y1;
			  fit_valueA[1] = y1;
			  
			  // Non degenerate case
			  // start progressive splitting.  This is a greedy, non-optimal
		      // algorithm, but simple and close enough to the best answer.
			  for ( i=2; i<posts; i++ ) {
				  int sortpos = look.reverse_index[i];
				  int ln = loneighbor[sortpos];
				  int hn = hineighbor[sortpos];
				  
				  // eliminate repeat searches of a particular range with a memo
				  if ( memo[ln]!= hn ) {
					  // haven't performed this error search yet
					  int lsortpos = look.reverse_index[ln];
					  int hsortpos = look.reverse_index[hn];
					  memo[ln]=hn;
					  
					  // A note: we want to bound/minimize *local*, not global, error
					  int lx = info.postlist[ln];
					  int hx = info.postlist[hn];	  
					  int ly = post_Y( fit_valueA, fit_valueB, ln );
					  int hy = post_Y( fit_valueA, fit_valueB, hn );
					  
					  if ( ly == -1 || hy == -1 ) {
						  System.out.println( "ERROR: We want to bound/minimize *local*, not global" );
						  System.exit(1);
					  }
					  
					  if ( inspect_error( lx, hx, ly, hy, logmask, logmdct, info ) > 0 ) {
						  
						  // outside error bounds/begin search area.  Split it.
						  int ly0 = -200;
						  int ly1 = -200;
						  int hy0 = -200;
						  int hy1 = -200;
						  return_buffer = fit_line( fits, lsortpos, sortpos-lsortpos, ly0, ly1 );
						  ly0 = return_buffer[0];
						  ly1 = return_buffer[1];
						  return_buffer = fit_line( fits, sortpos, hsortpos-sortpos, hy0, hy1 );
						  hy0 = return_buffer[0];
						  hy1 = return_buffer[1];
						  
						  // store new edge values
						  fit_valueB[ln] = ly0;
						  if ( ln == 0 )
							  fit_valueA[ln] = ly0;
						  fit_valueA[i] = ly1;
						  
						  fit_valueB[i] = hy0;
						  fit_valueA[hn] = hy1;
						  if ( hn == 1 )
							  fit_valueB[hn] = hy1;
						  
						  if ( ly1 >= 0 || hy0 >= 0 ) {
							  // store new neighbor values
							  for ( j=sortpos-1; j >= 0; j-- )
								  if ( hineighbor[j] == hn )
									  hineighbor[j] = i;
								  else
									  break;
							  for ( j=sortpos+1; j < posts; j++ )
								  if ( loneighbor[j] == ln )
									  loneighbor[j] = i;
								  else
									  break;
						  }
					  } else {
						  fit_valueA[i] = -200;
						  fit_valueB[i] = -200;
					  }
				  }
			  }
			  
			  // output = _vorbis_block_alloc(vb,sizeof(*output)*posts);
			  output = new int[ posts ];
			  
			  output[0] = post_Y( fit_valueA, fit_valueB, 0 );
			  output[1] = post_Y( fit_valueA, fit_valueB, 1 );
			  
			  // fill in posts marked as not using a fit; we will zero back out to 'unused' when encoding
			  // them so long as curve interpolation doesn't force them into use
			  
			  for ( i=2; i < posts; i++ ) {
				  
				  int ln = look.loneighbor[i-2];
				  int hn = look.hineighbor[i-2];
				  int x0 = info.postlist[ln];
				  int x1 = info.postlist[hn];
				  y0 = output[ln];
				  y1 = output[hn];
				  
				  int predicted = render_point( x0, x1, y0, y1, info.postlist[i] );
				  int vx = post_Y( fit_valueA, fit_valueB, i );
				  
				  if ( vx >= 0 && predicted != vx ) { 
					  output[i] = vx;
				  } else {
					  output[i] = predicted|0x8000;
				  }
			  }
		  }

		  return output;
	}
	
	static int[] floor1_interpolate_fit( vorbis_look_floor1 look, int[] A,int[] B, int del ) {
		
		int i;
		int posts = look.posts;
		int[] output = null;
		
		if ( A != null && B != null ) {
			
			// output=_vorbis_block_alloc(vb,sizeof(*output)*posts);
			output = new int[ posts ];
			
			for ( i=0; i < posts; i++ ) {
				output[i] = ((65536-del)*(A[i]&0x7fff)+del*(B[i]&0x7fff)+32768)>>16;
			
			boolean aTrue = (A[i]&0x8000) > 0;
			boolean bTrue = (B[i]&0x8000) > 0;
			if ( aTrue && bTrue )
				output[i] |= 0x8000;
			}
		}
		return(output);	
	}
	
	static float dipole_hypot( float a, float b ) {
		
		if ( a > 0. ) {
			if( b > 0. )return new Double( Math.sqrt(a*a+b*b) ).floatValue();
			if( a > -b )return new Double( Math.sqrt(a*a-b*b) ).floatValue();
			return new Double( -Math.sqrt(b*b-a*a) ).floatValue();
		}
		
		if( b < 0. )return new Double( -Math.sqrt(a*a+b*b) ).floatValue();
		if( -a > b )return new Double( -Math.sqrt(a*a-b*b) ).floatValue();
		
		return new Double( Math.sqrt(b*b-a*a) ).floatValue();
	}
	
	static float round_hypot( float a, float b ) {
		
		if ( a > 0. ) {
			if ( b > 0. )return new Double( Math.sqrt(a*a+b*b) ).floatValue();
			if ( a > -b )return new Double( Math.sqrt(a*a+b*b) ).floatValue();
			return new Double( -Math.sqrt(b*b+a*a) ).floatValue();
		}
		
		if ( b < 0. )return new Double( -Math.sqrt(a*a+b*b) ).floatValue();
		
		if ( -a > b )return new Double( -Math.sqrt(a*a+b*b) ).floatValue();
		
		return new Double( Math.sqrt(b*b+a*a) ).floatValue();
	}
	
	private float[][] _vp_quantize_couple_memo( vorbis_info_psy_global g, vorbis_look_psy p, vorbis_info_mapping0 vi, float[][] mdct) {
		
		int i, j;
		int n = p.n;
		
		// float **ret=_vorbis_block_alloc(vb,vi->coupling_steps*sizeof(*ret));
		float[][] ret = new float[ vi.coupling_steps ][ n ];
		int limit = g.coupling_pointlimit[p.vi.blockflag][PACKETBLOBS/2];
		
		for ( i=0; i < vi.coupling_steps; i++ ) {
			
			float[] mdctM = mdct[vi.coupling_mag[i]];
			float[] mdctA = mdct[vi.coupling_ang[i]];
			// ret[i] = _vorbis_block_alloc(vb,n*sizeof(**ret));
			for ( j=0; j < limit; j++ )
				ret[i][j] = dipole_hypot( mdctM[j], mdctA[j] );
			for ( ; j < n; j++ )
				ret[i][j] = round_hypot( mdctM[j], mdctA[j] );
		}
		
		return ret;
	}

	private static void SORT4( int o, float[] data, int offset, int[] n ) {
		
		if ( Math.abs(data[offset+o+2]) >= Math.abs(data[offset+o+3]) )
			if ( Math.abs(data[offset+o+0]) >= Math.abs(data[offset+o+1]) )
				if ( Math.abs(data[offset+o+0]) >= Math.abs(data[offset+o+2]) )
					if ( Math.abs(data[offset+o+1]) >= Math.abs(data[offset+o+2]) )
					{ n[o]=o+0; n[o+1]=o+1; n[o+2]=o+2; n[o+3]=o+3; }
					else
						if ( Math.abs(data[offset+o+1]) >= Math.abs(data[offset+o+3]) )
						{ n[o]=o+0; n[o+1]=o+2; n[o+2]=o+1; n[o+3]=o+3; }
						else
						{ n[o]=o+0; n[o+1]=o+2; n[o+2]=o+3; n[o+3]=o+1; }
				else
					if ( Math.abs(data[offset+o+0]) >= Math.abs(data[offset+o+3]) )
						if ( Math.abs(data[offset+o+1]) >= Math.abs(data[offset+o+3]) )
						{ n[o]=o+2; n[o+1]=o+0; n[o+2]=o+1; n[o+3]=o+3; }
						else
						{ n[o]=o+2; n[o+1]=o+0; n[o+2]=o+3; n[o+3]=o+1; }
					else
					{ n[o]=o+2; n[o+1]=o+3; n[o+2]=o+0; n[o+3]=o+1; }
			else
				if ( Math.abs(data[offset+o+1]) >= Math.abs(data[offset+o+2]) )
					if ( Math.abs(data[offset+o+0]) >= Math.abs(data[offset+o+2]) )
					{ n[o]=o+1; n[o+1]=o+0; n[o+2]=o+2; n[o+3]=o+3; }
					else
						if ( Math.abs(data[offset+o+0]) >= Math.abs(data[offset+o+3]) )
						{ n[o]=o+1; n[o+1]=o+2; n[o+2]=o+0; n[o+3]=o+3; }
						else
						{ n[o]=o+1; n[o+1]=o+2; n[o+2]=o+3; n[o+3]=o+0; }
				else
					if ( Math.abs(data[offset+o+1]) >= Math.abs(data[offset+o+3]) )
						if ( Math.abs(data[offset+o+0]) >= Math.abs(data[offset+o+3]) )
						{ n[o]=o+2; n[o+1]=o+1; n[o+2]=o+0; n[o+3]=o+3; }
						else
						{ n[o]=o+2; n[o+1]=o+1; n[o+2]=o+3; n[o+3]=o+0; }
					else
					{ n[o]=o+2; n[o+1]=o+3; n[o+2]=o+1; n[o+3]=o+0; }
		else
			if ( Math.abs(data[offset+o+0]) >= Math.abs(data[offset+o+1]) )
				if ( Math.abs(data[offset+o+0]) >= Math.abs(data[offset+o+3]) )
					if ( Math.abs(data[offset+o+1]) >= Math.abs(data[offset+o+3]) )
					{ n[o]=o+0; n[o+1]=o+1; n[o+2]=o+3; n[o+3]=o+2; }
					else
						if ( Math.abs(data[offset+o+1]) >= Math.abs(data[offset+o+2]) )
						{ n[o]=o+0; n[o+1]=o+3; n[o+2]=o+1; n[o+3]=o+2; }
						else
						{ n[o]=o+0; n[o+1]=o+3; n[o+2]=o+2; n[o+3]=o+1; }
				else
					if ( Math.abs(data[offset+o+0]) >= Math.abs(data[offset+o+2]) )
						if ( Math.abs(data[offset+o+1]) >= Math.abs(data[offset+o+2]) )
						{ n[o]=o+3; n[o+1]=o+0; n[o+2]=o+1; n[o+3]=o+2; }
						else
						{ n[o]=o+3; n[o+1]=o+0; n[o+2]=o+2; n[o+3]=o+1; }
					else
					{ n[o]=o+3; n[o+1]=o+2; n[o+2]=o+0; n[o+3]=o+1; }
			else
				if ( Math.abs(data[offset+o+1]) >= Math.abs(data[offset+o+3]) )
					if ( Math.abs(data[offset+o+0]) >= Math.abs(data[offset+o+3]) )
					{ n[o]=o+1; n[o+1]=o+0; n[o+2]=o+3; n[o+3]=o+2; }
					else
						if ( Math.abs(data[offset+o+0]) >= Math.abs(data[offset+o+2]) )
						{ n[o]=o+1; n[o+1]=o+3; n[o+2]=o+0; n[o+3]=o+2; }
						else
						{ n[o]=o+1; n[o+1]=o+3; n[o+2]=o+2; n[o+3]=o+0; }
				else
					if ( Math.abs(data[offset+o+1]) >= Math.abs(data[offset+o+2]) )
						if ( Math.abs(data[offset+o+0]) >= Math.abs(data[offset+o+2]) )
						{ n[o]=o+3; n[o+1]=o+1; n[o+2]=o+0; n[o+3]=o+2; }
						else
						{ n[o]=o+3; n[o+1]=o+1; n[o+2]=o+2; n[o+3]=o+0; }
					else
					{ n[o]=o+3; n[o+1]=o+2; n[o+2]=o+1; n[o+3]=o+0; }
	}
	
	static void sortindex_fix8( int[] index, int ioff, float[] data, int offset ) {
		
		int i,j,k;
		int[] n = new int[ 8 ];
		
		// index+=offset;
		// data+=offset;
		
		SORT4( 0, data, offset, n );
		SORT4( 4, data, offset, n );
		
		j=0;k=4;
		for ( i=0; i < 8; i++ )
			index[ioff+offset+i] = n[ ((k>=8) || (j<4) && (Math.abs(data[offset+0+n[j]]) >= Math.abs(data[offset+0+n[k]]))?j++:k++) ] + offset;
	}
	
	static void sortindex_fix32( int[] index, int ioff, float[] data, int offset ) {
		
		int i,j,k;
		int[] n = new int[ 32 ];
		
		for ( i=0; i < 32; i+=8 )
			sortindex_fix8( index, ioff, data, offset+i );
		
		ioff += offset;
		
		for ( i=j=0,k=8; i < 16; i++ )
			n[i]=index[ ioff+ ((k>=16)||(j<8) && (Math.abs(data[0+index[ioff+j]]) >= Math.abs(data[0+index[ioff+k]]))?j++:k++ ) ];
		
		for ( i=j=16,k=24; i < 32; i++ )
			n[i]=index[ ioff+ ((k>=32)||(j<24) && (Math.abs(data[0+index[ioff+j]]) >= Math.abs(data[0+index[ioff+k]]))?j++:k++ ) ];
		
		for ( i=j=0,k=16; i < 32; i++ )
			index[ioff+i] = n[ ((k>=32)||(j<16) && (Math.abs(data[0+n[j]]) >= Math.abs(data[0+n[k]]))?j++:k++ ) ];
	}
	
	static void sortindex_shellsort( int[] index, int ioff, float[] data, int offset, int count ) {
		
		int gap,pos,left;
		// int right;
		int i,j;
		
		ioff += offset;
		
		for ( i=0; i < count; i++ )
			index[ioff+i] = i+offset;
		gap=1;
		while ( gap <= count )
			gap=gap*3+1;
		gap/=3;
		if ( gap >= 4 )
			gap/=3;
		
		while ( gap > 0 ){
			for ( pos=gap; pos < count; pos++ ) {
				for ( left=pos-gap; left >= 0; left-=gap ) {
					i=index[ioff+left];
					j=index[ioff+left+gap];
					if ( !(Math.abs(data[0+i]) >= Math.abs(data[0+j])) ) {
						index[ioff+left]=j;
						index[ioff+left+gap]=i;
						}
					else
						break;
					}
				}
			gap/=3;
			}
		}
	
	static void sortindex( int[] index, int ioff, float[] data, int offset, int count ) {
		
		if ( count == 8 )
			sortindex_fix8( index, ioff, data, offset );
		else
			if ( count == 32 )
				sortindex_fix32( index, ioff, data, offset );
			else
				sortindex_shellsort( index, ioff, data, offset, count );
	}
	
	private int[][] _vp_quantize_couple_sort( vorbis_look_psy p, vorbis_info_mapping0 vi, float[][] mags ) {
		
		if ( p.vi.normal_point_p > 0 ) {
			
			int i,j;
			int n = p.n;
			
			// int **ret=_vorbis_block_alloc(vb,vi->coupling_steps*sizeof(*ret));
			int[][] ret = new int[ vi.coupling_steps ][ n ];
			int partition = p.vi.normal_partition;
			// float **work=alloca(sizeof(*work)*partition);
			// float[][] work = new float[ partition ][];
			
			for ( i=0; i < vi.coupling_steps; i++ ) {
				
				// ret[i]=_vorbis_block_alloc(vb,n*sizeof(**ret));
				
				for ( j=0; j < n; j+=partition ) {
					/*
					for ( k=0; k < partition; k++ ) {
						// work[k] = mags[i]+k+j;
						work[k] = new float[ mags[i].length - k+j ];
						System.arraycopy(mags[i], k+j, work[k], 0, mags[i].length - k+j );
					}
					
					// qsort(work,partition,sizeof(*work),apsort);
					Arrays.sort( work );
					
					// for( k=0; k < partition; k++ )
					// 	ret[i][k+j] = work[k]-mags[i];
					for ( k=0; k < partition; k++)
						ret[i][k+j] = work[k] - mags[i];
					*/
					sortindex( ret[i], 0, mags[i], j, partition );
				}
			}
			return(ret);
		}
		return null;
	}
	
	private void _vp_noise_normalize_sort( vorbis_look_psy p, float[] magnitudes,int[] sortedindex ) {
		
		int j;
		int n = p.n;
		
		vorbis_info_psy vi = p.vi;
		int partition = vi.normal_partition;
		// float **work=alloca(sizeof(*work)*partition);
		int start = vi.normal_start;
		
		for ( j=start; j < n; j+=partition ) {
			
			if ( j+partition > n )
				partition = n-j;
			
			//    for(i=0;i<partition;i++)work[i]=magnitudes+i+j;
			//    qsort(work,partition,sizeof(*work),apsort);
			//    for(i=0;i<partition;i++){
			//      sortedindex[i+j-start]=work[i]-magnitudes;
			//    }
			sortindex( sortedindex, -start, magnitudes, j, partition );
		}
	}
	
	private void hf_reduction( vorbis_info_psy_global g, vorbis_look_psy p,  vorbis_info_mapping0 vi, float[][] mdct ) {
		
		int i,j;
		int n = p.n;
		int de = (int)(0.3*p.m_val);
		int limit = g.coupling_pointlimit[p.vi.blockflag][PACKETBLOBS/2];
		// int start = p.vi.normal_start;
		
		for ( i=0; i < vi.coupling_steps; i++ ) {
			// for(j=start; j<limit; j++){} // ???
			for ( j=limit; j < n; j++ ) 
				mdct[i][j] *= (1.0 - de*((float)(j-limit) / (float)(n-limit)));
		}
	}
	
	private void _vp_remove_floor( vorbis_look_psy p, float[] mdct, int[] codedflr, float[] residue, int sliding_lowpass ) { 
		
		int i;
		int n = p.n;
		
		if ( sliding_lowpass > n )
			sliding_lowpass = n;
		
		for ( i=0; i < sliding_lowpass; i++ )
			residue[i] = mdct[i]*FLOOR1_fromdB_INV_LOOKUP[codedflr[i]];
		
		for ( ; i < n; i++ )
			residue[i] = 0.f;
	}
	
	private void _vp_noise_normalize( vorbis_look_psy p, float[] in, int out, int[] sortedindex ) {
		
		// int flag = 0;
		int i;
		int j = 0;
		int n = p.n;
		
		vorbis_info_psy vi = p.vi;
		int partition = vi.normal_partition;
		int start = vi.normal_start;
		
		if ( start > n )
			start = n;
		
		if ( vi.normal_channel_p > 0 ) {
			
			for ( ; j < start; j++ )
				in[out+j] = (float)Math.rint(in[j]);
			
			for ( ; j+partition <= n; j+=partition ) {
				
				float acc = 0.f;
				int k;
				
				for ( i=j; i < j+partition; i++ )
					acc += in[i]*in[i];
				
				for ( i=0 ; i < partition; i++ ) {
					
					k = sortedindex[i+j-start];
					
					if ( in[k]*in[k] >= .25f ) {
						
						in[out+k] = (float)Math.rint(in[k]);
						acc-=in[k]*in[k];
						// flag=1;
					} else {
						
						if ( acc < vi.normal_thresh )
							break;
						in[out+k] = unitnorm( in[k] );
						acc -= 1.;
					}
				}
				
				for ( ; i < partition; i++ ) {
					
					k = sortedindex[i+j-start];
					in[out+k] = 0.f;
					
				}
			}
		}
		
		for ( ; j < n; j++ )
			in[out+j]=(float)Math.rint(in[j]);
	}
	
	static float[] couple_lossless( float A, float B, float qA, float qB ) {
		
		int test1 = ((Math.abs(qA) > Math.abs(qB))?1:0);
		test1 -= ((Math.abs(qA) < Math.abs(qB))?1:0);
		
		if( test1 <= 0 )
			test1 = ( (((Math.abs(A)>Math.abs(B)))?1:0) <<1)-1;
		
		if ( test1 == 1 ) {
			qB = (qA>0.f?qA-qB:qB-qA);
		} else {
			float temp = qB;  
			qB = (qB>0.f?qA-qB:qB-qA);
			qA = temp;
		}
		
		if ( qB > Math.abs(qA)*1.9999f ) {
			qB= -Math.abs(qA)*2.f;
			qA= -qA;
		}
		
		float[] ret = { qA, qB };
		return ret;
	}
	
	static float[] hypot_lookup = { // [32]
		-0.009935f, -0.011245f, -0.012726f, -0.014397f, 
		-0.016282f, -0.018407f, -0.020800f, -0.023494f, 
		-0.026522f, -0.029923f, -0.033737f, -0.038010f, 
		-0.042787f, -0.048121f, -0.054064f, -0.060671f, 
		-0.068000f, -0.076109f, -0.085054f, -0.094892f, 
		-0.105675f, -0.117451f, -0.130260f, -0.144134f, 
		-0.159093f, -0.175146f, -0.192286f, -0.210490f, 
		-0.229718f, -0.249913f, -0.271001f, -0.292893f };
	
	static float[] precomputed_couple_point( float premag, int floorA, int floorB, float mag, float ang ) {
		
		int test = ((floorA > floorB)?1:0)-1;
		int offset = 31-Math.abs(floorA-floorB);
		float floormag = hypot_lookup[(((offset<0)?1:0)-1)&offset]+1.f;
		
		floormag *= FLOOR1_fromdB_INV_LOOKUP[(floorB&test)|(floorA&(~test))];
		
		mag = premag*floormag;
		ang = 0.f;
		
		float[] ret = { mag, ang };
		return ret;
	}
	
	private void _vp_couple( int blobno, vorbis_info_psy_global g, vorbis_look_psy p, vorbis_info_mapping0 vi,
							float[][] res, float[][] mag_memo, int[][] mag_sort, int[][] ifloor, int[] nonzero, int sliding_lowpass ) {
		
		int i,j,k;
		int n = p.n;
		
		// perform any requested channel coupling
		// point stereo can only be used in a first stage (in this encoder) because of the dependency on floor lookups
		
		for ( i=0; i < vi.coupling_steps; i++ ) {
			
			// once we're doing multistage coupling in which a channel goes
			// through more than one coupling step, the floor vector
			// magnitudes will also have to be recalculated an propogated
			// along with PCM.  Right now, we're not (that will wait until 5.1
			// most likely), so the code isn't here yet. The memory management
			// here is all assuming single depth couplings anyway.
			
			// make sure coupling a zero and a nonzero channel results in two nonzero channels.
			
			if ( ( nonzero[vi.coupling_mag[i]] > 0 ) || ( nonzero[vi.coupling_ang[i]] > 0 ) ) {
				
				float[] rM = res[vi.coupling_mag[i]];
				float[] rA = res[vi.coupling_ang[i]];
				// float *qM = rM+n;
				int qM = n;
				// float *qA = rA+n;
				int qA = n;
				int[] floorM = ifloor[vi.coupling_mag[i]];
				int[] floorA = ifloor[vi.coupling_ang[i]];
				float prepoint = stereo_threshholds[g.coupling_prepointamp[blobno]];
				float postpoint = stereo_threshholds[g.coupling_postpointamp[blobno]];
				int partition = ( (p.vi.normal_point_p>0)?p.vi.normal_partition:p.n );
				int limit = g.coupling_pointlimit[p.vi.blockflag][blobno];
				int pointlimit = limit;
				
				nonzero[vi.coupling_mag[i]] = 1; 
				nonzero[vi.coupling_ang[i]] = 1; 
				
				// The threshold of a stereo is changed with the size of n
				if ( n > 1000 )
					postpoint = stereo_threshholds_limited[g.coupling_postpointamp[blobno]]; 
				
				for ( j=0; j < p.n; j+=partition ) {
					
					float acc = 0.f;
					
					for ( k=0; k < partition; k++ ) {
						
						int l=k+j;
						
						if ( l < sliding_lowpass ) {
							
							if ( ( l >= limit && Math.abs(rM[l]) < postpoint && Math.abs(rA[l]) < postpoint) || (Math.abs(rM[l]) < prepoint && Math.abs(rA[l]) < prepoint ) ) {
								
								float[] return_buffer = precomputed_couple_point( mag_memo[i][l], floorM[l], floorA[l], rM[qM+l], rA[qA+l] );
								rM[qM+l] = return_buffer[0];
								rA[qA+l] = return_buffer[1];
								
								if ( Math.rint(rM[qM+l]) == 0 )
									acc += rM[qM+l]*rM[qM+l];
								
							} else {
								float[] return_buffer = couple_lossless( rM[l], rA[l], rM[qM+l], rA[qA+l] );
								rM[qM+l] = return_buffer[0];
								rA[qA+l] = return_buffer[1];								
							}
						} else {
							rM[qM+l] = 0.f;
							rA[qA+l] = 0.f;
						}
					}
					
					if ( p.vi.normal_point_p > 0 ) {
						
						for ( k=0; k < partition && acc >= p.vi.normal_thresh; k++ ) {
							
							int l = mag_sort[i][j+k];
							
							if ( l < sliding_lowpass && l >= pointlimit && Math.rint(rM[qM+l]) == 0 ) {
								
								rM[qM+l] = unitnorm(rM[qM+l]);
								acc -= 1.f;
							}
						} 
					}
				}
			}
		}
	}
	
	// designed for stereo or other modes where the partition size is an
	// integer multiple of the number of channels encoded in the current submap
	private int[][] _2class( vorbis_look_residue0 look, float[][] in, int in_offset, int ch ) {
		
	  int i,j,k,l;
	  
	  vorbis_info_residue0 info = look.info;

	  // move all this setup out later
	  int samples_per_partition = info.grouping;
	  int possible_partitions = info.partitions;
	  int n = info.end - info.begin;

	  int partvals = n/samples_per_partition;
	  // int[][] partword=_vorbis_block_alloc(vb,sizeof(*partword));
	  // partword[0] = _vorbis_block_alloc(vb,n*ch/samples_per_partition*sizeof(*partword[0]));
	  // memset(partword[0],0,n*ch/samples_per_partition*sizeof(*partword[0]));
	  int[][] partword= new int[1][n*ch/samples_per_partition];
	  
	  for ( i=0,l=info.begin/ch; i < partvals; i++ ) {
		  
		  float magmax = 0.f;
		  float angmax = 0.f;
		  
		  for( j=0; j<samples_per_partition; j+=ch ) {
			  
			  if ( Math.abs(in[0][l+in_offset]) > magmax )
				  magmax = Math.abs(in[0][l+in_offset]);
			  
			  for ( k=1; k < ch; k++ )
				  if ( Math.abs(in[k][l+in_offset]) > angmax )
					  angmax = Math.abs(in[k][l+in_offset]);
			  l++;  
		  }
		  
		  for ( j=0; j < possible_partitions-1; j++ )
			  if ( magmax <= info.classmetric1[j] && angmax <= info.classmetric2[j] )
				  break;
		  
		  partword[0][i] = j;  
	  }
	  
	  look.frames++;

	  return partword;  
	}

	
	private int[][] res2_class( vorbis_look_residue0 vl, float[][] in, int in_offset, int[] nonzero, int ch ) {
		
		int i,used=0;
		for ( i=0; i < ch; i++ )
			if ( nonzero[i] > 0 )
				used++;
		if ( used > 0)
			return _2class( vl, in, in_offset, ch );
		else
			return new int[][] {{ 0 }};
	}
	
	// break an abstraction and copy some code for performance purposes
	private int local_book_besterror( codebook book, float[] a, int a_off ) {
		
		int dim=book.dim,i,k,o;
		int best = 0;
		
		encode_aux_threshmatch tt = book.c.thresh_tree;
		
		// find the quant val of each scalar
		for ( k=0, o=dim; k < dim; ++k ) {

			float val = a[a_off + --o];
			i = tt.threshvals >> 1;
		
			if ( val < tt.quantthresh[i] ) {
				
				if ( val < tt.quantthresh[i-1] ) {
					for ( --i; i > 0; --i )
						if ( val >= tt.quantthresh[i-1] )
							break;	
				}	
			}
			else {
				
				for ( ++i; i < tt.threshvals-1; ++i )
					if ( val < tt.quantthresh[i] )
						break;	
			}
			
			best = (best*tt.quantvals) + tt.quantmap[i];	
		}
		
		// regular lattices are easy :-)
		
		if ( book.c.lengthlist[best] <= 0 ) {
			
			final static_codebook c = book.c;
			int index,j;
			float bestf = 0.f;
			// float[] e = book.valuelist;
			int e_off = 0;
			best = -1;
			
			for ( index=0; index < book.entries; index++ ) {
				
				if ( c.lengthlist[index] > 0 ) {
					float this_local = 0.f;
					for ( j=0; j < dim; j++ ) {
						float val = (book.valuelist[e_off+j]-a[a_off+j]);
						this_local += val*val;
					}
					if ( best == -1 || this_local < bestf ) {
						bestf=this_local;
						best=index;
					}
				}
				e_off += dim;	
			}
		}
		
		{
			// float *ptr = book.valuelist + best*dim;
			int ptr = best * dim;
			for ( i=0; i < dim; i++ ) {
				// *a++ -= *ptr++;
				a[a_off++] -= book.valuelist[ptr++];
			}
		}
		
		return best;
	}

	private int _encodepart( oggpack_buffer opb_local, float[] vec, int vec_off, int n, codebook book, int[] acc ) {
		
		int i,bits = 0;
		int dim = book.dim;
		int step = n/dim;
		
		for ( i=0; i < step; i++ ) {
			
			int entry = local_book_besterror( book, vec, vec_off+i*dim );
			
			// #ifdef TRAIN_RES
			//    acc[entry]++;
			// #endif
			
			bits += opb_local.vorbis_book_encode( book, entry );
		}
		
		return bits;	
	}
	
	private boolean _01forward( oggpack_buffer opb_local, vorbis_look_residue0 look, float[] in, int ch, int[][] partword ) {
		
		int i,j,k,s;	// long
		vorbis_info_residue0 info = look.info;
		
		// move all this setup out later
		int samples_per_partition = info.grouping;
		int possible_partitions = info.partitions;
		int partitions_per_word = look.phrasebook.dim;
		int n = info.end - info.begin;
		
		int partvals = n/samples_per_partition;
		// long resbits[128];
		// long resvals[128];
		// memset(resbits,0,sizeof(resbits));
		// memset(resvals,0,sizeof(resvals));
		int[] resbits = new int[ 128 ];
		int[] resvals = new int[ 128 ];
		
		// we code the partition words for each channel, then the residual
		// words for a partition per channel until we've written all the
		// residual words for that partition word.  Then write the next
		// partition channel words...
		
		for ( s=0; s < look.stages; s++ ) {
			
			for ( i=0; i < partvals; ) {
				
				// first we encode a partition codeword for each channel
				if ( s==0 ) {
					
					for ( j=0; j < ch; j++ ) {
						int val = partword[j][i];	// long
						for ( k=1; k < partitions_per_word; k++ ) {
							val*=possible_partitions;
							if ( i+k < partvals )
								val += partword[j][i+k];	
						}
						
						// training hack
						if ( val < look.phrasebook.entries )
							look.phrasebits += opb_local.vorbis_book_encode( look.phrasebook, val );
						else
							System.out.println( "! - val < look.phrasebook.entries - !" );	
					}	
				}
				
				// now we encode interleaved residual values for the partitions
				for ( k=0; k < partitions_per_word && i < partvals; k++, i++ ) {
					
					int offset = i*samples_per_partition + info.begin;	// long
					
					for ( j=0; j < ch; j++ ) {
						
						if ( s==0 )
							resvals[partword[j][i]] += samples_per_partition;
						
						if ( ( info.secondstages[partword[j][i]]&(1<<s) ) > 0 ) {
							
							codebook statebook = look.partbooks[partword[j][i]][s];
							if ( statebook != null ) {
								int ret;
								// long *accumulator = NULL;
								int[] accumulator = null;	// long
								
								ret = _encodepart( opb_local, in, j+offset, samples_per_partition, statebook, accumulator );
								
								look.postbits += ret;
								resbits[partword[j][i]] += ret;
							}
						}
					}
				}
			}
		}
		return true;	
	}
	
	// res2 is slightly more different; all the channels are interleaved into a single vector and encoded.
	private boolean res2_forward( oggpack_buffer opb_local, vorbis_look_residue0 vl, float[][] in, int in_offset, float[][] out, int[] nonzero, int ch, int[][] partword) {
		
		int i,j,k;			// long
		int n = pcmend/2;	// long
		int used = 0;		// long
		
		// don't duplicate the code; use a working vector hack for now and reshape ourselves into a single channel res1
		// ugly; reallocs for each coupling pass :-(
		
		// float *work=_vorbis_block_alloc(vb,ch*n*sizeof(*work));
		float[] work = new float[ ch*n ];
		
		for ( i=0; i < ch; i++ ) {
			
			// float *pcm = in[i];
			
			if ( nonzero[i] > 0 )
				used++;
			
			for ( j=0, k=i; j < n; j++, k+=ch )
				work[k] = in[i][j+in_offset];	
		}
		
		if ( used > 0 ) {
			
			boolean ret = _01forward( opb_local, vl, work, 1, partword );
			
// TODO - offset for out
// out is currently always called as null
			
			// update the sofar vector
			if ( out != null ) {
				
				for ( i=0; i < ch; i++ ) {
					
					// float *pcm=in[i];
					
					// float *sofar=out[i];
					float[] sofar = out[i];
					
					for ( j=0, k=i; j < n; j++, k+=ch )
						sofar[j] += in[i][j+in_offset]-work[k];
				}
			}
			return ret;
		}
		else {
			return false;	
		}	
	}
	
	
	public void mapping0_forward() {

		vorbis_info           vi = vd.vi;
		codec_setup_info      ci = vi.codec_setup;
		private_state         b = vd.backend_state;
		vorbis_block_internal vbi = internal;
		int                    n = pcmend;
		int i,j,k;

		// int    *nonzero     = alloca(sizeof(*nonzero)*vi.channels);
		int[] nonzero = new int[ vi.channels ];
		
		// float  **gmdct      = _vorbis_block_alloc(vb,vi.channels*sizeof(*gmdct));
		float[][] gmdct = new float[ vi.channels ][];

		// int    **ilogmaskch = _vorbis_block_alloc(vb,vi.channels*sizeof(*ilogmaskch));
		int[][] ilogmaskch = new int[ vi.channels ][];
		
		// int ***floor_posts  = _vorbis_block_alloc(vb,vi.channels*sizeof(*floor_posts));
		int[][][] floor_posts = new int[ vi.channels ][][];
		
		float global_ampmax = vbi.ampmax;
		// float *local_ampmax = alloca(sizeof(*local_ampmax)*vi.channels);
		float[] local_ampmax = new float[ vi.channels ];
		int blocktype = vbi.blocktype;
	
		int modenumber = W;
		vorbis_info_mapping0 info = ci.map_param[modenumber];
		
		// vorbis_look_psy psy_look = b.psy+blocktype+(vb.W?2:0);
		int psy_look_offset;
		if ( W > 0 )
			psy_look_offset = blocktype+2;
		else
			psy_look_offset = blocktype;

		mode = modenumber;

		for ( i=0; i < vi.channels; i++ ) {
		
			float scale = 4.f/n;
			float scale_dB;
			
// TODO - trace where the logfft (pcm) data gets slightly thrown from C
// causes flr errors later call to accumulate_fit( logmask, logmdct, look.sorted_index[i], look.sorted_index[i+1], fits[i], n, info ); 
// sets off the lsfit_acc data when flr[mdct+i]+info.twofitatten >= flr[i]
			
			float[] logfft = pcm[i];
		
			// gmdct[i] = _vorbis_block_alloc(vb,n/2*sizeof(**gmdct));
			gmdct[i] = new float[ n/2 ];
			
			scale_dB = todB( scale ) + .345f; 
			// + .345 is a hack; the original todB estimation used on IEEE 754
			// compliant machines had a bug that returned dB values about a third
			// of a decibel too high.  The bug was harmless because tunings
			// implicitly took that into account.  However, fixing the bug
			// in the estimator requires changing all the tunings as well.
			// For now, it's easier to sync things back up here, and
			// recalibrate the tunings in the next major model upgrade.
			
			// window the PCM data

			window._vorbis_apply_window( pcm[i], b.window, ci.blocksizes, lW, W, nW );

			// transform the PCM data
			// only MDCT right now....

			b.transform[W][0].mdct_forward( pcm[i], gmdct[i] );
			
			// FFT yields more accurate tonal estimation (not phase sensitive)

			b.fft_look[W].drft_forward( pcm[i] );

			logfft[0] = scale_dB + todB(pcm[i][0]) + .345f;
			local_ampmax[i] = logfft[0];
			for ( j=1; j<n-1; j+=2 ) {
				float temp = pcm[i][j]*pcm[i][j]+pcm[i][j+1]*pcm[i][j+1];
				temp = logfft[(j+1)>>1] = scale_dB + .5f * todB(temp) + .345f;
				if ( temp > local_ampmax[i] )
					local_ampmax[i] = temp;
			}
			
			if ( local_ampmax[i] > 0.f )
				local_ampmax[i] = 0.f;
		    if ( local_ampmax[i] > global_ampmax)
		    	global_ampmax = local_ampmax[i];
		}
		
		// float   *noise        = _vorbis_block_alloc(vb,n/2*sizeof(*noise));
		float[] noise = new float[ n/2 ];
		
		// float   *tone         = _vorbis_block_alloc(vb,n/2*sizeof(*tone));
		float[] tone = new float[ n/2 ];
		
		for ( i=0; i < vi.channels; i++ ) {
		
			// the encoder setup assumes that all the modes used by any
			// specific bitrate tweaking use the same floor

			int submap = info.chmuxlist[i];
			
			// the following makes things clearer to *me* anyway

			float[] mdct    = gmdct[i];
			float[] logfft  = pcm[i];
			
			// float *logmdct = logfft+n/2;
			int logmdct = n/2;
			// float *logmask = logfft;
			
			mode = modenumber;
			
			// floor_posts[i] = _vorbis_block_alloc(vb,PACKETBLOBS*sizeof(**floor_posts));
			// memset(floor_posts[i],0,sizeof(**floor_posts)*PACKETBLOBS);
			floor_posts[i] = new int[ PACKETBLOBS ][];
			
			for ( j=0; j < n/2; j++ )
				logfft[ logmdct+j ] = todB( mdct[j] ) + .345f;
			//	logmdct[j] = todB(mdct+j) + .345f;

			// first step; noise masking.  Not only does 'noise masking'
			// give us curves from which we can decide how much resolution
			// to give noise parts of the spectrum, it also implicitly hands
			// us a tonality estimate (the larger the value in the
			// 'noise_depth' vector, the more tonal that area is)
			
			// b.psy[ psy_look_offset ]._vp_noisemask( logmdct, noise); // noise does not have by-frequency offset bias applied yet
			b.psy[ psy_look_offset ]._vp_noisemask( logfft, logmdct, noise); // noise does not have by-frequency offset bias applied yet

			// second step: 'all the other crap'; all the stuff that isn't
			// computed/fit for bitrate management goes in the second psy
			// vector.  This includes tone masking, peak limiting and ATH
			
			b.psy[ psy_look_offset ]._vp_tonemask( logfft, tone, global_ampmax, local_ampmax[i] );
				 
			// third step; we offset the noise vectors, overlay tone
			// masking.  We then do a floor1-specific line fit.  If we're
			// performing bitrate management, the line fit is performed
			// multiple times for up/down tweakage on demand.
			
			// b.psy[ psy_look_offset ]._vp_offset_and_mix( noise, tone, 1, logmask, mdct, logmdct );
			b.psy[ psy_look_offset ]._vp_offset_and_mix( noise, tone, 1, logfft, mdct, logmdct );

			// this algorithm is hardwired to floor 1 for now; abort out if
			// we're *not* floor1.  This won't happen unless someone has
			// broken the encode setup lib.  Guard it anyway.

			if ( ci.floor_type[info.floorsubmap[submap]] != 1 ) {
				System.out.println( "Error vorbis_block::mapping0_forward() - floor_type != floor1" );
				return;
			}
			
// TODO - logfft float data off
// trace of pcm before this function are dead on until here
// call to 
				
			// floor_posts[i][PACKETBLOBS/2] = floor1_fit( b.flr[info.floorsubmap[submap]], logmdct, logmask );
			floor_posts[i][PACKETBLOBS/2] = floor1_fit( b.flr[info.floorsubmap[submap]], logmdct, logfft );
			
			// are we managing bitrate?  If so, perform two more fits for later rate tweaking (fits represent hi/lo)
			if ( vorbis_bitrate_managed() && (floor_posts[i][PACKETBLOBS/2] != null ) ) {
				
				// higher rate by way of lower noise curve
				b.psy[ psy_look_offset ]._vp_offset_and_mix( noise, tone, 2, logfft, mdct, logmdct );
				
				// floor_posts[i][PACKETBLOBS-1] = floor1_fit( vb, b.flr[info.floorsubmap[submap]], logmdct, logmask );
				floor_posts[i][PACKETBLOBS-1] = floor1_fit( b.flr[info.floorsubmap[submap]], logmdct, logfft );
				
				// lower rate by way of higher noise curve
				b.psy[ psy_look_offset ]._vp_offset_and_mix( noise, tone, 0, logfft, mdct, logmdct );
					
				// floor_posts[i][0] = floor1_fit( vb, b.flr[info.floorsubmap[submap]], logmdct, logmask );
				floor_posts[i][0] = floor1_fit( b.flr[info.floorsubmap[submap]], logmdct, logfft );
					
				// we also interpolate a range of intermediate curves for intermediate rates
				
				for ( k=1; k < PACKETBLOBS/2; k++ ) {
					floor_posts[i][k] = floor1_interpolate_fit( b.flr[info.floorsubmap[submap]],
																floor_posts[i][0],
																floor_posts[i][PACKETBLOBS/2],
																k*65536/(PACKETBLOBS/2) );
				}
						
				for ( k=PACKETBLOBS/2+1; k < PACKETBLOBS-1; k++ ) {
					floor_posts[i][k] = floor1_interpolate_fit( b.flr[info.floorsubmap[submap]],
																floor_posts[i][PACKETBLOBS/2],
																floor_posts[i][PACKETBLOBS-1],
																(k-PACKETBLOBS/2)*65536/(PACKETBLOBS/2) );
				}
			}
		}

		vbi.ampmax = global_ampmax;
		
		//  the next phases are performed once for vbr-only and PACKETBLOB times for bitrate managed modes.
		//  1) encode actual mode being used
		//  2) encode the floor for each channel, compute coded mask curve/res
		//  3) normalize and couple.
		//  4) encode residue
		//  5) save packet bytes to the packetblob vector

		// iterate over the many masking curve fits we've created

		// float **res_bundle=alloca(sizeof(*res_bundle)*vi.channels);
		// float **couple_bundle=alloca(sizeof(*couple_bundle)*vi.channels);
		// int *zerobundle=alloca(sizeof(*zerobundle)*vi.channels);
		// int **sortindex=alloca(sizeof(*sortindex)*vi.channels);
		// float **mag_memo;
		// int **mag_sort;
		
		float[][] res_bundle = new float[ vi.channels ][];
		float[][] couple_bundle = new float[ vi.channels ][];
		int couple_bundle_offset = 0;
		int[] zerobundle = new int[ vi.channels ];
		int[][] sortindex = new int[ vi.channels ][];
		float[][] mag_memo = null;
		int[][] mag_sort = null;
		
		if ( info.coupling_steps > 0 ) {
		
			mag_memo = _vp_quantize_couple_memo( ci.psy_g_param, b.psy[ psy_look_offset ], info, gmdct );
			mag_sort = _vp_quantize_couple_sort( b.psy[ psy_look_offset ], info, mag_memo );
			hf_reduction( ci.psy_g_param, b.psy[ psy_look_offset ], info, mag_memo );
		}
		
		// memset( sortindex, 0, sizeof(*sortindex)*vi.channels);
		if ( b.psy[ psy_look_offset ].vi.normal_channel_p > 0 ) {
		
			for ( i=0; i < vi.channels; i++ ) {
			
				float[] mdct = gmdct[i];
				// sortindex[i] = alloca(sizeof(**sortindex)*n/2);
				sortindex[i] = new int[ n/2 ];
				_vp_noise_normalize_sort( b.psy[ psy_look_offset ], mdct, sortindex[i] );
			}
		}
		
		for ( k=(vorbis_bitrate_managed()?0:PACKETBLOBS/2); k <= (vorbis_bitrate_managed()?PACKETBLOBS-1:PACKETBLOBS/2); k++ ) {
			
			oggpack_buffer opb_local = vbi.packetblob[k];
		
			// start out our new packet blob with packet type and mode
			// Encode the packet type

			opb_local.oggpack_write( 0, 1 );

			// Encode the modenumber
			// Encode frame mode, pre,post windowsize, then dispatch

			opb_local.oggpack_write( modenumber, b.modebits );
			if ( W > 0 ) {
				opb_local.oggpack_write( lW, 1 );
				opb_local.oggpack_write( nW, 1 );
			}
			
			// encode floor, compute masking curve, sep out residue
			for ( i=0; i < vi.channels; i++ ) {
			
				int submap = info.chmuxlist[i];
				float[] mdct = gmdct[i];
				float[] res = pcm[i];

				ilogmaskch[i] = new int[ n/2 ];
				int[] ilogmask = ilogmaskch[i];
				// int   *ilogmask = ilogmaskch[i] = _vorbis_block_alloc(vb,n/2*sizeof(**gmdct));
				
				nonzero[i] = opb_local.floor1_encode( this, b.flr[info.floorsubmap[submap]], floor_posts[i][k], ilogmask );
				
				_vp_remove_floor( b.psy[ psy_look_offset ], mdct, ilogmask, res, ci.psy_g_param.sliding_lowpass[W][k] );
				
				_vp_noise_normalize( b.psy[ psy_look_offset ], res, n/2, sortindex[i] );
			}
			
			// our iteration is now based on masking curve, not prequant and coupling.  Only one prequant/coupling step

			// quantize/couple
			// incomplete implementation that assumes the tree is all depth one, or no tree at all

			if ( info.coupling_steps > 0 ) {
				_vp_couple( k, ci.psy_g_param, b.psy[ psy_look_offset ], info,
							pcm, mag_memo, mag_sort, ilogmaskch, 
							nonzero, ci.psy_g_param.sliding_lowpass[W][k] );
			}
			
			// classify and encode by submap

			for ( i=0; i < info.submaps; i++ ) {
			
				int ch_in_bundle = 0;
				int[][] classifications;
				int resnum = info.residuesubmap[i];
				
				for ( j=0; j < vi.channels; j++ ) {
				
					if ( info.chmuxlist[j] == i ) {

						zerobundle[ch_in_bundle] = 0;
						if ( nonzero[j] > 0 )
							zerobundle[ch_in_bundle] = 1;
						res_bundle[ch_in_bundle] = pcm[j];
						couple_bundle[ch_in_bundle++] = pcm[j];
						couple_bundle_offset = n/2;
					}
				}
				
				// classifications = _residue_P[ci.residue_type[resnum]].class( vb, b.residue[resnum], couple_bundle, zerobundle, ch_in_bundle );
				classifications = res2_class( b.residue[resnum], couple_bundle, couple_bundle_offset, zerobundle, ch_in_bundle );
				
				// _residue_P[ci.residue_type[resnum]].forward( vb, b.residue[resnum], couple_bundle, NULL, zerobundle, ch_in_bundle, classifications );
				res2_forward( opb_local, b.residue[resnum], couple_bundle, couple_bundle_offset, null, zerobundle, ch_in_bundle, classifications );
			}
			
			// ok, done encoding.  Mark this protopacket and prepare next.
		}
	}
	
	// finish taking in the block we just processed
	public boolean vorbis_bitrate_addblock() {
		
		vorbis_block_internal vbi = internal;
		private_state b = vd.backend_state; 
		bitrate_manager_state bm = b.bms;
		vorbis_info vi = vd.vi;
		codec_setup_info ci = vi.codec_setup;
		bitrate_manager_info bi = ci.bi;
		
		int choice = new Double( Math.rint(bm.avgfloat) ).intValue();
		int this_bits = vbi.packetblob[choice].oggpack_bytes() * 8;	// long
		
		int min_target_bits;		// long
		int max_target_bits;		// long
		int avg_target_bits;		// long
		
		if ( W > 0 ) {
			min_target_bits = bm.min_bitsper*bm.short_per_long;	
			max_target_bits = bm.max_bitsper*bm.short_per_long;
			avg_target_bits = bm.avg_bitsper*bm.short_per_long;
		}
		else {
			min_target_bits = bm.min_bitsper;
			max_target_bits = bm.max_bitsper;
			avg_target_bits = bm.avg_bitsper;
		}
		
		int  samples = ci.blocksizes[W]>>1;
		int desired_fill = new Float( bi.reservoir_bits*bi.reservoir_bias ).intValue();	// long
		
		if ( bm.managed <= 0 ) {
			
			// not a bitrate managed stream, but for API simplicity, we'll buffer the packet to keep the code path clean
			
			if ( bm.vb != null )
				return false;		// one has been submitted without being claimed 
			
			bm.vb = this;
			
			return true;	
		}
		
		bm.vb = this;
		
		// look ahead for avg floater
		if ( bm.avg_bitsper > 0 ) {
			
			float slew = 0.f;	// double
			// long avg_target_bits = (vb->W?bm.avg_bitsper*bm.short_per_long:bm.avg_bitsper);
			float slewlimit = 15.f/bi.slew_damp;	// double
			
			// choosing a new floater:
			// 		if we're over target, we slew down
			// 		if we're under target, we slew up
			
			// choose slew as follows: look through packetblobs of this frame
			// and set slew as the first in the appropriate direction that
			// gives us the slew we want.  This may mean no slew if delta is already favorable.
			// Then limit slew to slew max
			
			if ( bm.avg_reservoir+(this_bits-avg_target_bits) > desired_fill ) {
				while ( choice>0 && this_bits>avg_target_bits && bm.avg_reservoir+(this_bits-avg_target_bits)>desired_fill ) {
					choice--;
					this_bits = vbi.packetblob[choice].oggpack_bytes()*8;
					
				}
			}
			else if (bm.avg_reservoir+(this_bits-avg_target_bits)<desired_fill){
				while ( choice+1<PACKETBLOBS && this_bits<avg_target_bits && bm.avg_reservoir+(this_bits-avg_target_bits)<desired_fill ) {
					choice++;
					this_bits = vbi.packetblob[choice].oggpack_bytes()*8;
					
				}
			}
			
			slew = new Double( Math.rint(choice-bm.avgfloat)/samples*vi.rate ).floatValue();
			if ( slew < -slewlimit )
				slew = -slewlimit;
			if ( slew > slewlimit )
				slew = slewlimit;
			choice = new Float( Math.rint(bm.avgfloat+= slew/vi.rate*samples) ).intValue();
			this_bits = vbi.packetblob[choice].oggpack_bytes()*8;		
		}
		
		// enforce min(if used) on the current floater (if used)
		if ( bm.min_bitsper > 0 ) {
			// do we need to force the bitrate up?
			if ( this_bits < min_target_bits ) {
				while ( bm.minmax_reservoir-(min_target_bits-this_bits) < 0 ) {
					choice++;
					if ( choice >= PACKETBLOBS )
						break;
					this_bits = vbi.packetblob[choice].oggpack_bytes()*8;
				}
			}
		}
		
		// enforce max (if used) on the current floater (if used)
		if ( bm.max_bitsper > 0 ) {
			// do we need to force the bitrate down?
			if ( this_bits > max_target_bits ) {
				while ( bm.minmax_reservoir+(this_bits-max_target_bits) > bi.reservoir_bits ) {
					choice--;
					if ( choice < 0 )
						break;
					this_bits = vbi.packetblob[choice].oggpack_bytes()*8;
				}
			}
		}
		
		// Choice of packetblobs now made based on floater, and min/max requirements. Now boundary check extreme choices
		
		if ( choice < 0 ) {
			// choosing a smaller packetblob is insufficient to trim bitrate. frame will need to be truncated
			int maxsize = (max_target_bits+(bi.reservoir_bits-bm.minmax_reservoir))/8;	// long
			bm.choice = choice = 0;
			
			if ( vbi.packetblob[choice].oggpack_bytes() > maxsize ) {
				
				vbi.packetblob[choice].oggpack_writetrunc( maxsize*8 );
				this_bits = vbi.packetblob[choice].oggpack_bytes()*8;
			}
		}
		else {
			
			int minsize=(min_target_bits-bm.minmax_reservoir+7)/8;	// long
			if ( choice >= PACKETBLOBS )
				choice = PACKETBLOBS-1;
			
			bm.choice = choice;
			
			// prop up bitrate according to demand. pad this frame out with zeroes
			minsize -= vbi.packetblob[choice].oggpack_bytes();
			while ( minsize-- > 0 )
				vbi.packetblob[choice].oggpack_write( 0, 8 );
			this_bits = vbi.packetblob[choice].oggpack_bytes()*8;
		}
		
		// now we have the final packet and the final packet size.  Update statistics
		// min and max reservoir
		if ( bm.min_bitsper > 0 || bm.max_bitsper > 0 ) {

			if ( max_target_bits > 0 && this_bits > max_target_bits ) {
				bm.minmax_reservoir += (this_bits-max_target_bits);
			} else if ( min_target_bits > 0 && this_bits < min_target_bits ) {
				bm.minmax_reservoir += (this_bits-min_target_bits);
			} else {
				// inbetween; we want to take reservoir toward but not past desired_fill
				if ( bm.minmax_reservoir > desired_fill){
					if ( max_target_bits > 0 ) { 	// logical bulletproofing against initialization state
						bm.minmax_reservoir += (this_bits-max_target_bits);
						if ( bm.minmax_reservoir < desired_fill)
							bm.minmax_reservoir = desired_fill;
					}
					else {
						bm.minmax_reservoir = desired_fill;
					}
				}
				else {
					if ( min_target_bits > 0 ) { 	// logical bulletproofing against initialization state
						bm.minmax_reservoir+=(this_bits-min_target_bits);
						
						if ( bm.minmax_reservoir > desired_fill )
							bm.minmax_reservoir = desired_fill;
					}
					else {
						bm.minmax_reservoir=desired_fill;
					}
				}
			}
		}
		
		// avg reservoir
		if ( bm.avg_bitsper > 0 ) {
			// int avg_target_bits = (vb->W?bm->avg_bitsper*bm->short_per_long:bm->avg_bitsper);    // long
			if ( W > 0 )
				avg_target_bits = bm.avg_bitsper*bm.short_per_long;
			else
				avg_target_bits = bm.avg_bitsper;

			bm.avg_reservoir += this_bits-avg_target_bits;
		}
		return true;
	}
	
}
