//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// 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 HMath {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

public static double NotNumberAsZero(double value)
{
   return !double.IsNaN( value ) ? value : 0;
}

public static float NotNumberAsZero(float value)
{
   return !float.IsNaN( value ) ? value : 0;
}

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

public static double SetNotNumberToZero(ref double value)
{
   return !double.IsNaN( value ) ? value : (value = 0);
}

public static float SetNotNumberToZero(ref float value)
{
   return !float.IsNaN( value ) ? value : (value = 0);
}

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

public static double NotFiniteNumberAsZero(double value)
{
   return HDouble.IsFiniteNumber( value ) ? value : 0;
}

public static float NotFiniteNumberAsZero(float value)
{
   return HSingle.IsFiniteNumber( value ) ? value : 0;
}

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

public static double SetNotFiniteNumberToZero(ref double value)
{
   return HDouble.IsFiniteNumber( value ) ? value : (value = 0);
}

public static float SetNotFiniteNumberToZero(ref float value)
{
   return HSingle.IsFiniteNumber( value ) ? value : (value = 0);
}

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

public static double FractionalPart(double value)
{
   return value - Math.Truncate( value );
}

public static decimal FractionalPart(decimal value)
{
   return value - Math.Truncate( value );
}

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

public static bool HasFractionalPart(double value)
{
   return FractionalPart( value ) != 0;
}

public static bool HasFractionalPart(decimal value)
{
   return FractionalPart( value ) != 0;
}

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

public static double DoubleDiv(double x,double y)
{
   return x / y;
}

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

public static decimal DecimalDiv(decimal x,decimal y)
{
   return x / y;
}

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

public static double RoundUp(double value)
{
   if ( double.IsNaN( value ) )
      return double.NaN;
   var trunc = Math.Truncate( value );
   return HasFractionalPart( value ) ? trunc+1 : trunc;
}

public static double RoundDown(double value)
{
   if ( double.IsNaN( value ) )
      return double.NaN;
   var trunc = Math.Truncate( value );
   return HasFractionalPart( value ) ? trunc-1 : trunc;
}

public static double RoundFromZero(double value)
{
   if ( double.IsNaN( value ) )
      return double.NaN;
   var trunc = Math.Truncate( value );
   return HasFractionalPart( value ) ?
      value >= 0 ? trunc+1 : trunc-1 :
      trunc;
}

public static double RoundToZero(double value)
{
   if ( double.IsNaN( value ) )
      return double.NaN;
   var trunc = Math.Truncate( value );
   return HasFractionalPart( value ) ?
      value == 0 ? 0 : value > 0 ? trunc-1 : trunc+1 :
      trunc;
}

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

public static decimal RoundUp(decimal value)
{
   var trunc = Math.Truncate( value );
   return HasFractionalPart( value ) ? trunc+1 : trunc;
}

public static decimal RoundDown(decimal value)
{
   var trunc = Math.Truncate( value );
   return HasFractionalPart( value ) ? trunc-1 : trunc;
}

public static decimal RoundFromZero(decimal value)
{
   var trunc = Math.Truncate( value );
   return HasFractionalPart( value ) ?
      value >= 0 ? trunc+1 : trunc-1 :
      trunc;
}

public static decimal RoundToZero(decimal value)
{
   var trunc = Math.Truncate( value );
   return HasFractionalPart( value ) ?
      value == 0 ? 0 : value > 0 ? trunc-1 : trunc+1 :
      trunc;
}

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

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

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

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

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

/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentException" />
public static double Avg(params double[] values)
{
   HArrayArgChecking.VerifyNotEmpty( values );
   return Sum( values ) / values.Length;
}

/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentException" />
public static double Avg(double[] values,double[] weights)
{
   HArrayArgChecking.VerifyNotEmpty( values );
   HArrayArgChecking.VerifyNotEmpty( weights );
   if ( values.Length != weights.Length )
      throw new ArgumentException();
   double weightSum = 0;
   foreach (double weight in weights)
   {
      if ( weight < 0 )
         throw new ArgumentException();
      weightSum += weight;
   }
   if ( weightSum == 0 )
      throw new ArgumentException();
   double avg = 0;
   int weightIdx = values.GetLowerBound(0);
   foreach (double value in values)
      avg += value * weights[ weightIdx++ ] / weightSum;
   return avg;
}

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

/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentException" />
public static decimal Avg(params decimal[] values)
{
   HArrayArgChecking.VerifyNotEmpty( values );
   return Sum( values ) / values.Length;
}

/// <exception cref="ArgumentNullException" />
/// <exception cref="ArgumentException" />
public static decimal Avg(decimal[] values,decimal[] weights)
{
   HArrayArgChecking.VerifyNotEmpty( values );
   HArrayArgChecking.VerifyNotEmpty( weights );
   if ( values.Length != weights.Length )
      throw new ArgumentException();
   decimal weightSum = 0;
   foreach (decimal weight in weights)
   {
      if ( weight < 0 )
         throw new ArgumentException();
      weightSum += weight;
   }
   if ( weightSum == 0 )
      throw new ArgumentException();
   decimal avg = 0;
   int weightIdx = values.GetLowerBound(0);
   foreach (decimal value in values)
      avg += value * weights[ weightIdx++ ] / weightSum;
   return avg;
}

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // HMath
//**************************************************************************************************

} // CommonHelpers (.WASM)
