//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// CommonHelpers satellite library | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using System;

#if !WASM
namespace CommonHelpers {
#else
namespace CommonHelpers.WASM {
#endif

//**************************************************************************************************
/// <summary>
/// </summary>
public static partial class HArith {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

public static bool IsOdd(int n)
{
   return n % 2 != 0;
}
public static bool IsEven(int n)
{
   return n % 2 == 0;
}

//==================================================================================================

public static int Sign(int n)
{
   if ( n > 0 ) return  1;
   if ( n < 0 ) return -1;
   return 0;
}
public static double Sign(double x)
{
   if ( x > 0 ) return  1d;
   if ( x < 0 ) return -1d;
   return 0;
}

//--------------------------------------------------------------------------------------------------

public static bool AreSameSignValues(int m,int n)
{
   return Sign( m ) * Sign( n ) >= 0;
}
public static bool AreSameSignValues(double x,double y)
{
   return Sign( x ) * Sign( y ) >= 0;
}

//==================================================================================================

public static int ConfinePositiveOrZero(int n)
{
   return Math.Max( n, 0 );
}
public static int ConfineNegativeOrZero(int n)
{
   return Math.Min( n, 0 );
}

//--------------------------------------------------------------------------------------------------

public static double ConfinePositiveOrZero(double x)
{
   return Math.Max( x, 0 );
}
public static double ConfineNegativeOrZero(double x)
{
   return Math.Min( x, 0 );
}

//==================================================================================================

[CLSCompliant(false)]
public static int ConfinePositiveOrZero(ref int n)
{
   return n = ConfinePositiveOrZero( n );
}
[CLSCompliant(false)]
public static int ConfineNegativeOrZero(ref int n)
{
   return n = ConfineNegativeOrZero( n );
}

//--------------------------------------------------------------------------------------------------

[CLSCompliant(false)]
public static double ConfinePositiveOrZero(ref double x)
{
   return x = ConfinePositiveOrZero( x );
}
[CLSCompliant(false)]
public static double ConfineNegativeOrZero(ref double x)
{
   return x = ConfineNegativeOrZero( x );
}

//==================================================================================================

/// <exception cref="ArgumentOutOfRangeException" />
public static int Confine(int n,int min,int max)
{
   if ( min > max )
      throw new ArgumentOutOfRangeException();
   return Math.Min( Math.Max( n, min ), max );
}
/// <exception cref="ArgumentOutOfRangeException" />
public static double Confine(double x,double min,double max)
{
   if ( min > max )
      throw new ArgumentOutOfRangeException();
   return Math.Min( Math.Max( x, min ), max );
}

//--------------------------------------------------------------------------------------------------

/// <exception cref="ArgumentOutOfRangeException" />
[CLSCompliant(false)]
public static int Confine(ref int n,int min,int max)
{
   return n = Confine( n, min, max );
}
/// <exception cref="ArgumentOutOfRangeException" />
[CLSCompliant(false)]
public static double Confine(ref double x,double min,double max)
{
   return x = Confine( x, min, max );
}

//==================================================================================================

/// <exception cref="ArgumentOutOfRangeException" />
public static int UpConfine(ref int n,int max)
{
   return n = Math.Min( n, max );
}
/// <exception cref="ArgumentOutOfRangeException" />
public static int DownConfine(ref int n,int min)
{
   return n = Math.Max( n, min );
}

/// <exception cref="ArgumentOutOfRangeException" />
public static double UpConfine(ref double x,double max)
{
   return x = Math.Min( x, max );
}
/// <exception cref="ArgumentOutOfRangeException" />
public static double DownConfine(ref double x,double min)
{
   return x = Math.Max( x, min );
}

//==================================================================================================

public static int RoundDiv(int m,int n)
{
   int div = m/n;
   return
      Math.Abs(m % n * 2) >= Math.Abs(n) ?
      (Math.Abs(div)+1)*Sign(m)*Sign(n) :
      div;
}

//--------------------------------------------------------------------------------------------------

public static int UpperDiv(int m,int n)
{
   return m / n + ( m % n != 0 ? Sign(m)*Sign(n) : 0 );
}

//==================================================================================================

public static int IntPower(int m,int n)
{
   if ( n < 0 )
      throw new ArgumentOutOfRangeException();
   int result = 1;
   for (int i=0; i<n; i++)
      result *= m;
   return result;
}

//==================================================================================================

/// <exception cref="ArgumentException" />
public static int Min(params int[] values)
{
   HArrayArgChecking.VerifyNotEmpty( values );
   int? min = null;
   foreach (int value in values)
      if ( min == null || value < min )
         min = value;
   return min.Value;
}

/// <exception cref="ArgumentException" />
public static int Max(params int[] values)
{
   HArrayArgChecking.VerifyNotEmpty( values );
   int? max = null;
   foreach (int value in values)
      if ( max == null || value > max )
         max = value;
   return max.Value;
}

//--------------------------------------------------------------------------------------------------

/// <exception cref="ArgumentException" />
public static double Min(params double[] values)
{
   HArrayArgChecking.VerifyNotEmpty( values );
   double? min = null;
   foreach (double value in values)
      if ( min == null || value < min )
         min = value;
   return min.Value;
}

/// <exception cref="ArgumentException" />
public static double Max(params double[] values)
{
   HArrayArgChecking.VerifyNotEmpty( values );
   double? max = null;
   foreach (double value in values)
      if ( max == null || value > max )
         max = value;
   return max.Value;
}

//--------------------------------------------------------------------------------------------------

/// <exception cref="ArgumentException" />
public static decimal Min(params decimal[] values)
{
   HArrayArgChecking.VerifyNotEmpty( values );
   decimal? min = null;
   foreach (decimal value in values)
      if ( min == null || value < min )
         min = value;
   return min.Value;
}

/// <exception cref="ArgumentException" />
public static decimal Max(params decimal[] values)
{
   HArrayArgChecking.VerifyNotEmpty( values );
   decimal? max = null;
   foreach (decimal value in values)
      if ( max == null || value > max )
         max = value;
   return max.Value;
}

//==================================================================================================

public static int Ceiling(double value)
{
   return (int)Math.Ceiling( value );
}

//==================================================================================================

public static int Round(double value)
{
   return (int)Math.Round( value );
}

public static int Round(decimal value)
{
   return (int)Math.Round( value );
}

//--------------------------------------------------------------------------------------------------

public static int RoundUp(double value)
{
   return (int)HMath.RoundUp( value );
}

public static int RoundDown(double value)
{
   return (int)HMath.RoundDown( value );
}

public static int RoundFromZero(double value)
{
   return (int)HMath.RoundFromZero( value );
}

public static int RoundToZero(double value)
{
   return (int)HMath.RoundToZero( value );
}

//--------------------------------------------------------------------------------------------------

public static int RoundUp(decimal value)
{
   return (int)HMath.RoundUp( value );
}

public static int RoundDown(decimal value)
{
   return (int)HMath.RoundDown( value );
}

public static int RoundFromZero(decimal value)
{
   return (int)HMath.RoundFromZero( value );
}

public static int RoundToZero(decimal value)
{
   return (int)HMath.RoundToZero( value );
}

//==================================================================================================

/// <exception cref="ArgumentNullException" />
/// <exception cref="OverflowException" />
public static int IntSum(params int[] values)
{
   int sum = 0;
   HOps.ForEach( values, delegate (int value) { sum += value; } );
   return sum;
}

//==================================================================================================

/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentException" />
/// <exception cref="OverflowException" />
public static int IntAvg(params int[] values)
{
   HArrayArgChecking.VerifyNotEmpty( values );
   return RoundDiv( IntSum( values ), values.Length );
}

//--------------------------------------------------------------------------------------------------

/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentException" />
public static int SafeIntAvg(params int[] values)
{
   HArrayArgChecking.VerifyNotEmpty( values );
   decimal sum = 0;
   foreach (int val in values)
      sum += val;
   return Round( sum / values.Length );
}

//==================================================================================================

/// <exception cref="ArgumentException" />
/// <exception cref="DivideByZeroException" />
/// <exception cref="OverflowException" />
public static int ValueToPercent(double value)
{
   return ValueToPercent( value, 1.0 );
}
/// <exception cref="ArgumentException" />
/// <exception cref="DivideByZeroException" />
/// <exception cref="OverflowException" />
public static int ValueToPercent(double value,double entireValue)
{
   if ( double.IsNaN( value ) || double.IsNaN( entireValue ) )
      throw new ArgumentException();
   if ( entireValue == 0 )
       throw new DivideByZeroException();
   try { return (int)Math.Round( value / entireValue * 100 ); }
   catch { throw new OverflowException(); }
}

//--------------------------------------------------------------------------------------------------

public static double PercentToValue(double percent)
{
   return PercentToValue( percent, 1.0 );
}
public static double PercentToValue(double percent,double entireValue)
{
   return percent / 100 * entireValue;
}

public static int PercentToRoundValue(double percent)
{
   return PercentToRoundValue( percent, 1.0 );
}
public static int PercentToRoundValue(double percent,double entireValue)
{
   return Round( PercentToValue( percent, entireValue ) );
}

//==================================================================================================

/// <exception cref="ArgumentException" />
/// <exception cref="DivideByZeroException" />
/// <exception cref="OverflowException" />
public static int ValueToPercent(decimal value)
{
   return ValueToPercent( (double)value );
}
/// <exception cref="ArgumentException" />
/// <exception cref="DivideByZeroException" />
/// <exception cref="OverflowException" />
public static int ValueToPercent(decimal value,decimal entireValue)
{
   return ValueToPercent( (double)value, (double)entireValue );
}

public static decimal PercentToValue(decimal percent)
{
   return (decimal)PercentToValue( (double)percent );
}
public static decimal PercentToValue(decimal percent,decimal entireValue)
{
   return (decimal)PercentToValue( (double)percent, (double)entireValue );
}

public static int PercentToRoundValue(decimal percent)
{
   return PercentToRoundValue( (double)percent );
}
public static int PercentToRoundValue(decimal percent,decimal entireValue)
{
   return PercentToRoundValue( (double)percent, (double)entireValue );
}

//==================================================================================================

static HArith()
{
   CommonHelpers_Core.Access();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // HArith
//**************************************************************************************************

} // CommonHelpers (.WASM)
