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

#if !WASM
namespace user_rtx { using u = user_rtx;
#else
namespace user_rtx.wasm { using u = user_rtx.wasm;
#endif

static partial class math {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

/// <exception cref="ArgumentException" />
public static double Min( params double[] values )
{
   u.array_argcheck.NotEmpty( values, nameof(values) );
   double? min = null;
   foreach ( var val in values )
      if ( min == null || val < min )
         min = val;
   return min.V();
}

/// <exception cref="ArgumentException" />
public static double Max( params double[] values )
{
   u.array_argcheck.NotEmpty( values, nameof(values) );
   double? max = null;
   foreach ( var val in values )
      if ( max == null || val > max )
         max = val;
   return max.HasValue ? max.Value : double.NaN;
}

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

/// <exception cref="ArgumentException" />
public static decimal Min( params decimal[] values )
{
   u.array_argcheck.NotEmpty( values, nameof(values) );
   decimal? min = null;
   foreach ( var val in values )
      if ( min == null || val < min )
         min = val;
   return min.V();
}

/// <exception cref="ArgumentException" />
public static decimal Max( params decimal[] values )
{
   u.array_argcheck.NotEmpty( values, nameof(values) );
   decimal? max = null;
   foreach ( var val in values )
      if ( max == null || val > max )
         max = val;
   return max.V();
}

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

public static double Sum( params double[] values )
{
   double sum = 0;
   foreach ( var val in values )
      sum += val;
   return sum;
}

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

public static decimal Sum( params decimal[] values )
{
   decimal sum = 0;
   foreach ( var val in values )
      sum += val;
   return sum;
}

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

/// <exception cref="ArgumentException" />
public static double Avg( params double[] values )
{
   u.array_argcheck.NotEmpty( values, nameof(values) );
   return Sum( values ) / values.Length;
}

/// <exception cref="ArgumentException" />
public static double Avg( double[] values, double[] weights )
{
   u.array_argcheck.NotEmpty( values, nameof(values) );
   u.array_argcheck.NotEmpty( weights, nameof(weights) );
   if ( values.Length != weights.Length )
      throw new ArgumentException(
         "Number of values and number of weights must be equal for calculation of average value." );
   double weightSum = 0;
   bool hasPositiveWeights = false;
   foreach ( var weight in weights ) {
      if ( double.IsNaN( weight ) )
         throw e_argument.NewWith( nameof(weights),
            "Weight values must not be NAN." );
      if ( weight < 0 )
         throw e_argument.NewWith( nameof(weights),
            "Weight value must be greater than or equal to zero." );
      weightSum += weight;
      if ( weight > 0 )
         hasPositiveWeights = true;
   }
   if ( !hasPositiveWeights )
      throw e_argument.NewWith( nameof(weights),
         "All weight values are equal to zero." );
   double avg = 0;
   for ( int i=0; i<values.Length; i++ )
      avg += values[ i ] * weights[ i ] / weightSum;
   return avg;
}

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

/// <exception cref="ArgumentException" />
public static decimal Avg( params decimal[] values )
{
   u.array_argcheck.NotEmpty( values, nameof(values) );
   return Sum( values ) / values.Length;
}

/// <exception cref="ArgumentException" />
public static decimal Avg( decimal[] values, decimal[] weights )
{
   u.array_argcheck.NotEmpty( values, nameof(values) );
   u.array_argcheck.NotEmpty( weights, nameof(weights) );
   if ( values.Length != weights.Length )
      throw new ArgumentException(
         "Number of values and number of weights must be equal for calculation of average value." );
   decimal weightSum = 0;
   bool hasPositiveWeights = false;
   foreach ( var weight in weights ) {
      if ( weight < 0 )
         throw e_argument.NewWith( nameof(weights),
            "Weight value must be greater than or equal to zero." );
      weightSum += weight;
      if ( weight > 0 )
         hasPositiveWeights = true;
   }
   if ( !hasPositiveWeights )
      throw e_argument.NewWith( nameof(weights),
         "All weight values are equal to zero." );
   decimal avg = 0;
   for ( int i=0; i<values.Length; i++ )
      avg += values[ i ] * weights[ i ] / weightSum;
   return avg;
}

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

/// <exception cref="ArgumentException" />
/// <exception cref="OverflowException" />
public static int SafeIntAvg( params int[] values )
{
   u.array_argcheck.NotEmpty( values, nameof(values) );
   decimal sum = 0;
   foreach ( int val in values )
      sum += val;
   return IntRound( sum / values.Length );
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // math

} // user_rtx (.wasm)
