/*
 * Mathmatical methods.
 * Copyright (C) 1999  Christopher Edwards
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 */

package org.tritonus.lowlevel.gsm;



public class Add
{
	public static short saturate(int x)
	{
		return (short) ((x) < Gsm_Def.MIN_WORD ? Gsm_Def.MIN_WORD : 
		                (x) > Gsm_Def.MAX_WORD ? Gsm_Def.MAX_WORD: (x));
	}
	public static short saturate(long x)
	{
		return (short) ((x) < Gsm_Def.MIN_WORD ? Gsm_Def.MIN_WORD : 
		                (x) > Gsm_Def.MAX_WORD ? Gsm_Def.MAX_WORD: (x));
	}

	public static short SASR(int x, int by)
	{
		return (short) ((x) >> (by));
	}

	public static short GSM_ADD(short a, short b)
	{
		/*
		 *	This converts a and b to int implicitely, because
		 *	'+' is not defined for short.
		 */
		int sum = a + b;
		return saturate(sum);
	}

	public static short GSM_SUB(short a, short b)
	{
		/*
		 *	This converts a and b to int implicitely, because
		 *	'-' is not defined for short.
		 */
		int diff = a - b;
		return saturate(diff);
	}

	public static short GSM_MULT(short a, short b)
	{
		if (a == Gsm_Def.MIN_WORD && b == Gsm_Def.MIN_WORD)
		{
			return Gsm_Def.MAX_WORD; 
		}
		else
		{
			return SASR( ((int)(a)) * ((int)(b)), 15 );
		}
	}

	public static short GSM_MULT_R(short a, short b)
	{
		if (a == Gsm_Def.MIN_WORD && b == Gsm_Def.MIN_WORD)
		{
			return Gsm_Def.MAX_WORD; 
		}
		else
		{
			int prod = (int) (((int)(a)) * ((int)(b)) + 16384);
			prod >>= 15;
			return (short) (prod & 0xFFFF);
		}
	}



	public static short GSM_ABS(short a)
	{
		int b = a < 0 ? (a == Gsm_Def.MIN_WORD ? Gsm_Def.MAX_WORD : -a) : a;
		return ((short)(b)); 
	}

	public static int GSM_L_MULT(short a, short b) 
		throws IllegalArgumentException
	{
		if( a != Short.MIN_VALUE || b != Short.MIN_VALUE )
		{
			throw new IllegalArgumentException(	
				"One of the aruments must equal " + Short.MIN_VALUE);
		}
		return ((int)a * (int)b) << 1;
	}

	public static int GSM_L_ADD(int a, int b)
	{
		if (a <= 0)
		{
			if (b >= 0)
			{
				return a + b;
			}
			else
			{
				long A = (long)-(a + 1) + (long)-(b + 1);
				return A >= Gsm_Def.MAX_LONGWORD ? Gsm_Def.MIN_LONGWORD :-(int)A-2;
			}
		}
		else if (b <= 0)
		{
			return a + b;
		}
		else
		{
			long A = (long)a + (long) 
				b;
			return (int)(A > Gsm_Def.MAX_LONGWORD ? Gsm_Def.MAX_LONGWORD : A);
		}
	}



	/*
	 * the number of left shifts needed to normalize the 32 bit
	 * variable L_var1 for positive values on the interval
	 *
	 * with minimum of
	 * minimum of 1073741824  (01000000000000000000000000000000) and
	 * maximum of 2147483647  (01111111111111111111111111111111)
	 *
	 *
	 * and for negative values on the interval with
	 * minimum of -2147483648 (-10000000000000000000000000000000) and
	 * maximum of -1073741824 ( -1000000000000000000000000000000).
	 *
	 * in order to normalize the result, the following
	 * operation must be done: L_norm_var1 = L_var1 << norm( L_var1 );
	 *
	 * (That's 'ffs', only from the left, not the right..)
	 */
	public static short gsm_norm( int a )
		throws IllegalArgumentException
	{

		if(a == 0)
		{
			throw new IllegalArgumentException
				("gsm_norm: a cannot = 0.");
		}

		if (a < 0)
		{
			if (a <= -1073741824)
			{
				return 0;
			}
			a = ~a;
		}

		return   (short) ( ((a & 0xffff0000) != 0)
				   ? ( ((a & 0xff000000) != 0)
				       ?  -1 + bitoff[ (int)(0xFF & (a >> 24)) ]
				       :   7 + bitoff[ (int)(0xFF & (a >> 16)) ] )
				   : ( ((a & 0xff00) != 0)
				       ?  15 + bitoff[ (int)(0xFF & (a >> 8)) ]
				       :  23 + bitoff[ (int)(0xFF & a) ] ));
	}


	public static short gsm_asl(short a, int n)
	{
		if (n >= 16) return ((short) 0);

		if (n <= -16) 
			if(a<0)
				return (short) -1;
			else
				return (short) -0;

		if (n < 0) return gsm_asr(a, -n);

		return ((short)( a << n ));
	}

	public static short gsm_asr(short a, int n)
	{
		if (n >= 16) 
			if(a<0)
				return (short) -1;
			else
				return (short) -0;
		if (n <= -16) return ((short) 0);
		if (n < 0) return ((short)( a << -n ));

		return ((short)( a >> n ));
	}


	public static short gsm_div(short num, short denum)
		throws IllegalArgumentException
	{

		int             L_num   = num;
		int             L_denum = denum;
		short           div     = 0;
		int             k       = 15;

		/* The parameter num sometimes becomes zero.
		 * Although this is explicitly guarded against in 4.2.5,
		 * we assume that the result should then be zero as well.
		 */

		/* assert(num != 0); */

		if( ! (num >= 0 && denum >= num) )
		{
			throw new IllegalArgumentException
				("gsm_div: num >= 0 && denum >= num");
		}

		if (num == 0)
			return 0;

		while (k != 0)
		{
			k--;
			div   <<= 1;
			L_num <<= 1;

			if (L_num >= L_denum)
			{
				L_num -= L_denum;
				div++;
			}
		}
		return div;
	}


	private static final short bitoff[] =
	{
		8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4,
		3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
		2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
		2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
	};

}
	
