
package jp.riken.brain.ni.samuraigraph.base;

import java.math.BigDecimal;


/**
 * A class with utility methods for the calculation of numbers.
 *
 */
public class SGUtilityNumber implements SGIConstants
{

	public static void main( String[] args )
	{

	}


	/**
	 * Returns the order of number. For example: 35.5 -> 1, -121.1 -> 2, 0.025 -> -2.
	 * @param d - number to get order
	 * @return the order of the number
	 * @throws IllegalArgumentException input value is equal to 0.0
	 */
	public static int getOrder( final double d )
	{
		if( d == 0.0 )
		{
			throw new IllegalArgumentException("d == 0.0");
		}

		final double absd = Math.abs(d);

		BigDecimal bd = new BigDecimal(absd);
		int cnt = 0;
		int order = 0;
		if( absd > 1.0 )
		{
			while( true )
			{
				if( bd.doubleValue() < 1.0 )
				{
					break;
				}
				bd = bd.movePointLeft(1);
				cnt++;
			}
			order = cnt -1;
		}
		else
		{
			while( true )
			{
				if( bd.doubleValue() >= 1.0 )
				{
					break;
				}
				bd = bd.movePointRight(1);
				cnt++;
			}
			order = - cnt;
		}

		return order;
	}



	/**
	 * Returns ten to the power of given ordinal number.
	 * @param order - ordinal number
	 * @return ten to the power of given ordinal
	 */
	public static double getPowersOfTen( final int order )
	{
		BigDecimal bd = getBigDecimalPowersOfTen(order);
		return bd.doubleValue();
	}


	/**
	 * Returns ten to the power of given ordinal number in BigDecimal form.
	 * @param order - ordinal number
	 * @return ten to the power of given ordinal
	 */
	public static BigDecimal getBigDecimalPowersOfTen( final int order )
	{
		BigDecimal bd = new BigDecimal("1.0");
		bd = bd.movePointRight( order );
		return bd;
	}


	/**
	 * Truncate the number at given digit.
	 * For example: value=8715.61, digit=2 -> 8700.0
	 * @param value - number to be truncated
	 * @param digit - digit
	 * @return truncated number
	 */
	public static double truncateNumber( final double value, final int digit )
	{
		return truncateNumber( Double.toString(value), digit );
	}


	/**
	 * Truncate the number at given digit.
	 * @param value - a text string of the number to be truncated
	 * @param digit - digit
	 * @return truncated number
	 */
	public static double truncateNumber( final String value, final int digit )
	{
		BigDecimal bd = new BigDecimal(value);
		bd = bd.movePointLeft( digit );
		final int num = (int)bd.doubleValue();
		bd = new BigDecimal( num );
		bd = bd.movePointRight( digit );
		return bd.doubleValue();
	}


	/**
	 * Round off the number at given digit.
	 * For example: value=8715.61, digit=1 -> 8700.0 and value=8765.61, digit=1 -> 8800.0
	 * @param value - number to be rounded off
	 * @param digit - digit
	 * @return the result of rounded off
	 */
	public static double roundOffNumber( final double value, final int digit )
	{
		return roundOffNumber( Double.toString(value), digit );
	}


	/**
	 * Round off the number at given digit.
	 * For example: value=8715.61, digit=1 -> 8700.0 and value=8765.61, digit=1 -> 8800.0
	 * @param value - a text string of the number to be rounded off
	 * @param digit - digit
	 * @return the result of rounded off
	 */
	public static double roundOffNumber( final String value, final int digit )
	{
		BigDecimal bd = new BigDecimal(value);
		bd = bd.movePointLeft(digit+1);
		final double num = Math.rint( bd.doubleValue() );
		bd = new BigDecimal(num);
		bd = bd.movePointRight(digit+1);
		return bd.doubleValue();
	}


	/**
	 * Round out the number at given digit.
	 * For example: value=8715.61, digit=1 -> 8800.0
	 * @param value - number to be rounded out
	 * @param digit - digit
	 * @return the result of rounded off
	 */
	public static double roundOutNumber( final double value, final int digit )
	{
		return roundOutNumber( Double.toString(value), digit );
	}


	/**
	 * Round out the number at given digit.
	 * For example: value=8715.61, digit=1 -> 8800.0
	 * @param value - a text string of the number to be rounded off
	 * @param digit - digit
	 * @return the result of rounded off
	 */
	public static double roundOutNumber( final String value, final int digit )
	{
		BigDecimal bd = new BigDecimal(value);
		bd = bd.movePointLeft(digit+1);
		final double num = Math.ceil( bd.doubleValue() );
		bd = new BigDecimal(num);
		bd = bd.movePointRight(digit+1);
		return bd.doubleValue();
	}



	/**
	 * 
	 * @param x1
	 * @param y1
	 * @param x2
	 * @param y2
	 * @return
	 */
	public static boolean isOverlapping(
		final double x1, final double y1, final double x2, final double y2 )
	{
		final double value = getOverlap(x1,y1,x2,y2);
		return !(value==0.0);
	}



	/**
	 * 
	 * @param x1
	 * @param y1
	 * @param x2
	 * @param y2
	 * @return
	 */
	public static double getOverlap(
		final double x1, final double y1, final double x2, final double y2 )
	{
		final double min1 = x1<y1 ? x1 : y1 ;
		final double max1 = x1<y1 ? y1 : x1 ;
		final double min2 = x2<y2 ? x2 : y2 ;
		final double max2 = x2<y2 ? y2 : x2 ;

		if( !( min1<max2 | min2<max1 ) )
		{
			return 0.0;
		}

		double value;
		if( min1<min2 )
		{
			value = max1 - min2;
		}
		else
		{
			value = max2 - min1;
		}

		return value;
	}



	/**
	 * 
	 * @param min
	 * @param max
	 * @param value
	 * @return
	 */
	public static boolean contains(
		final double x1, final double x2, final double value )
	{
		double min;
		double max;
		if( x1<=x2 )
		{
			min = x1;
			max = x2;
		}
		else
		{
			min = x2;
			max = x1;
		}
		return ( min<=value & value<=max );
	}


	/**
	 * 
	 * @param x1
	 * @param y1
	 * @param x2
	 * @param y2
	 * @return
	 */
	public static boolean contains(
		final double x1, final double y1, final double x2, final double y2 )
	{
		final double min1 = x1<y1 ? x1 : y1 ;
		final double max1 = x1<y1 ? y1 : x1 ;
		final double min2 = x2<y2 ? x2 : y2 ;
		final double max2 = x2<y2 ? y2 : x2 ;
		return ( min1<=min2 & max2<=max1 );
	}



	/**
	 * 
	 * @param flag
	 * @param value
	 * @param min
	 * @param max
	 * @param step
	 * @param err
	 * @return
	 */
	public static double stepValue(
		final boolean flag, final double value,
		final double min, final double max,
		final double step, final double err )
	{

		final int indexMax = (int)Math.rint(max/step);

		final int indexNearest = (int)Math.rint(value/step);
		final double valueNearest = indexNearest*step;

		int indexNew;
		if( Math.abs( value - valueNearest ) < err )
		{
			if( flag )
			{
				indexNew = indexNearest + 1;
			}
			else
			{
				indexNew = indexNearest - 1;
			}
		}
		else
		{
			if( flag )
			{
				indexNew = (int)Math.floor(value/step) + 1;
			}
			else
			{
				indexNew = (int)Math.floor(value/step);
			}
		}

		final double valueNew = indexNew*step;

		return valueNew;
	}



	/**
	 * 
	 */
	public static double getNumberInNumberOrder(
		final double value,
		final double num,
		final int digit )
	{
		final int order = SGUtilityNumber.getOrder( num )
			- digit + 1;
		BigDecimal bd = new BigDecimal( Double.toString(value) );
		bd = bd.movePointLeft(order);
		bd = bd.setScale(0,BigDecimal.ROUND_HALF_UP);
		bd = bd.movePointRight(order);
		return bd.doubleValue();
	}


	/**
	 * 
	 */
	public static double getNumberInRangeOrder(
		final double value,
		final double min,
		final double max,
		final int digit )
	{
		return getNumberInNumberOrder( value, max - min, digit );
	}



	/**
	 * Return the minimum value in an array of double.
	 * @param array - an array to be searched
	 * @return the minimum value
	 */
	public static double min( final double[] array )
	{
		double min = Double.MAX_VALUE;
		for( int ii=0; ii<array.length; ii++ )
		{
			if( array[ii] < min )
			{
				min = array[ii];
			}
		}
		return min;
	}


	/**
	 * Return the maximum value in an array of double.
	 * @param array - an array to be searched
	 * @return the maximum value
	 */
	public static double max( final double[] array )
	{
		double max = - Double.MAX_VALUE;
		for( int ii=0; ii<array.length; ii++ )
		{
			if( array[ii] > max )
			{
				max = array[ii];
			}
		}
		return max;
	}


}

