/********************************************************
             SV_basic_op.c
*********************************************************/

#include "SV_basic_op.h"


int SV_Overflow = 0;
int SV_Carry = 0;

#define LOG10QFIX /* fnLog10(2) */ -605866608
#define LOG10FIX  /* fnLog10(1) */ -626068080

#define SV_BASIC_OPT_NORM_L
#if defined(_WIN32)     // // predefined macro for VS
static int CountLeadZeros(int v)
{
	if (v == 0) return sizeof(int) * 8;
	int c = 0; // c will be the number of zero bits on the right
	if ((v & 0xFFFF0000) == 0) { c += 16; v <<= 16; }
	if ((v & 0xFF000000) == 0) { c += 8; v <<= 8; }
	if ((v & 0xF0000000) == 0) { c += 4; v <<= 4; }
	if ((v & 0xc0000000) == 0) { c += 2; v <<= 2; }
	if ((v & 0x80000000) == 0) { c += 1; v <<= 1; }
	return c;
}
#elif (__GNUC__>=5)    // predefined macro for GCC armclang (__GNUC__==5)
static int inline CountLeadZeros(int v)
{
	int res;
	__asm ("CLZ %[result], %[input_v]"
	: [result] "=r" (res)
		: [input_v] "r" (v)
		);
	return res;
	//    return _arm_clz(v);    // there is no arm untrinsic for GCC :-(
}
#else    // predefined macro for ARMCC Compiler 5 (__GNUC__==4)
static int inline CountLeadZeros(int v)
{
	int res;
	__asm {
		clz res, v
	};
	return res;
	//    return __clz(v);     // or you may use this intrinsic instead of inline asm
}
#endif


/* Short */
extern short SV_shl(short var1, short var2)
{
	short var_out;
	int result;

	if (var2 < 0)
	{
		var_out = SV_shr(var1, (short)(-var2));
	}
	else
	{
		result = (int)var1 *((int)1 << var2);
		if ((var2 > 15 && var1 != 0) || (result != (int)((short)result)))
		{
			var_out = (var1 > 0) ? SV_MAX_16 : SV_MIN_16;
		}
		else
		{
			var_out = SV_extract_l(result);
		}
	}

	return (var_out);
}

extern short SV_shr(short var1, short var2)
{
	short var_out;

	if (var2 < 0)
	{
		var_out = SV_shl(var1, (short)(-var2));
	}
	else
	{
		if (var2 >= 15)
		{
			var_out = (var1 < 0) ? -1 : 0;
		}
		else
		{
			if (var1 < 0)
			{
				var_out = ~((~var1) >> var2);
			}
			else
			{
				var_out = var1 >> var2;
			}
		}
	}

	return (var_out);
}

extern int SV_L_deposit_h(short var1)
{
	int L_var_out;

	L_var_out = (int)var1 << 16;

	return (L_var_out);
}

extern short SV_add(short var1, short var2)
{
	short var_out;
	int L_sum;

	L_sum = (int)var1 + var2;
	var_out = SV_saturate(L_sum);

	return (var_out);
}

extern short SV_sub(short var1, short var2)
{
	short var_out;
	int L_diff;

	L_diff = (int)var1 - var2;
	var_out = SV_saturate(L_diff);

	return (var_out);
}

extern short SV_abs_s(short var1)
{
	short var_out;

	if (var1 == (short)0X8000)
	{
		var_out = SV_MAX_16;
	}
	else
	{
		if (var1 < 0)
		{
			var_out = -var1;
		}
		else
		{
			var_out = var1;
		}
	}
	return (var_out);
}

extern short SV_mult_r(short var1, short var2)
{
	short var_out;
	int L_product_arr;
	L_product_arr = (int)var1 *(int)var2;       /* product */
	L_product_arr += (int)0x00004000L;      /* SV_round */
	L_product_arr &= (int)0xffff8000L;
	L_product_arr >>= 15;       /* shift */
	if (L_product_arr & (int)0x00010000L)   /* sign extend when necessary */
	{
		L_product_arr |= (int)0xffff0000L;
	}
	var_out = SV_saturate(L_product_arr);
	return (var_out);
}

extern short SV_div_s(short var1, short var2)
{
	short var_out = 0;
	short iteration;
	int L_num;
	int L_denom;

	if (var2 == 0) var2 = 1;

	if (var1 == 0)
	{
		var_out = 0;
	}
	else
	{
		if (var1 == var2)
		{
			var_out = SV_MAX_16;
		}
		else
		{
			L_num = SV_L_deposit_l(var1);
			L_denom = SV_L_deposit_l(var2);

			for (iteration = 0; iteration < 15; iteration++)
			{
				var_out <<= 1;
				L_num <<= 1;

				if (L_num >= L_denom)
				{
					L_num = SV_L_sub(L_num, L_denom);
					var_out = SV_add(var_out, 1);
				}
			}
		}
	}

	return (var_out);
}

/* Long */
extern int SV_L_shl(int L_var1, short var2)
{
	int L_var_out;

	if (var2 <= 0)
	{
		L_var_out = SV_L_shr(L_var1, (short)(-var2));
	}
	else
	{
		for (; var2 > 0; var2--)
		{
			if (L_var1 > (int)0X3fffffffL)
			{
				L_var_out = SV_MAX_32;
				break;
			}
			else
			{
				if (L_var1 < (int)0xc0000000L)
				{
					L_var_out = SV_MIN_32;
					break;
				}
			}
			L_var1 *= 2;
			L_var_out = L_var1;
		}
	}

	return (L_var_out);
}

extern int SV_L_shr(int L_var1, short var2)
{
	int L_var_out;

	if (var2 < 0)
	{
		L_var_out = SV_L_shl(L_var1, (short)(-var2));
	}
	else
	{
		if (var2 >= 31)
		{
			L_var_out = (L_var1 < 0L) ? -1 : 0;
		}
		else
		{
			if (L_var1 < 0)
			{
				L_var_out = ~((~L_var1) >> var2);
			}
			else
			{
				L_var_out = L_var1 >> var2;
			}
		}
	}

	return (L_var_out);
}

extern int SV_L_shr_r(int L_var1, short var2)
{
	int L_var_out;

	if (var2 > 31)
	{
		L_var_out = 0;
	}
	else
	{
		L_var_out = SV_L_shr(L_var1, var2);

		if (var2 > 0)
		{
			if ((L_var1 & ((int)1 << (var2 - 1))) != 0)
			{
				L_var_out++;
			}
		}
	}

	return (L_var_out);
}

extern short SV_L_norm(int L_var1)
{
	short var_out;

	if (L_var1 == 0)
	{
		var_out = 0;
	}
	else
	{
		if (L_var1 == (int)0xffffffffL)
		{
			var_out = 31;
		}
		else
		{
			if (L_var1 < 0)
			{
				L_var1 = ~L_var1;
			}
			for (var_out = 0; L_var1 < (int)0x40000000L; var_out++)
			{
				L_var1 <<= 1;
			}
		}
	}

	return (var_out);
}

extern short SV_saturate(int L_var1)
{
	short var_out;

	if (L_var1 > 0X00007fffL)
	{
		var_out = SV_MAX_16;
	}
	else if (L_var1 < (int)0xffff8000L)
	{
		var_out = SV_MIN_16;
	}
	else
	{
		var_out = SV_extract_l(L_var1);
	}

	return (var_out);
}

extern short SV_extract_h(int L_var1)
{
	short var_out;
	var_out = (short)(L_var1 >> 16);
	return (var_out);
}

extern short SV_extract_l(int L_var1)
{
	short var_out;
	var_out = (short)L_var1;
	return (var_out);
}

extern int SV_L_deposit_l(short var1)
{
	int L_var_out;

	L_var_out = (int)var1;

	return (L_var_out);
}

extern int SV_L_add(int L_var1, int L_var2)
{
	int L_var_out;

	L_var_out = L_var1 + L_var2;
	if (((L_var1 ^ L_var2) & SV_MIN_32) == 0)
	{
		if ((L_var_out ^ L_var1) & SV_MIN_32)
		{
			L_var_out = (L_var1 < 0) ? SV_MIN_32 : SV_MAX_32;
		}
	}

	return (L_var_out);
}

extern int SV_L_sub(int L_var1, int L_var2)
{
	int L_var_out;

	L_var_out = L_var1 - L_var2;

	if (((L_var1 ^ L_var2) & SV_MIN_32) != 0)
	{
		if ((L_var_out ^ L_var1) & SV_MIN_32)
		{
			L_var_out = (L_var1 < 0L) ? SV_MIN_32 : SV_MAX_32;
		}
	}
	return (L_var_out);
}

extern int SV_L_mult(short var1, short var2)
{
	int L_var_out;

	L_var_out = (int)var1 *(int)var2;

	if (L_var_out != (int)0x40000000L)
	{
		L_var_out *= 2;
	}
	else
	{
		L_var_out = SV_MAX_32;
	}
	return (L_var_out);
}

extern int SV_L_mpy_ls(int L_var2, short var1)
{
	int L_varOut;
	short swtemp;

	//#define MULT16_32_Q15(a,b) ADD32(SHL(MULT16_16((a),SHR((b),16)),1), SHR(MULT16_16SU((a),((b)&0x0000ffff)),15))
	swtemp = SV_shr(SV_extract_l(L_var2), 1);
	swtemp = (short)32767 & (short)swtemp;

	L_varOut = SV_L_mult(var1, swtemp);
	L_varOut = SV_L_shr(L_varOut, 15);
	L_varOut = SV_L_mac(L_varOut, var1, SV_extract_h(L_var2));

	return (L_varOut);
}

extern int SV_L_mpy_ll(int L_var1, int L_var2)
{
	short swLow1, swLow2, swHigh1, swHigh2;
	int L_varOut, L_low, L_mid1, L_mid2, L_mid;

	swLow1 = SV_shr(SV_extract_l(L_var1), 1);
	swLow1 = SV_MAX_16 & swLow1;

	swLow2 = SV_shr(SV_extract_l(L_var2), 1);
	swLow2 = SV_MAX_16 & swLow2;
	swHigh1 = SV_extract_h(L_var1);
	swHigh2 = SV_extract_h(L_var2);

	L_low = SV_L_mult(swLow1, swLow2);
	L_low = SV_L_shr(L_low, 16);

	L_mid1 = SV_L_mult(swHigh1, swLow2);
	L_mid1 = SV_L_shr(L_mid1, 1);
	L_mid = SV_L_add(L_mid1, L_low);

	L_mid2 = SV_L_mult(swHigh2, swLow1);
	L_mid2 = SV_L_shr(L_mid2, 1);
	L_mid = SV_L_add(L_mid, L_mid2);

	L_mid = SV_L_shr(L_mid, 14);
	L_varOut = SV_L_mac(L_mid, swHigh1, swHigh2);

	return (L_varOut);
}

extern int SV_L_mac(int L_var3, short var1, short var2)
{
	int L_var_out;
	int L_product;

	L_product = SV_L_mult(var1, var2);
	L_var_out = SV_L_add(L_var3, L_product);

	return (L_var_out);
}

extern int SV_L_msu(int L_var3, short var1, short var2)
{
	int L_var_out;
	int L_product;

	L_product = SV_L_mult(var1, var2);
	L_var_out = SV_L_sub(L_var3, L_product);
	return (L_var_out);
}

#ifdef USEOPT
extern int SV_L_divide_asm(int L_num, int L_denom);
int SV_L_divide(int L_num, int L_denom)
{
	return SV_L_divide_asm(L_num, L_denom);
}
#else
int SV_L_divide(int L_num, int L_denom)
{
	short approx;
	int L_div;

	if (L_num < 0 || L_denom < 0 || L_num > L_denom)
	{
		return (0);
	}

	/* First approximation: 1 / L_denom = 1/SV_extract_h(L_denom) */
	approx = SV_div_s((short)0x3fff, SV_extract_h(L_denom));

	/* 1/L_denom = approx * (2.0 - L_denom * approx) */
	L_div = SV_L_mpy_ls(L_denom, approx);
	L_div = SV_L_sub((int)0x7fffffffL, L_div);
	L_div = SV_L_mpy_ls(L_div, approx);

	/* L_num * (1/L_denom) */
	L_div = SV_L_mpy_ll(L_num, L_div);
	L_div = SV_L_shl(L_div, 2);

	return (L_div);
}
#endif

extern int fx_SV_32bit_Divide(int numerator, int denominator, short MIN_denominator, short Qbit)
{
	/*
	Q format for num and denom should be same
	output Q format : Q,31-Q
	*/
	short norm_shift_num, norm_shift_denom;
	int L_num, L_denom, Lout;

#ifdef USEOPT
	extern void fx_SV_32bit_Divide_subfunc(int num, int denom, short MIN_denom, short Qbit, short *norm_shift_num, int *L_num, short *norm_shift_denom, int *L_denom);
	fx_SV_32bit_Divide_subfunc(numerator, denominator, MIN_denominator, Qbit, &norm_shift_num, &L_num, &norm_shift_denom, &L_denom);
#else
	// numerator
	norm_shift_num = SV_L_norm(numerator);
	L_num = SV_L_shl(numerator, (short)(norm_shift_num - 1));

	//denominator
	norm_shift_denom = SV_L_norm(denominator);
	L_denom = SV_L_shl(denominator, norm_shift_denom);
	L_denom = MAX(L_denom, MIN_denominator); // normalization
#endif

	// divide
	Lout = SV_L_divide(L_num, L_denom);  //2
	Lout = SV_L_shr_r(Lout, (short)(Qbit - 1 + norm_shift_num - norm_shift_denom));

	return Lout;

}

extern INT64 SV_LL_shl(INT64 L_var1, short var2)
{
	INT64 L_var_out;

	if (var2 <= 0)
	{
		L_var_out = SV_LL_shr(L_var1, (short)(-var2));
	}
	else
	{
		for (; var2 > 0; var2--)
		{
			if (L_var1 > (INT64)0x3fffffffffffffffLL)
			{
				L_var_out = SV_MAX_64;
				break;
			}
			else
			{
				if (L_var1 < (INT64)0xc000000000000000LL)
				{
					L_var_out = SV_MIN_64;
					break;
				}
			}
			L_var1 *= 2;
			L_var_out = L_var1;
		}
	}

	return (L_var_out);
}

extern INT64 SV_LL_shr(INT64 L_var1, short var2)
{
	INT64 L_var_out;

	if (var2 < 0)
	{
		L_var_out = SV_LL_shl(L_var1, (short)(-var2));
	}
	else
	{
		if (var2 >= 63)
		{
			L_var_out = (L_var1 < 0LL) ? -1 : 0;
		}
		else
		{
			if (L_var1 < 0)
			{
				L_var_out = ~((~L_var1) >> var2);
			}
			else
			{
				L_var_out = L_var1 >> var2;
			}
		}
	}

	return (L_var_out);
}

extern int SV_LL_norm(INT64 L_var1)
{
	int var_out;

	if (L_var1 == 0)
	{
		var_out = 0;
	}
	else
	{
		if (L_var1 == (INT64)0xffffffffffffffffL)
		{
			var_out = 63;
		}
		else
		{
			if (L_var1 < 0)
			{
				L_var1 = ~L_var1;
			}
			for (var_out = 0; L_var1 < (INT64)0x4000000000000000L; var_out++)
			{
				L_var1 <<= 1;
			}
		}
	}

	return (var_out);
}

extern int SV_L_extract_h(INT64 L_var1)
{
	int var_out;
	var_out = (int)(L_var1 >> 32);
	return (var_out);
}

extern int fx_SV_64bit_Divide(INT64 numerator, INT64 denominator, short MIN_denominator, short Qbit)
{
	/*
	Q format for num and denom should be same
	output Q format : Q,31-Q
	*/
	short norm_shift_num, norm_shift_denom;
	int L_num, L_denom, Lout;
	INT64 LL_num;

	// numerator
	norm_shift_num = SV_LL_norm(numerator);
	LL_num = SV_LL_shl(numerator, (short)(norm_shift_num - 1));
	L_num = SV_L_extract_h(LL_num);

	//denominator
	norm_shift_denom = SV_LL_norm(denominator);
	LL_num = SV_LL_shl(denominator, norm_shift_denom);
	L_denom = SV_L_extract_h(LL_num);
	L_denom = MAX(L_denom, MIN_denominator); // normalization

												  // devide
	Lout = SV_L_divide(L_num, L_denom);  
	Lout = SV_L_shr_r(Lout, (short)(Qbit - 1 + norm_shift_num - norm_shift_denom));

	return Lout;

}

extern int SV_fnExp2(int L_Input)
{
	static short pswPCoefE[3] =
	{							/* c2,   c1,    c0 */
		0x15ef, 0x556f, 0x7fbd
	};
	short swTemp1, swTemp2, swTemp3, swTemp4;
	int LwIn;
	swTemp3 = 0x0020;

	/* determine normlization shift count */
	/* ---------------------------------- */
	swTemp1 = SV_extract_h(L_Input);
	LwIn = SV_L_mult(swTemp1, swTemp3);
	swTemp2 = SV_extract_h(LwIn);

	/* determine un-normalized shift count */
	/* ----------------------------------- */
	swTemp3 = -0x0001;
	swTemp4 = SV_sub(swTemp3, swTemp2);

	/* normalize input */
	/* --------------- */
	LwIn = LwIn & 0xffff;
	LwIn = SV_L_add(LwIn, SV_L_deposit_h(swTemp3));
	LwIn = SV_L_shr(LwIn, 1);
	swTemp1 = SV_extract_l(LwIn);

	/* calculate x*x*c2 */
	/* ---------------- */
	swTemp2 = SV_mult_r(swTemp1, swTemp1);
	LwIn = SV_L_mult(swTemp2, pswPCoefE[0]);

	/* calculate x*x*c2 + x*c1 */
	/* ----------------------- */
	LwIn = SV_L_mac(LwIn, swTemp1, pswPCoefE[1]);

	/* calculate x*x*c2 + x*c1 + c0 */
	/* --------------------------- */
	LwIn = SV_L_add(LwIn, SV_L_deposit_h(pswPCoefE[2]));

	/* un-normalize exponent if its requires it */
	/* ---------------------------------------- */
	if (swTemp4 > 0)
	{
		LwIn = SV_L_shr(LwIn, swTemp4);
	}

	/* return result */
	/* ------------- */
	return (LwIn);
}

extern int SV_fnExp10(int L_Input/*Q26*/)
{
	static short InvScale = 27213;		/* (1/log10(2))/4 */
	int LwIn;

	LwIn = SV_L_mpy_ls(L_Input, InvScale);
	LwIn = SV_L_shl(LwIn, 2);
	LwIn = SV_fnExp2(LwIn);

	return (LwIn); //Q31
}

extern int SV_fnExp(int L_Input/*Q26*/)
{
	static int InvScale = 932640298;	 /* 1/log(10) , Q31*/
	int LwIn;

	LwIn = SV_L_mpy_ll(L_Input, InvScale);//Q26
	LwIn = SV_fnExp10(LwIn);

	return (LwIn); //Q31
}



/*___________________________________________________________________________
|                                                                           |
|   Function Name : SV_LL_add                                           |
|                                                                           |
|   Purpose :                                                               |
|                                                                           |
|   32 bits addition of the two 32 bits variables (L_var1+L_var2) with      |
|   overflow control and saturation;                                        |
|                                                                           |
|___________________________________________________________________________|
*/
long long SV_LL_add(long long L_var1, long long L_var2)
{
	long long L_var_out;

	L_var_out = L_var1 + L_var2;
	if (((L_var1 ^ L_var2) & SV_MIN_64) == 0)
	{
		if ((L_var_out ^ L_var1) & SV_MIN_64)
		{
			L_var_out = (L_var1 < 0) ? SV_MIN_64 : SV_MAX_64;
			SV_Overflow = 1;
		}
	}

	return (L_var_out);
}

/*___________________________________________________________________________
|                                                                           |
|   Function Name : SV_LL_sub                                           |
|                                                                           |
|   Purpose :                                                               |
|                                                                           |
|   64 bits subtraction of the two 64 bits variables (L_var1-L_var2) with   |
|   overflow control and saturation                                         |
|___________________________________________________________________________|
*/
long long SV_LL_sub(long long L_var1, long long L_var2)
{
	long long L_var_out;

	L_var_out = L_var1 - L_var2;

	if (((L_var1 ^ L_var2) & SV_MIN_64) != 0)
	{
		if ((L_var_out ^ L_var1) & SV_MIN_64)
		{
			L_var_out = (L_var1 < 0L) ? SV_MIN_64 : SV_MAX_64;
			SV_Overflow = 1;
		}
	}
	return (L_var_out);
}

int SV_L_abs(int L_var1)
{
	int L_var_out;

	if (L_var1 == SV_MIN_32)
	{
		L_var_out = SV_MAX_32;
	}
	else
	{
		if (L_var1 < 0)
		{
			L_var_out = -L_var1;
		}
		else
		{
			L_var_out = L_var1;
		}
	}

	return (L_var_out);
}

int SV_L_negate(int L_var1)
{
	int L_var_out;

	L_var_out = (L_var1 == SV_MIN_32) ? SV_MAX_32 : -L_var1;
	return (L_var_out);
}

int SV_L_saturate(long long L_var)
{
	int ret = 0;
	if (L_var > SV_MAX_32)
	{
		ret = SV_MAX_32;
	}
	else if (L_var < SV_MIN_32)
	{
		ret = SV_MIN_32;
	}
	else
	{
		ret = (int)L_var;
	}
	return ret;
}




short SV_mult(short var1, short var2)
{
	short var_out;
	int L_product;

	L_product = (int)var1 *(int)var2;

	L_product = (L_product & (int)0xffff8000L) >> 15;

	if (L_product & (int)0x00010000L)
		L_product = L_product | (int)0xffff0000L;

	var_out = SV_saturate(L_product);
	return (var_out);
}


short SV_norm_l(int L_var1)
{
#ifdef SV_BASIC_OPT_NORM_L
	if (L_var1 == 0) return 0;
	if (L_var1 < 0)
	{
		return CountLeadZeros(~L_var1) - 1;
	}
	else
	{
		return CountLeadZeros(L_var1) - 1;
	}
#else
	short var_out;

	if (L_var1 == 0)
	{
		var_out = 0;
	}
	else
	{
		if (L_var1 == (int)0xffffffffL)
		{
			var_out = 31;
		}
		else
		{
			if (L_var1 < 0)
			{
				L_var1 = ~L_var1;
			}
			for (var_out = 0; L_var1 < (int)0x40000000L; var_out++)
			{
				L_var1 <<= 1;
			}
		}
	}

	return (var_out);
#endif
}


short SV_norm_s(short var1)
{
	short var_out;

	if (var1 == 0)
	{
		var_out = 0;
	}
	else
	{
		if (var1 == (short)0xffff)
		{
			var_out = 15;
		}
		else
		{
			if (var1 < 0)
			{
				var1 = ~var1;
			}
			for (var_out = 0; var1 < 0x4000; var_out++)
			{
				var1 <<= 1;
			}
		}
	}

	return (var_out);
}


short SV_negate(short var1)
{
	short var_out;
	var_out = (var1 == SV_MIN_16) ? SV_MAX_16 : -var1;
	return (var_out);
}

int divide(int var1_denorm, int q1, int var2_denorm, int q2, int dest_qForm)
{
	int signbit = 0;
	int var1 = var1_denorm;
	int var2 = var2_denorm;
	int ret = 0;
	short n1;
	short n2;

	if (var1_denorm < 0) {
		var1 = SV_L_negate(var1_denorm);
		signbit = !signbit;
	}

	if (var2_denorm < 0) {
		var2 = SV_L_negate(var2_denorm);
		signbit = !signbit;
	}

	if (var1_denorm == 0) {
		return 0;
	}

	if (var2_denorm == 0) {
		//divide by 0, undefined, just return var1_denorm
		return var1_denorm;
	}

	n1 = SV_norm_l(var1);
	n2 = SV_norm_l(var2);
	//adjust var1 to 0x20000000 to 0x3fffffff, adjust var2 to 0x40000000 to 0x7fffffff
	if (n1 == 0) {
		var1 = var1 >> 1;
	}
	else {
		var1 = var1 << (n1 - 1);
	}
	var2 = var2 << n2;

	ret = SV_L_divide(var1, var2);   //6   
										 //ret = Fixed2Fixed_f(div_ret, 31 + q1 + n1 - 1 - q2 - n2, dest_qForm);


	ret = SV_L_shl(ret, (short)(dest_qForm - (31 + q1 + n1 - 1 - q2 - n2)));

	if (signbit) {
		ret = SV_L_negate(ret);
	}

	return ret;
}



short int_sqrt_0_50(int num)		//in: Q31, out:Q15
{
	int i;
	short guess, root;

	root = 0;
	for (i = 14; i >= 0; i--)
	{
		guess = root | (1 << i);
		if (num >= SV_L_mult(guess, guess)) root = guess;
	}
	return root;
}

short int_sqrt_0_25(int num)		//in: Q31, out:Q15
{
	int i;
	short guess, tmp, root;

	root = 0;
	for (i = 14; i >= 0; i--)
	{
		guess = root | (1 << i);
		tmp = SV_mult(guess, guess);
		if (num >= SV_L_mult(tmp, tmp)) root = guess;
	}
	return root;
}

short int_sqrt_0_75(int num)		//in: Q31, out:Q15
{
	short root;

	root = int_sqrt_0_50(num);
	num = SV_L_mpy_ls(num, root);
	root = int_sqrt_0_50(num);

	return root;
}




int SV_fnLog2(int L_Input)
{
	static short swC0 = -0x2b2a, swC1 = 0x7fc5, swC2 = -0x54d0;
	short siShiftCnt, swInSqrd, swIn;
	int LwIn;

	/* normalize input and store shifts required */
	/* ----------------------------------------- */
	siShiftCnt = SV_norm_l(L_Input);
	LwIn = SV_L_shl(L_Input, siShiftCnt);
	siShiftCnt = SV_add(siShiftCnt, 1);
	siShiftCnt = SV_negate(siShiftCnt);

	/* calculate x*x*c0 */
	/* ---------------- */
	swIn = SV_extract_h(LwIn);
	swInSqrd = SV_mult_r(swIn, swIn);
	LwIn = SV_L_mult(swInSqrd, swC0);

	/* SV_add x*c1 */
	/* --------- */
	LwIn = SV_L_mac(LwIn, swIn, swC1);

	/* SV_add c2 */
	/* ------ */
	LwIn = SV_L_add(LwIn, SV_L_deposit_h(swC2));

	/* apply *(4/32) */
	/* ------------- */
	LwIn = SV_L_shr(LwIn, 3);
	LwIn = LwIn & 0x03ffffff;
	siShiftCnt = SV_shl(siShiftCnt, 10);
	LwIn = SV_L_add(LwIn, SV_L_deposit_h(siShiftCnt));

	/* return log2 */
	/* ----------- */
	return (LwIn);
}

#ifdef USEOPT
extern int SV_fnLog10_asm(int L_Input);
int SV_fnLog10(int L_Input)
{
	return SV_fnLog10_asm(L_Input);
}
#else
int SV_fnLog10(int L_Input)
{
	static short Scale = 9864;			/* 0.30103 = log10(2) */ //Q15
	int LwIn;

	if (L_Input == 0) return (-2147483647);

	/* 0.30103*log2(x) */
	/* ------------------- */
	LwIn = SV_fnLog2(L_Input);
	LwIn = SV_L_mpy_ls(LwIn, Scale);

	return (LwIn);
}
#endif

/**
Calculate 10log10(pwr)
@param pwr Q(pwr_q)
@param pwr_q Q for pwr variable (~31)
@return 10log10(pwr), Q23
*/
int SV_10log10_l(int pwr, short pwr_q)
{
	int pwrdB;

	/* fnLog10(pwr) - fnLog10(1) - pwr_q*(fnLog10(2) - fnLog10(1)) */
	// pwrdB: Q26(31-5)

	/*
	pwr = L_sub(L_sub(fnLog10(L_add(pwr, L_shl(1, pwr_q))), LOG10FIX),
		L_shl(SV_L_mpy_ls(L_sub(LOG10QFIX, LOG10FIX), shl(pwr_q, 10)), 5));	// 8*log10(x): Q23

	pwrdB = L_add(pwr, L_shr(pwr, 2));	// 10*log10(x): Q23
	*/

	int Ltmp1, Ltmp2, Ltmp3;

	Ltmp1 = SV_L_sub(SV_fnLog10(pwr), LOG10FIX);	// 8*log10(pwr), Q23
	Ltmp2 = SV_L_sub(LOG10QFIX, LOG10FIX);		// 8*log10(2), Q23
	Ltmp3 = SV_L_shl(SV_L_mpy_ls(Ltmp2, SV_shl(pwr_q, 10)), 5);	// 8 * pwr_q * log10(2), Q23
	Ltmp2 = SV_L_sub(Ltmp1, Ltmp3);			// 8*log10(x) = 8*(log10(pwr) - pwr_q*log10(2)), Q23

	pwrdB = SV_L_add(Ltmp2, SV_L_shr_r(Ltmp2, 2));	// 10*log10(x): Q23

	return pwrdB;
}


/**
Calculate 10log10(pwr)
@param pwr Q(pwr_q)
@param pwr_q Q for pwr variable
@return 10log10(pwr), Q8
*/
short Fx_10log10(int pwr, short pwr_q)
{
	short pwrdB;

	/* SV_fnLog10(pwr) - SV_fnLog10(1) - pwr_q*(SV_fnLog10(2) - SV_fnLog10(1)) */
	// pwr: Q26(31-5)


#ifdef USEOPT
	extern short Fx_10log10_subfunc(int pwr);
	pwrdB = Fx_10log10_subfunc(pwr);
#else
	pwr = SV_L_sub(SV_L_sub(SV_fnLog10(SV_L_add(pwr, SV_shl(1, pwr_q))), LOG10FIX),
		SV_L_shl(SV_L_mpy_ls(SV_L_sub(LOG10QFIX, LOG10FIX), SV_shl(pwr_q, 11)), 4));
	pwr = SV_L_shr(pwr, 4);                      // Q26 -> Q22
	pwr = SV_L_shl(SV_L_mpy_ls(pwr, 0x2800), 5);    // 10 << 10 and then again SV_shl to obtain Q15
	pwrdB = SV_extract_l(SV_L_shr(pwr, 14));         // Q22 -> Q8
#endif

	return pwrdB;
}