using System;

namespace CUETools.Codecs
{
	public class lpc
	{
		public const int MAX_LPC_ORDER = 32;
		public const int MAX_LPC_WINDOWS = 16;
		public const int MAX_LPC_PRECISIONS = 4;
        public const int MAX_LPC_SECTIONS = 128;

		public unsafe static void window_welch(float* window, int L)
		{
			int N = L - 1;
			double N2 = (double)N / 2.0;

			for (int n = 0; n <= N; n++)
			{
				double k = ((double)n - N2) / N2;
				k = 1.0 - k * k;
				window[n] = (float)(k);
			}
		}

        public unsafe static void window_bartlett(float* window, int L)
		{
			int N = L - 1;
			double N2 = (double)N / 2.0;
			for (int n = 0; n <= N; n++)
			{
				double k = ((double)n - N2) / N2;
				k = 1.0 - k * k;
				window[n] = (float)(k * k);
			}
		}

        public unsafe static void window_rectangle(float* window, int L)
		{
			for (int n = 0; n < L; n++)
				window[n] = 1.0F;
		}

        public unsafe static void window_flattop(float* window, int L)
		{
			int N = L - 1;
			for (int n = 0; n < L; n++)
				window[n] = (float)(1.0 - 1.93 * Math.Cos(2.0 * Math.PI * n / N) + 1.29 * Math.Cos(4.0 * Math.PI * n / N) - 0.388 * Math.Cos(6.0 * Math.PI * n / N) + 0.0322 * Math.Cos(8.0 * Math.PI * n / N));
		}

        public unsafe static void window_tukey(float* window, int L, double p)
		{
            int z = 0;
            int Np = (int)(p / 2.0 * L) - z;
			if (Np > 0)
			{
                for (int n = 0; n < z; n++)
                    window[n] = window[L - n - 1] = 0;
                for (int n = 0; n < Np - 1; n++)
                    window[n + z] = window[L - n - 1 - z] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * (n + 1) / Np));
                for (int n = z + Np - 1; n < L - z - Np + 1; n++)
                    window[n] = 1.0F;
			}
		}

        public unsafe static void window_punchout_tukey(float* window, int L, double p, double p1, double start, double end)
        {
            int start_n = (int)(start * L);
            int end_n = (int)(end * L);
            int Np = (int)(p / 2.0 * L);
            int Np1 = (int)(p1 / 2.0 * L);
            int i, n = 0;

            if (start_n != 0)
            {
                for (i = 1; n < Np; n++, i++)
                    window[n] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * i / Np));
                for (; n < start_n - Np1; n++)
                    window[n] = 1.0f;
                for (i = Np1; n < start_n; n++, i--)
                    window[n] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * i / Np1));
            }
            for (; n < end_n; n++)
		        window[n] = 0.0f;
            if (end_n != L)
            {
                for (i = 1; n < end_n + Np1; n++, i++)
                    window[n] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * i / Np1));
                for (; n < L - Np; n++)
                    window[n] = 1.0f;
                for (i = Np; n < L; n++, i--)
                    window[n] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * i / Np));
            }
        }

        public unsafe static void window_hann(float* window, int L)
		{
			int N = L - 1;
			for (int n = 0; n < L; n++)
				window[n] = (float)(0.5 - 0.5 * Math.Cos(2.0 * Math.PI * n / N));
		}

		private static short sign_only(int val)
		{
			return (short)((val >> 31) + ((val - 1) >> 31) + 1);
		}

#if XXX
		static public unsafe void
			compute_corr_int(/*const*/ short* data1, short* data2, int len, int min, int lag, int* autoc)
		{
			for (int i = min; i <= lag; ++i)
			{
				int temp = 0;
				int temp2 = 0;

				for (int j = 0; j <= lag - i; ++j)
					temp += data1[j + i] * data2[j];

				for (int j = lag + 1 - i; j < len - i; j += 2)
				{
					temp += data1[j + i] * data2[j];
					temp2 += data1[j + i + 1] * data2[j + 1];
				}
				autoc[i] = temp + temp2;
			}
		}
#endif

		/**
		 * Calculates autocorrelation data from audio samples
		 * A window function is applied before calculation.
		 */
		static public unsafe void
            compute_autocorr(/*const*/ int* data, float* window, int len, int min, int lag, double* autoc)
		{
#if FPAC
			short* data1 = stackalloc short[len + 1];
			short* data2 = stackalloc short[len + 1];
			int* c1 = stackalloc int[lpc.MAX_LPC_ORDER + 1];
			int* c2 = stackalloc int[lpc.MAX_LPC_ORDER + 1];
			int* c3 = stackalloc int[lpc.MAX_LPC_ORDER + 1];
			int* c4 = stackalloc int[lpc.MAX_LPC_ORDER + 1];

			for (int i = 0; i < len; i++)
			{
				int val = (int)(data[i] * window[i]);
				data1[i] = (short)(sign_only(val) * (Math.Abs(val) >> 9));
				data2[i] = (short)(sign_only(val) * (Math.Abs(val) & 0x1ff));
			}
			data1[len] = 0;
			data2[len] = 0;

			compute_corr_int(data1, data1, len, min, lag, c1);
			compute_corr_int(data1, data2, len, min, lag, c2);
			compute_corr_int(data2, data1, len, min, lag, c3);
			compute_corr_int(data2, data2, len, min, lag, c4);
			
			for (int coeff = min; coeff <= lag; coeff++)
			    autoc[coeff] = (c1[coeff] * (double)(1 << 18) + (c2[coeff] + c3[coeff]) * (double)(1 << 9) + c4[coeff]);
#else
#if XXX
            if (min == 0 && lag >= 4)
            {
                int* pdata = data;
                float* pwindow = window;

                double temp0 = 1.0;
                double temp1 = 1.0;
                double temp2 = 1.0;
                double temp3 = 1.0;
                double temp4 = 1.0;

                double c0 = *(pdata++) * *(pwindow++);
                float c1 = *(pdata++) * *(pwindow++);
                float c2 = *(pdata++) * *(pwindow++);
                float c3 = *(pdata++) * *(pwindow++);
                float c4 = *(pdata++) * *(pwindow++);

                int* finish = data + len;

                while (pdata <= finish)
                {
                    temp0 += c0 * c0;
                    temp1 += c0 * c1;
                    temp2 += c0 * c2;
                    temp3 += c0 * c3;
                    temp4 += c0 * c4;

                    c0 = c1;
                    c1 = c2;
                    c2 = c3;
                    c3 = c4;
                    c4 = *(pdata++) * *(pwindow++);
                }

                temp0 += c0 * c0;
                temp1 += c0 * c1;
                temp2 += c0 * c2;
                temp3 += c0 * c3;
                c0 = c1;
                c1 = c2;
                c2 = c3;
                temp0 += c0 * c0;
                temp1 += c0 * c1;
                temp2 += c0 * c2;
                c0 = c1;
                c1 = c2;
                temp0 += c0 * c0;
                temp1 += c0 * c1;
                c0 = c1;
                temp0 += c0 * c0;
                
                autoc[0] += temp0;
                autoc[1] += temp1;
                autoc[2] += temp2;
                autoc[3] += temp3;
                autoc[4] += temp4;
                min = 5;

                if (lag < min) return;
            }
#endif
            double* data1 = stackalloc double[len];
            int i;

            for (i = 0; i < len; i++)
                data1[i] = data[i] * window[i];

            for (i = min; i <= lag; ++i)
			{
				double temp = 0;
				double temp2 = 0;
                double* pdata = data1;
				double* finish = data1 + len - 1 - i;

                while (pdata < finish)
                {
                    temp += pdata[i] * (*pdata++);
                    temp2 += pdata[i] * (*pdata++);
                }
                if (pdata <= finish)
                    temp += pdata[i] * (*pdata++);

                autoc[i] += temp + temp2;
			}
#endif
		}

        static public unsafe void
            compute_autocorr_windowless(/*const*/ int* data, int len, int min, int lag, double* autoc)
        {
            // if databits*2 + log2(len) <= 64
#if !XXX
#if XXX
            if (min == 0 && lag >= 4)
            {
                long temp0 = 0;
                long temp1 = 0;
                long temp2 = 0;
                long temp3 = 0;
                long temp4 = 0;
                int* pdata = data;
                int* finish = data + len - 4;
                while (pdata < finish)
                {
                    long c0 = *(pdata++);
                    temp0 += c0 * c0;
                    temp1 += c0 * pdata[0];
                    temp2 += c0 * pdata[1];
                    temp3 += c0 * pdata[2];
                    temp4 += c0 * pdata[3];
                }
                {
                    long c0 = *(pdata++);
                    temp0 += c0 * c0;
                    temp1 += c0 * pdata[0];
                    temp2 += c0 * pdata[1];
                    temp3 += c0 * pdata[2];
                }
                {
                    long c0 = *(pdata++);
                    temp0 += c0 * c0;
                    temp1 += c0 * pdata[0];
                    temp2 += c0 * pdata[1];
                }
                {
                    long c0 = *(pdata++);
                    temp0 += c0 * c0;
                    temp1 += c0 * pdata[0];
                }
                {
                    long c0 = *(pdata++);
                    temp0 += c0 * c0;
                }
                autoc[0] += temp0;
                autoc[1] += temp1;
                autoc[2] += temp2;
                autoc[3] += temp3;
                autoc[4] += temp4;
                min = 5;

                if (lag < min) return;
            }
#endif
            for (int i = min; i <= lag; ++i)
            {
                long temp = 0;
                long temp2 = 0;
                int* pdata = data;
                int* finish = data + len - i - 1;
                while (pdata < finish)
                {
                    temp += (long)pdata[i] * (*pdata++);
                    temp2 += (long)pdata[i] * (*pdata++);
                }
                if (pdata <= finish)
                    temp += (long)pdata[i] * (*pdata++);
                autoc[i] += temp + temp2;
            }
#else
            for (int i = min; i <= lag; ++i)
            {
                double temp = 0;
                double temp2 = 0;
                int* pdata = data;
                int* finish = data + len - i - 1;

                while (pdata < finish)
                {
                    temp += (double)pdata[i] * (double)(*pdata++);
                    temp2 += (double)pdata[i] * (double)(*pdata++);
                }
                if (pdata <= finish)
                    temp += (double)pdata[i] * (double)(*pdata++);
                autoc[i] += temp + temp2;
            }
#endif
        }

        static public unsafe void
            compute_autocorr_windowless_large(/*const*/ int* data, int len, int min, int lag, double* autoc)
        {
            for (int i = min; i <= lag; ++i)
            {
                double temp = 0;
                double temp2 = 0;
                int* pdata = data;
                int* finish = data + len - i - 1;
                while (pdata < finish)
                {
                    temp += (long)pdata[i] * (*pdata++);
                    temp2 += (long)pdata[i] * (*pdata++);
                }
                if (pdata <= finish)
                    temp += (long)pdata[i] * (*pdata++);
                autoc[i] += temp + temp2;
            }
        }

        static public unsafe void
            compute_autocorr_glue(/*const*/ int* data, float* window, int offs, int offs1, int min, int lag, double* autoc)
        {
            double* data1 = stackalloc double[lag + lag];
            for (int i = -lag; i < lag; i++)
                data1[i + lag] = offs + i >= 0 && offs + i < offs1 ? data[offs + i] * window[offs + i] : 0;
            for (int i = min; i <= lag; ++i)
            {
                double temp = 0;
                double* pdata = data1 + lag - i;
                double* finish = data1 + lag;
                while (pdata < finish)
                    temp += pdata[i] * (*pdata++);
                autoc[i] += temp;
            }
        }

        static public unsafe void
            compute_autocorr_glue(/*const*/ int* data, int min, int lag, double* autoc)
        {
            for (int i = min; i <= lag; ++i)
            {
                long temp = 0;
                int* pdata = data - i;
                int* finish = data;
                while (pdata < finish)
                    temp += (long)pdata[i] * (*pdata++);
                autoc[i] += temp;
            }
        }

        /**
		 * Levinson-Durbin recursion.
		 * Produces LPC coefficients from autocorrelation data.
		 */
		public static unsafe void
		compute_lpc_coefs(uint max_order, double* reff, float* lpc/*[][MAX_LPC_ORDER]*/)
		{
			double* lpc_tmp = stackalloc double[MAX_LPC_ORDER];

			if (max_order > MAX_LPC_ORDER)
				throw new Exception("weird");

			for (int i = 0; i < max_order; i++)
				lpc_tmp[i] = 0;

			for (int i = 0; i < max_order; i++)
			{
				double r = reff[i];
				int i2 = (i >> 1);
				lpc_tmp[i] = r;
				for (int j = 0; j < i2; j++)
				{
					double tmp = lpc_tmp[j];
					lpc_tmp[j] += r * lpc_tmp[i - 1 - j];
					lpc_tmp[i - 1 - j] += r * tmp;
				}

				if (0 != (i & 1))
					lpc_tmp[i2] += lpc_tmp[i2] * r;

				for (int j = 0; j <= i; j++)
					lpc[i * MAX_LPC_ORDER + j] = (float)-lpc_tmp[j];
			}
		}

		public static unsafe void
		compute_schur_reflection(/*const*/ double* autoc, uint max_order,
							  double* reff/*[][MAX_LPC_ORDER]*/, double * err)
		{
			double* gen0 = stackalloc double[MAX_LPC_ORDER];
			double* gen1 = stackalloc double[MAX_LPC_ORDER];

			// Schur recursion
			for (uint i = 0; i < max_order; i++)
				gen0[i] = gen1[i] = autoc[i + 1];

			double error = autoc[0];
			reff[0] = -gen1[0] / error;
			error += gen1[0] * reff[0];
			err[0] = error;
			for (uint i = 1; i < max_order; i++)
			{
				for (uint j = 0; j < max_order - i; j++)
				{
					gen1[j] = gen1[j + 1] + reff[i - 1] * gen0[j];
					gen0[j] = gen1[j + 1] * reff[i - 1] + gen0[j];
				}
				reff[i] = -gen1[0] / error;
				error += gen1[0] * reff[i];
				err[i] = error;
			}
		}

		/**
		 * Quantize LPC coefficients
		 */
		public static unsafe void
		quantize_lpc_coefs(float* lpc_in, int order, uint precision, int* lpc_out,
						   out int shift, int max_shift, int zero_shift)
		{
			int i;
			float d, cmax, error;
			int qmax;
			int sh, q;

			// define maximum levels
			qmax = (1 << ((int)precision - 1)) - 1;

			// find maximum coefficient value
			cmax = 0.0F;
			for (i = 0; i < order; i++)
			{
				d = Math.Abs(lpc_in[i]);
				if (d > cmax)
					cmax = d;
			}
			// if maximum value quantizes to zero, return all zeros
			if (cmax * (1 << max_shift) < 1.0)
			{
				shift = zero_shift;
				for (i = 0; i < order; i++)
					lpc_out[i] = 0;
				return;
			}

			// calculate level shift which scales max coeff to available bits
			sh = max_shift;
			while ((cmax * (1 << sh) > qmax) && (sh > 0))
			{
				sh--;
			}

			// since negative shift values are unsupported in decoder, scale down
			// coefficients instead
			if (sh == 0 && cmax > qmax)
			{
				float scale = ((float)qmax) / cmax;
				for (i = 0; i < order; i++)
				{
					lpc_in[i] *= scale;
				}
			}

			// output quantized coefficients and level shift
			error = 0;
			for (i = 0; i < order; i++)
			{
				error += lpc_in[i] * (1 << sh);
				q = (int)(error + 0.5);
				if (q < -(qmax+1)) q = -(qmax + 1);
				if (q > qmax) q = qmax;
				error -= q;
				lpc_out[i] = q;
			}
			shift = sh;
		}

		private static unsafe ulong
		encode_residual_partition(int* s, int* r, int* seg_end, int* coefs, int shift, int order)
		{
			ulong sum = 0ul;
			int c0 = coefs[0];
			int c1 = coefs[1];
			switch (order)
			{
				case 1:
					while (s < seg_end)
					{
						int pred = c0 * *(s++);
						//*(r++) = *s - (pred >> shift);
						int d = *(r++) = *s - (pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
					}
					break;
				case 2:
					while (s < seg_end)
					{
						int pred = c1 * *(s++);
						pred += c0 * *(s++);
						int d = *(r++) = *(s--) - (pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
					}
					break;
				case 3:
					while (s < seg_end)
					{
						int pred = coefs[2] * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						int d = *(r++) = *s - (pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
						s -= 2;
					}
					break;
				case 4:
					while (s < seg_end)
					{
						int* c = coefs + order - 1;
						int pred =
							*(c--) * *(s++) + *(c--) * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						int d = *(r++) = *s - (pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
						s -= 3;
					}
					break;
				case 5:
					while (s < seg_end)
					{
						int* c = coefs + order - 1;
						int pred =
							*(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						int d = *(r++) = *s - (pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
						s -= 4;
					}
					break;
				case 6:
					while (s < seg_end)
					{
						int* c = coefs + order - 1;
						int pred =
							*(c--) * *(s++) + *(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						int d = *(r++) = *s - (pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
						s -= 5;
					}
					break;
				case 7:
					while (s < seg_end)
					{
						int* c = coefs + order - 1;
						int pred =
							*(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						int d = *(r++) = *s - (pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
						s -= 6;
					}
					break;
				case 8:
					while (s < seg_end)
					{
						int* c = coefs + order - 1;
						int pred =
							*(c--) * *(s++) + *(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						int d = *(r++) = *s - (pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
						s -= 7;
					}
					break;
				case 9:
					while (s < seg_end)
					{
						int* c = coefs + order - 1;
						int pred =
							*(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						int d = *(r++) = *s - (pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
						s -= 8;
					}
					break;
				case 10:
					while (s < seg_end)
					{
						int* c = coefs + order - 1;
						int pred =
							*(c--) * *(s++) + *(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						int d = *(r++) = *s - (pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
						s -= 9;
					}
					break;
				case 11:
					while (s < seg_end)
					{
						int* c = coefs + order - 1;
						int pred =
							*(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						int d = *(r++) = *s - (pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
						s -= 10;
					}
					break;
				case 12:
					while (s < seg_end)
					{
						int* c = coefs + order - 1;
						int pred =
							*(c--) * *(s++) + *(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						int d = *(r++) = *s - (pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
						s -= 11;
					}
					break;
				default:
					while (s < seg_end)
					{
						int pred = 0;
						int* c = coefs + order - 1;
						int* c11 = coefs + 11;
						while (c > c11)
							pred += *(c--) * *(s++);
						pred +=
							*(c--) * *(s++) + *(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							*(c--) * *(s++) + *(c--) * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						int d = *(r++) = *s - (pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
						s -= order - 1;
					}
					break;
			}
			return sum;
		}

		public static unsafe void
		encode_residual(int* res, int* smp, int n, int order,
			int* coefs, int shift, ulong* sums, int pmax)
		{
			for (int i = 0; i < order; i++)
				res[i] = smp[i];

			int* s = smp;
			int* s_end = smp + n - order;
			int* seg_end = s + (n >> pmax) - order;
			int* r = res + order;
			while (s < s_end)
			{
				*(sums++) = encode_residual_partition(s, r, seg_end, coefs, shift, order);
				r += seg_end - s;
				s = seg_end;
				seg_end += n >> pmax;
			}
		}

		private static unsafe ulong
		encode_residual_long_partition(int* s, int* r, int* seg_end, int* coefs, int shift, int order)
		{
			ulong sum = 0ul;
			int c0 = coefs[0];
			int c1 = coefs[1];
			switch (order)
			{
				case 1:
					while (s < seg_end)
					{
						long pred = c0 * (long)*(s++);
						int d = *(r++) = *s - (int)(pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
					}
					break;
				case 2:
					while (s < seg_end)
					{
						long pred = c1 * (long)*(s++);
						pred += c0 * (long)*(s++);
						int d = *(r++) = *(s--) - (int)(pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
					}
					break;
				case 3:
					while (s < seg_end)
					{
						long pred = coefs[2] * (long)*(s++);
						pred += c1 * (long)*(s++);
						pred += c0 * (long)*(s++);
						int d = *(r++) = *s - (int)(pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
						s -= 2;
					}
					break;
				case 4:
					while (s < seg_end)
					{
						long pred = coefs[3] * (long)*(s++);
						pred += coefs[2] * (long)*(s++);
						pred += c1 * (long)*(s++);
						pred += c0 * (long)*(s++);
						int d = *(r++) = *s - (int)(pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
						s -= 3;
					}
					break;
				case 5:
					while (s < seg_end)
					{
						long pred = coefs[4] * (long)*(s++);
						pred += coefs[3] * (long)*(s++);
						pred += coefs[2] * (long)*(s++);
						pred += c1 * (long)*(s++);
						pred += c0 * (long)*(s++);
						int d = *(r++) = *s - (int)(pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
						s -= 4;
					}
					break;
				case 6:
					while (s < seg_end)
					{
						long pred = coefs[5] * (long)*(s++);
						pred += coefs[4] * (long)*(s++);
						pred += coefs[3] * (long)*(s++);
						pred += coefs[2] * (long)*(s++);
						pred += c1 * (long)*(s++);
						pred += c0 * (long)*(s++);
						int d = *(r++) = *s - (int)(pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
						s -= 5;
					}
					break;
				case 7:
					while (s < seg_end)
					{
						long pred = coefs[6] * (long)*(s++);
						pred += coefs[5] * (long)*(s++);
						pred += coefs[4] * (long)*(s++);
						pred += coefs[3] * (long)*(s++);
						pred += coefs[2] * (long)*(s++);
						pred += c1 * (long)*(s++);
						pred += c0 * (long)*(s++);
						int d = *(r++) = *s - (int)(pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
						s -= 6;
					}
					break;
				case 8:
					while (s < seg_end)
					{
						long pred = coefs[7] * (long)*(s++);
						pred += coefs[6] * (long)*(s++);
						pred += coefs[5] * (long)*(s++);
						pred += coefs[4] * (long)*(s++);
						pred += coefs[3] * (long)*(s++);
						pred += coefs[2] * (long)*(s++);
						pred += c1 * (long)*(s++);
						pred += c0 * (long)*(s++);
						int d = *(r++) = *s - (int)(pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
						s -= 7;
					}
					break;
				default:
					while (s < seg_end)
					{
						long pred = 0;
						int* co = coefs + order - 1;
						int* c7 = coefs + 7;
						while (co > c7)
							pred += *(co--) * (long)*(s++);
						pred += coefs[7] * (long)*(s++);
						pred += coefs[6] * (long)*(s++);
						pred += coefs[5] * (long)*(s++);
						pred += coefs[4] * (long)*(s++);
						pred += coefs[3] * (long)*(s++);
						pred += coefs[2] * (long)*(s++);
						pred += c1 * (long)*(s++);
						pred += c0 * (long)*(s++);
						int d = *(r++) = *s - (int)(pred >> shift);
						sum += (uint)((d << 1) ^ (d >> 31));
						s -= order - 1;
					}
					break;
			}
			return sum;
		}

		public static unsafe void
		encode_residual_long(int* res, int* smp, int n, int order,
			int* coefs, int shift, ulong* sums, int pmax)
		{
			for (int i = 0; i < order; i++)
				res[i] = smp[i];

			int* s = smp;
			int* s_end = smp + n - order;
			int* seg_end = s + (n >> pmax) - order;
			int* r = res + order;
			while (s < s_end)
			{
				*(sums++) = encode_residual_long_partition(s, r, seg_end, coefs, shift, order);
				r += seg_end - s;
				s = seg_end;
				seg_end += n >> pmax;
			}
		}

    	public static unsafe void
		decode_residual(int* res, int* smp, int n, int order,
			int* coefs, int shift)
		{
			for (int i = 0; i < order; i++)
				smp[i] = res[i];

			int* s = smp;
			int* r = res + order;
			int c0 = coefs[0];
			int c1 = coefs[1];
			switch (order)
			{
				case 1:
					for (int i = n - order; i > 0; i--)
					{
						int pred = c0 * *(s++);
						*s = *(r++) + (pred >> shift);
					}
					break;
				case 2:
					for (int i = n - order; i > 0; i--)
					{
						int pred = c1 * *(s++) + c0 * *(s++);
						*(s--) = *(r++) + (pred >> shift);
					}
					break;
				case 3:
					for (int i = n - order; i > 0; i--)
					{
						int* co = coefs + order - 1;
						int pred =
							*(co--) * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						*s = *(r++) + (pred >> shift);
						s -= 2;
					}
					break;
				case 4:
					for (int i = n - order; i > 0; i--)
					{
						int* co = coefs + order - 1;
						int pred =
							*(co--) * *(s++) + *(co--) * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						*s = *(r++) + (pred >> shift);
						s -= 3;
					}
					break;
				case 5:
					for (int i = n - order; i > 0; i--)
					{
						int* co = coefs + order - 1;
						int pred =
							*(co--) * *(s++) +
							*(co--) * *(s++) + *(co--) * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						*s = *(r++) + (pred >> shift);
						s -= 4;
					}
					break;
				case 6:
					for (int i = n - order; i > 0; i--)
					{
						int* co = coefs + order - 1;
						int pred =
							*(co--) * *(s++) + *(co--) * *(s++) +
							*(co--) * *(s++) + *(co--) * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						*s = *(r++) + (pred >> shift);
						s -= 5;
					}
					break;
				case 7:
					for (int i = n - order; i > 0; i--)
					{
						int* co = coefs + order - 1;
						int pred =
							*(co--) * *(s++) +
							*(co--) * *(s++) + *(co--) * *(s++) +
							*(co--) * *(s++) + *(co--) * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						*s = *(r++) + (pred >> shift);
						s -= 6;
					}
					break;
				case 8:
					for (int i = n - order; i > 0; i--)
					{
						int* co = coefs + order - 1;
						int pred =
							*(co--) * *(s++) + *(co--) * *(s++) +
							*(co--) * *(s++) + *(co--) * *(s++) +
							*(co--) * *(s++) + *(co--) * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						*s = *(r++) + (pred >> shift);
						s -= 7;
					}
					break;
				case 9:
					for (int i = n - order; i > 0; i--)
					{
						int* co = coefs + order - 1;
						int pred =
							*(co--) * *(s++) +
							*(co--) * *(s++) + *(co--) * *(s++) +
							*(co--) * *(s++) + *(co--) * *(s++) +
							*(co--) * *(s++) + *(co--) * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						*s = *(r++) + (pred >> shift);
						s -= 8;
					}
					break;
				case 10:
					for (int i = n - order; i > 0; i--)
					{
						int* co = coefs + order - 1;
						int pred =
							*(co--) * *(s++) + *(co--) * *(s++) +
							*(co--) * *(s++) + *(co--) * *(s++) +
							*(co--) * *(s++) + *(co--) * *(s++) +
							*(co--) * *(s++) + *(co--) * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						*s = *(r++) + (pred >> shift);
						s -= 9;
					}
					break;
				case 11:
					for (int i = n - order; i > 0; i--)
					{
						int* co = coefs + order - 1;
						int pred =
							*(co--) * *(s++) +
							*(co--) * *(s++) + *(co--) * *(s++) +
							*(co--) * *(s++) + *(co--) * *(s++) +
							*(co--) * *(s++) + *(co--) * *(s++) +
							*(co--) * *(s++) + *(co--) * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						*s = *(r++) + (pred >> shift);
						s -= 10;
					}
					break;
				case 12:
					for (int i = n - order; i > 0; i--)
					{
						int* co = coefs + order - 1;
						int pred = 
							*(co--) * *(s++) + *(co--) * *(s++) +
							*(co--) * *(s++) + *(co--) * *(s++) +
							*(co--) * *(s++) + *(co--) * *(s++) +
							*(co--) * *(s++) + *(co--) * *(s++) +
							*(co--) * *(s++) + *(co--) * *(s++) +
							c1 * *(s++) + c0 * *(s++);
						*s = *(r++) + (pred >> shift);
						s -= 11;
					}
					break;
				default:
					for (int i = order; i < n; i++)
					{
						s = smp + i - order;
						int pred = 0;
						int* co = coefs + order - 1;
						int* c7 = coefs + 7;
						while (co > c7)
							pred += *(co--) * *(s++);
						pred += coefs[7] * *(s++);
						pred += coefs[6] * *(s++);
						pred += coefs[5] * *(s++);
						pred += coefs[4] * *(s++);
						pred += coefs[3] * *(s++);
						pred += coefs[2] * *(s++);
						pred += c1 * *(s++);
						pred += c0 * *(s++);
						*s = *(r++) + (pred >> shift);
					}
					break;
			}
		}
		public static unsafe void
		decode_residual_long(int* res, int* smp, int n, int order,
			int* coefs, int shift)
		{
			for (int i = 0; i < order; i++)
				smp[i] = res[i];

			int* s = smp;
			int* r = res + order;
			int c0 = coefs[0];
			int c1 = coefs[1];
			switch (order)
			{
				case 1:
					for (int i = n - order; i > 0; i--)
					{
						long pred = c0 * (long)*(s++);
						*s = *(r++) + (int)(pred >> shift);
					}
					break;
				case 2:
					for (int i = n - order; i > 0; i--)
					{
						long pred = c1 * (long)*(s++);
						pred += c0 * (long)*(s++);
						*(s--) = *(r++) + (int)(pred >> shift);
					}
					break;
				case 3:
					for (int i = n - order; i > 0; i--)
					{
						long pred = coefs[2] * (long)*(s++);
						pred += c1 * (long)*(s++);
						pred += c0 * (long)*(s++);
						*s = *(r++) + (int)(pred >> shift);
						s -= 2;
					}
					break;
				case 4:
					for (int i = n - order; i > 0; i--)
					{
						long pred = coefs[3] * (long)*(s++);
						pred += coefs[2] * (long)*(s++);
						pred += c1 * (long)*(s++);
						pred += c0 * (long)*(s++);
						*s = *(r++) + (int)(pred >> shift);
						s -= 3;
					}
					break;
				case 5:
					for (int i = n - order; i > 0; i--)
					{
						long pred = coefs[4] * (long)*(s++);
						pred += coefs[3] * (long)*(s++);
						pred += coefs[2] * (long)*(s++);
						pred += c1 * (long)*(s++);
						pred += c0 * (long)*(s++);
						*s = *(r++) + (int)(pred >> shift);
						s -= 4;
					}
					break;
				case 6:
					for (int i = n - order; i > 0; i--)
					{
						long pred = coefs[5] * (long)*(s++);
						pred += coefs[4] * (long)*(s++);
						pred += coefs[3] * (long)*(s++);
						pred += coefs[2] * (long)*(s++);
						pred += c1 * (long)*(s++);
						pred += c0 * (long)*(s++);
						*s = *(r++) + (int)(pred >> shift);
						s -= 5;
					}
					break;
				case 7:
					for (int i = n - order; i > 0; i--)
					{
						long pred = coefs[6] * (long)*(s++);
						pred += coefs[5] * (long)*(s++);
						pred += coefs[4] * (long)*(s++);
						pred += coefs[3] * (long)*(s++);
						pred += coefs[2] * (long)*(s++);
						pred += c1 * (long)*(s++);
						pred += c0 * (long)*(s++);
						*s = *(r++) + (int)(pred >> shift);
						s -= 6;
					}
					break;
				case 8:
					for (int i = n - order; i > 0; i--)
					{
						long pred = coefs[7] * (long)*(s++);
						pred += coefs[6] * (long)*(s++);
						pred += coefs[5] * (long)*(s++);
						pred += coefs[4] * (long)*(s++);
						pred += coefs[3] * (long)*(s++);
						pred += coefs[2] * (long)*(s++);
						pred += c1 * (long)*(s++);
						pred += c0 * (long)*(s++);
						*s = *(r++) + (int)(pred >> shift);
						s -= 7;
					}
					break;
				default:
					for (int i = order; i < n; i++)
					{
						s = smp + i - order;
						long pred = 0;
						int* co = coefs + order - 1;
						int* c7 = coefs + 7;
						while (co > c7)
							pred += *(co--) * (long)*(s++);
						pred += coefs[7] * (long)*(s++);
						pred += coefs[6] * (long)*(s++);
						pred += coefs[5] * (long)*(s++);
						pred += coefs[4] * (long)*(s++);
						pred += coefs[3] * (long)*(s++);
						pred += coefs[2] * (long)*(s++);
						pred += c1 * (long)*(s++);
						pred += c0 * (long)*(s++);
						*s = *(r++) + (int)(pred >> shift);
					}
					break;
			}
		}
	}
}
