Report a bug
If you spot a problem with this page, click here to create a GitHub issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using a local clone.

mir.stat.descriptive.univariate

This module contains algorithms for univariate descriptive statistics.
Note that used specialized summing algorithms execute more primitive operations than vanilla summation. Therefore, if in certain cases maximum speed is required at expense of precision, one can use Summation.fast .
License:
Apache-2.0
Several functions are borrowed from mir.math.stat. An additional VarianceAlgo is provided in this code, which is the new default.
Authors:
John Michael Hall, Ilya Yaroshenko
public import mir.math.sum : Summation;
template statType(T, bool checkComplex = true)
template meanType(T)
struct MeanAccumulator(T, Summation summation);
Output range for mean.
Examples:
import mir.ndslice.slice: sliced;

MeanAccumulator!(double, Summation.pairwise) x;
x.put([0.0, 1, 2, 3, 4].sliced);
assert(x.mean == 2);
x.put(5);
assert(x.mean == 2.5);
size_t count;
Summator!(T, summation) summator;
const pure nothrow @nogc @property @safe F mean(F = T)();
const pure nothrow @nogc @property @safe F sum(F = T)();
void put(Range)(Range r)
if (isIterable!Range);
void put()(T x);
void put(F = T)(MeanAccumulator!(F, summation) m);
template mean(F, Summation summation = Summation.appropriate)

template mean(Summation summation = Summation.appropriate)

template mean(F, string summation)

template mean(string summation)
Computes the mean of the input.
By default, if F is not floating point type or complex type, then the result will have a double type if F is implicitly convertible to a floating point type or a type for which isComplex!F is true.
Parameters:
F controls type of output
summation algorithm for calculating sums (default: Summation.appropriate)
Returns:
The mean of all the elements in the input, must be floating point or complex type
See Also:
Examples:
import mir.ndslice.slice: sliced;
import mir.complex;
alias C = Complex!double;

assert(mean([1.0, 2, 3]) == 2);
assert(mean([C(1, 3), C(2), C(3)]) == C(2, 1));

assert(mean!float([0, 1, 2, 3, 4, 5].sliced(3, 2)) == 2.5);

static assert(is(typeof(mean!float([1, 2, 3])) == float));
Examples:
Mean of vector
import mir.ndslice.slice: sliced;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
assert(x.mean == 29.25 / 12);
Examples:
Mean of matrix
import mir.ndslice.fuse: fuse;

auto x = [
    [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
].fuse;

assert(x.mean == 29.25 / 12);
Examples:
Column mean of matrix
import mir.ndslice.fuse: fuse;
import mir.ndslice.topology: alongDim, byDim, map;
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;

auto x = [
    [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
].fuse;
auto result = [1, 4.25, 3.25, 1.5, 2.5, 2.125];

// Use byDim or alongDim with map to compute mean of row/column.
assert(x.byDim!1.map!mean.all!approxEqual(result));
assert(x.alongDim!0.map!mean.all!approxEqual(result));

// FIXME
// Without using map, computes the mean of the whole slice
// assert(x.byDim!1.mean == x.sliced.mean);
// assert(x.alongDim!0.mean == x.sliced.mean);
Examples:
Can also set algorithm or output type
import mir.ndslice.slice: sliced;
import mir.ndslice.topology: repeat;

//Set sum algorithm or output type

auto a = [1, 1e100, 1, -1e100].sliced;

auto x = a * 10_000;

assert(x.mean!"kbn" == 20_000 / 4);
assert(x.mean!"kb2" == 20_000 / 4);
assert(x.mean!"precise" == 20_000 / 4);
assert(x.mean!(double, "precise") == 20_000.0 / 4);

auto y = uint.max.repeat(3);
assert(y.mean!ulong == 12884901885 / 3);
Examples:
For integral slices, pass output type as template parameter to ensure output type is correct.
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [0, 1, 1, 2, 4, 4,
          2, 7, 5, 1, 2, 0].sliced;

auto y = x.mean;
assert(y.approxEqual(29.0 / 12, 1.0e-10));
static assert(is(typeof(y) == double));

assert(x.mean!float.approxEqual(29f / 12, 1.0e-10));
Examples:
Mean works for complex numbers and other user-defined types (provided they can be converted to a floating point or complex type)
import mir.complex.math: approxEqual;
import mir.ndslice.slice: sliced;
import mir.complex;
alias C = Complex!double;

auto x = [C(1.0, 2), C(2, 3), C(3, 4), C(4, 5)].sliced;
assert(x.mean.approxEqual(C(2.5, 3.5)));
Examples:
Compute mean tensors along specified dimention of tensors
import mir.ndslice: alongDim, iota, as, map;
/++
  [[0,1,2],
   [3,4,5]]
 +/
auto x = iota(2, 3).as!double;
assert(x.mean == (5.0 / 2.0));

auto m0 = [(0.0+3.0)/2.0, (1.0+4.0)/2.0, (2.0+5.0)/2.0];
assert(x.alongDim!0.map!mean == m0);
assert(x.alongDim!(-2).map!mean == m0);

auto m1 = [(0.0+1.0+2.0)/3.0, (3.0+4.0+5.0)/3.0];
assert(x.alongDim!1.map!mean == m1);
assert(x.alongDim!(-1).map!mean == m1);

assert(iota(2, 3, 4, 5).as!double.alongDim!0.map!mean == iota([3, 4, 5], 3 * 4 * 5 / 2));
Examples:
Arbitrary mean
assert(mean(1.0, 2, 3) == 2);
assert(mean!float(1, 2, 3) == 2);
meanType!F mean(Range)(Range r)
if (isIterable!Range);
Parameters:
Range r range, must be finite iterable
meanType!F mean(scope const F[] ar...);
Parameters:
F[] ar values
template hmeanType(T)
template hmean(F, Summation summation = Summation.appropriate)

template hmean(Summation summation = Summation.appropriate)

template hmean(F, string summation)

template hmean(string summation)
Computes the harmonic mean of the input.
By default, if F is not floating point type or complex type, then the result will have a double type if F is implicitly convertible to a floating point type or a type for which isComplex!F is true.
Parameters:
F controls type of output
summation algorithm for calculating sums (default: Summation.appropriate)
Returns:
harmonic mean of all the elements of the input, must be floating point or complex type
See Also:
Examples:
Harmonic mean of vector
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [20.0, 100.0, 2000.0, 10.0, 5.0, 2.0].sliced;

assert(x.hmean.approxEqual(6.97269));
Examples:
Harmonic mean of matrix
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;

auto x = [
    [20.0, 100.0, 2000.0], 
    [10.0, 5.0, 2.0]
].fuse;

assert(x.hmean.approxEqual(6.97269));
Examples:
Column harmonic mean of matrix
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice: fuse;
import mir.ndslice.topology: alongDim, byDim, map;

auto x = [
    [20.0, 100.0, 2000.0],
    [ 10.0, 5.0, 2.0]
].fuse;

auto y = [13.33333, 9.52381, 3.996004];

// Use byDim or alongDim with map to compute mean of row/column.
assert(x.byDim!1.map!hmean.all!approxEqual(y));
assert(x.alongDim!0.map!hmean.all!approxEqual(y));
Examples:
Can also pass arguments to hmean
import mir.math.common: approxEqual;
import mir.ndslice.topology: repeat;
import mir.ndslice.slice: sliced;

//Set sum algorithm or output type
auto x = [1, 1e-100, 1, -1e-100].sliced;

assert(x.hmean!"kb2".approxEqual(2));
assert(x.hmean!"precise".approxEqual(2));
assert(x.hmean!(double, "precise").approxEqual(2));

//Provide the summation type
assert(float.max.repeat(3).hmean!double.approxEqual(float.max));
Examples:
For integral slices, pass output type as template parameter to ensure output type is correct.
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [20, 100, 2000, 10, 5, 2].sliced;

auto y = x.hmean;

assert(y.approxEqual(6.97269));
static assert(is(typeof(y) == double));

assert(x.hmean!float.approxEqual(6.97269));
Examples:
hmean works for complex numbers and other user-defined types (provided they can be converted to a floating point or complex type)
import mir.complex.math: approxEqual;
import mir.ndslice.slice: sliced;
import mir.complex;
alias C = Complex!double;

auto x = [C(1, 2), C(2, 3), C(3, 4), C(4, 5)].sliced;
assert(x.hmean.approxEqual(C(1.97110904, 3.14849332)));
Examples:
Arbitrary harmonic mean
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = hmean(20.0, 100, 2000, 10, 5, 2);
assert(x.approxEqual(6.97269));

auto y = hmean!float(20, 100, 2000, 10, 5, 2);
assert(y.approxEqual(6.97269));
hmeanType!F hmean(Range)(Range r)
if (isIterable!Range);
Parameters:
Range r range
hmeanType!F hmean(scope const F[] ar...);
Parameters:
F[] ar values
struct GMeanAccumulator(T) if (isMutable!T && isFloatingPoint!T);
Output range for gmean.
Examples:
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

GMeanAccumulator!double x;
x.put([1.0, 2, 3, 4].sliced);
assert(x.gmean.approxEqual(2.21336384));
x.put(5);
assert(x.gmean.approxEqual(2.60517108));
size_t count;
ProdAccumulator!T prodAccumulator;
const @property F gmean(F = T)()
if (isFloatingPoint!F);
void put(Range)(Range r)
if (isIterable!Range);
void put()(T x);
template gmeanType(T)
gmeanType!F gmean(F, Range)(Range r)
if (isFloatingPoint!F && isIterable!Range);

gmeanType!Range gmean(Range)(Range r)
if (isIterable!Range);
Computes the geometric average of the input.
By default, if F is not floating point type, then the result will have a double type if F is implicitly convertible to a floating point type.
Parameters:
Range r range, must be finite iterable
Returns:
The geometric average of all the elements in the input, must be floating point type
See Also:
gmeanType!F gmean(F)(scope const F[] ar...)
if (isFloatingPoint!F);
Parameters:
F[] ar values
Examples:
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

assert(gmean([1.0, 2, 3]).approxEqual(1.81712059));

assert(gmean!float([1, 2, 3, 4, 5, 6].sliced(3, 2)).approxEqual(2.99379516));

static assert(is(typeof(gmean!float([1, 2, 3])) == float));
Examples:
Geometric mean of vector
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [3.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 2.0].sliced;

assert(x.gmean.approxEqual(2.36178395));
Examples:
Geometric mean of matrix
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;

auto x = [
    [3.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 2.0]
].fuse;

assert(x.gmean.approxEqual(2.36178395));
Examples:
Column gmean of matrix
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;
import mir.ndslice.topology: alongDim, byDim, map;

auto x = [
    [3.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 2.0]
].fuse;
auto result = [2.44948974, 2.73861278, 2.73861278, 1.41421356, 2.29128784, 2.91547594];

// Use byDim or alongDim with map to compute mean of row/column.
assert(x.byDim!1.map!gmean.all!approxEqual(result));
assert(x.alongDim!0.map!gmean.all!approxEqual(result));

// FIXME
// Without using map, computes the mean of the whole slice
// assert(x.byDim!1.gmean.all!approxEqual(result));
// assert(x.alongDim!0.gmean.all!approxEqual(result));
Examples:
Can also set output type
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;
import mir.ndslice.topology: repeat;

auto x = [5120.0, 7340032, 32, 3758096384].sliced;

assert(x.gmean!float.approxEqual(259281.45295212));

auto y = uint.max.repeat(2);
assert(y.gmean!float.approxEqual(cast(float) uint.max));
Examples:
For integral slices, pass output type as template parameter to ensure output type is correct.
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [5, 1, 1, 2, 4, 4,
          2, 7, 5, 1, 2, 10].sliced;

auto y = x.gmean;
static assert(is(typeof(y) == double));

assert(x.gmean!float.approxEqual(2.79160522));
Examples:
gean works for user-defined types, provided the nth root can be taken for them
static struct Foo {
    float x;
    alias x this;
}

import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [Foo(1.0), Foo(2.0), Foo(3.0)].sliced;
assert(x.gmean.approxEqual(1.81712059));
Examples:
Compute gmean tensors along specified dimention of tensors
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;
import mir.ndslice.topology: alongDim, iota, map;

auto x = [
    [1.0, 2, 3],
    [4.0, 5, 6]
].fuse;

assert(x.gmean.approxEqual(2.99379516));

auto result0 = [2.0, 3.16227766, 4.24264069];
assert(x.alongDim!0.map!gmean.all!approxEqual(result0));
assert(x.alongDim!(-2).map!gmean.all!approxEqual(result0));

auto result1 = [1.81712059, 4.93242414];
assert(x.alongDim!1.map!gmean.all!approxEqual(result1));
assert(x.alongDim!(-1).map!gmean.all!approxEqual(result1));

auto y = [
    [
        [1.0, 2, 3],
        [4.0, 5, 6]
    ], [
        [7.0, 8, 9],
        [10.0, 9, 10]
    ]
].fuse;

auto result3 = [
    [2.64575131, 4.0,        5.19615242],
    [6.32455532, 6.70820393, 7.74596669]
];
assert(y.alongDim!0.map!gmean.all!approxEqual(result3));
Examples:
Arbitrary gmean
import mir.math.common: approxEqual;

assert(gmean(1.0, 2, 3).approxEqual(1.81712059));
assert(gmean!float(1, 2, 3).approxEqual(1.81712059));
template median(F, bool allowModify = false)

template median(bool allowModify = false)
Computes the median of slice.
By default, if F is not floating point type or complex type, then the result will have a double type if F is implicitly convertible to a floating point type or a type for which isComplex!F is true.
Can also pass a boolean variable, allowModify, that allows the input slice to be modified. By default, a reference-counted copy is made.
Parameters:
F output type
allowModify Allows the input slice to be modified, default is false
Returns:
the median of the slice
See Also:
@nogc meanType!F median(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);
Parameters:
Slice!(Iterator, N, kind) slice slice
meanType!(T[]) median(T)(scope const T[] ar...);
Parameters:
T[] ar array
auto median(T)(T sliceLike)
if (isConvertibleToSlice!T && !isSlice!T);
Parameters:
T sliceLike type that satisfies isConvertibleToSlice!T && !isSlice!T
Examples:
Median of vector
import mir.ndslice.slice: sliced;

auto x0 = [9.0, 1, 0, 2, 3, 4, 6, 8, 7, 10, 5].sliced;
assert(x0.median == 5);

auto x1 = [9.0, 1, 0, 2, 3, 4, 6, 8, 7, 10].sliced;
assert(x1.median == 5);
Examples:
Median of dynamic array
auto x0 = [9.0, 1, 0, 2, 3, 4, 6, 8, 7, 10, 5];
assert(x0.median == 5);

auto x1 = [9.0, 1, 0, 2, 3, 4, 6, 8, 7, 10];
assert(x1.median == 5);
Examples:
Median of matrix
import mir.ndslice.fuse: fuse;

auto x0 = [
    [9.0, 1, 0, 2,  3], 
    [4.0, 6, 8, 7, 10]
].fuse;

assert(x0.median == 5);
Examples:
Row median of matrix
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;
import mir.ndslice.slice: sliced;
import mir.ndslice.topology: alongDim, byDim, map;

auto x = [
    [0.0, 1.0, 1.5, 2.0, 3.5, 4.25], 
    [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
].fuse;

auto result = [1.75, 1.75].sliced;

// Use byDim or alongDim with map to compute median of row/column.
assert(x.byDim!0.map!median.all!approxEqual(result));
assert(x.alongDim!1.map!median.all!approxEqual(result));
Examples:
Can allow original slice to be modified or set output type
import mir.ndslice.slice: sliced;

auto x0 = [9.0, 1, 0, 2, 3, 4, 6, 8, 7, 10, 5].sliced;
assert(x0.median!true == 5);

auto x1 = [9, 1, 0, 2, 3, 4, 6, 8, 7, 10].sliced;
assert(x1.median!(float, true) == 5);
Examples:
Arbitrary median
assert(median(0, 1, 2, 3, 4) == 2);
Examples:
For integral slices, can pass output type as template parameter to ensure output type is correct
import mir.ndslice.slice: sliced;

auto x = [9, 1, 0, 2, 3, 4, 6, 8, 7, 10].sliced;
assert(x.median!float == 5f);

auto y = x.median;
assert(y == 5.0);
static assert(is(typeof(y) == double));
struct MapSummator(alias fun, T, Summation summation) if (isMutable!T);
Output range that applies function fun to each input before summing
Examples:
import mir.math.common: powi;
import mir.ndslice.slice: sliced;

alias f = (double x) => (powi(x, 2));
MapSummator!(f, double, Summation.pairwise) x;
x.put([0.0, 1, 2, 3, 4].sliced);
assert(x.sum == 30.0);
x.put(5);
assert(x.sum == 55.0);
Summator!(T, summation) summator;
const @property F sum(F = T)();
void put(Range)(Range r)
if (isIterable!Range);
void put()(T x);
enum VarianceAlgo: int;
Variance algorithms.
online
Performs Welford's online algorithm for updating variance. Can also put another VarianceAccumulator of different types, which uses the parallel algorithm from Chan et al., described above.
naive
Calculates variance using E(x^^2) - E(x)^2 (alowing for adjustments for population/sample variance). This algorithm can be numerically unstable. As in: E(x ^^ 2) - E(x) ^^ 2
twoPass
Calculates variance using a two-pass algorithm whereby the input is first centered and then the sum of squares is calculated from that. As in: E((x - E(x)) ^^ 2)
assumeZeroMean
Calculates variance assuming the mean of the dataseries is zero.
hybrid
When slices, slice-like objects, or ranges are the inputs, uses the two-pass algorithm. When an individual data-point is added, uses the online algorithm.
struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation) if (isMutable!T && (varianceAlgo == VarianceAlgo.naive));
Examples:
naive
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

VarianceAccumulator!(double, VarianceAlgo.naive, Summation.naive) v;
v.put(x);
assert(v.variance(true).approxEqual(54.76562 / 12));
assert(v.variance(false).approxEqual(54.76562 / 11));

v.put(4.0);
assert(v.variance(true).approxEqual(57.01923 / 13));
assert(v.variance(false).approxEqual(57.01923 / 12));
this(Range)(Range r)
if (isIterable!Range);
this()(T x);
void put(Range)(Range r)
if (isIterable!Range);
void put()(T x);
void put(U, Summation sumAlgo)(VarianceAccumulator!(U, varianceAlgo, sumAlgo) v);
@property size_t count();
const @property F mean(F = T)();
F sumOfSquares(F = T)();
F centeredSumOfSquares(F = T)();
@property F variance(F = T)(bool isPopulation);
struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation) if (isMutable!T && (varianceAlgo == VarianceAlgo.online));
Examples:
online
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

VarianceAccumulator!(double, VarianceAlgo.online, Summation.naive) v;
v.put(x);
assert(v.variance(true).approxEqual(54.76562 / 12));
assert(v.variance(false).approxEqual(54.76562 / 11));

v.put(4.0);
assert(v.variance(true).approxEqual(57.01923 / 13));
assert(v.variance(false).approxEqual(57.01923 / 12));
this(Range)(Range r)
if (isIterable!Range);
this()(T x);
void put(Range)(Range r)
if (isIterable!Range);
void put()(T x);
void put(U, VarianceAlgo varAlgo, Summation sumAlgo)(VarianceAccumulator!(U, varAlgo, sumAlgo) v)
if (varAlgo != VarianceAlgo.assumeZeroMean);
@property size_t count();
const @property F mean(F = T)();
F centeredSumOfSquares(F = T)();
@property F variance(F = T)(bool isPopulation);
struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation) if (isMutable!T && (varianceAlgo == VarianceAlgo.twoPass));
Examples:
twoPass
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

auto v = VarianceAccumulator!(double, VarianceAlgo.twoPass, Summation.naive)(x);
assert(v.variance(true).approxEqual(54.76562 / 12));
assert(v.variance(false).approxEqual(54.76562 / 11));
this(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);
this(SliceLike)(SliceLike x)
if (isConvertibleToSlice!SliceLike && !isSlice!SliceLike);
this(Range)(Range range)
if (isInputRange!Range && !isConvertibleToSlice!Range && is(elementType!Range : T));
@property size_t count();
const @property F mean(F = T)();
const @property F centeredSumOfSquares(F = T)();
@property F variance(F = T)(bool isPopulation);
struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation) if (isMutable!T && (varianceAlgo == VarianceAlgo.assumeZeroMean));
Examples:
assumeZeroMean
import mir.math.common: approxEqual;
import mir.stat.transform: center;
import mir.ndslice.slice: sliced;

auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
auto x = a.center;

VarianceAccumulator!(double, VarianceAlgo.assumeZeroMean, Summation.naive) v;
v.put(x);
assert(v.variance(true).approxEqual(54.76562 / 12));
assert(v.variance(false).approxEqual(54.76562 / 11));
v.put(4.0);
assert(v.variance(true).approxEqual(70.76562 / 13));
assert(v.variance(false).approxEqual(70.76562 / 12));
this(Range)(Range r)
if (isIterable!Range);
this()(T x);
void put(Range)(Range r)
if (isIterable!Range);
void put()(T x);
void put(U, Summation sumAlgo)(VarianceAccumulator!(U, varianceAlgo, sumAlgo) v);
@property size_t count();
const @property F mean(F = T)();
MeanAccumulator!(T, summation) meanAccumulator()();
const @property F centeredSumOfSquares(F = T)();
@property F variance(F = T)(bool isPopulation);
struct VarianceAccumulator(T, VarianceAlgo varianceAlgo, Summation summation) if (isMutable!T && (varianceAlgo == VarianceAlgo.hybrid));
Examples:
online
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

auto v = VarianceAccumulator!(double, VarianceAlgo.hybrid, Summation.naive)(x);
assert(v.variance(true).approxEqual(54.76562 / 12));
assert(v.variance(false).approxEqual(54.76562 / 11));

v.put(4.0);
assert(v.variance(true).approxEqual(57.01923 / 13));
assert(v.variance(false).approxEqual(57.01923 / 12));
this(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);
this(SliceLike)(SliceLike x)
if (isConvertibleToSlice!SliceLike && !isSlice!SliceLike);
this(Range)(Range range)
if (isIterable!Range && !isConvertibleToSlice!Range);
void put(Range)(Range r)
if (isIterable!Range);
void put()(T x);
void put(U, VarianceAlgo varAlgo, Summation sumAlgo)(VarianceAccumulator!(U, varAlgo, sumAlgo) v)
if (varAlgo != VarianceAlgo.assumeZeroMean);
@property size_t count();
const @property F mean(F = T)();
F centeredSumOfSquares(F = T)();
@property F variance(F = T)(bool isPopulation);
template variance(F, VarianceAlgo varianceAlgo = VarianceAlgo.hybrid, Summation summation = Summation.appropriate)

template variance(VarianceAlgo varianceAlgo = VarianceAlgo.hybrid, Summation summation = Summation.appropriate)

template variance(F, string varianceAlgo, string summation = "appropriate")

template variance(string varianceAlgo, string summation = "appropriate")
Calculates the variance of the input
By default, if F is not floating point type or complex type, then the result will have a double type if F is implicitly convertible to a floating point type or a type for which isComplex!F is true.
Parameters:
F controls type of output
varianceAlgo algorithm for calculating variance (default: VarianceAlgo.hybrid)
summation algorithm for calculating sums (default: Summation.appropriate)
Returns:
The variance of the input, must be floating point or complex type
Examples:
import mir.math.common: approxEqual;
import mir.complex.math: capproxEqual = approxEqual;
import mir.ndslice.slice: sliced;
import mir.complex;
alias C = Complex!double;

assert(variance([1.0, 2, 3]).approxEqual(2.0 / 2));
assert(variance([1.0, 2, 3], true).approxEqual(2.0 / 3));

assert(variance([C(1, 3), C(2), C(3)]).capproxEqual(C(-4, -6) / 2));

assert(variance!float([0, 1, 2, 3, 4, 5].sliced(3, 2)).approxEqual(17.5 / 5));

static assert(is(typeof(variance!float([1, 2, 3])) == float));
Examples:
Variance of vector
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

assert(x.variance.approxEqual(54.76562 / 11));
Examples:
Variance of matrix
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;

auto x = [
    [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
].fuse;

assert(x.variance.approxEqual(54.76562 / 11));
Examples:
Column variance of matrix
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;
import mir.ndslice.topology: alongDim, byDim, map;

auto x = [
    [0.0,  1.0, 1.5, 2.0], 
    [3.5, 4.25, 2.0, 7.5],
    [5.0,  1.0, 1.5, 0.0]
].fuse;
auto result = [13.16667 / 2, 7.041667 / 2, 0.1666667 / 2, 30.16667 / 2];

// Use byDim or alongDim with map to compute variance of row/column.
assert(x.byDim!1.map!variance.all!approxEqual(result));
assert(x.alongDim!0.map!variance.all!approxEqual(result));

// FIXME
// Without using map, computes the variance of the whole slice
// assert(x.byDim!1.variance == x.sliced.variance);
// assert(x.alongDim!0.variance == x.sliced.variance);
Examples:
Can also set algorithm type
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

auto x = a + 1_000_000_000;

auto y = x.variance;
assert(y.approxEqual(54.76562 / 11));

// The naive algorithm is numerically unstable in this case
auto z0 = x.variance!"naive";
assert(!z0.approxEqual(y));

auto z1 = x.variance!"online";
assert(z1.approxEqual(54.76562 / 11));

// But the two-pass algorithm provides a consistent answer
auto z2 = x.variance!"twoPass";
assert(z2.approxEqual(y));

// And the assumeZeroMean algorithm is way off
auto z3 = x.variance!"assumeZeroMean";
assert(z3.approxEqual(1.2e19 / 11));
Examples:
Can also set algorithm or output type
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;
import mir.ndslice.topology: repeat;

//Set population variance, variance algorithm, sum algorithm or output type

auto a = [1.0, 1e100, 1, -1e100].sliced;
auto x = a * 10_000;

/++
Due to Floating Point precision, when centering `x`, subtracting the mean 
from the second and fourth numbers has no effect. Further, after centering 
and squaring `x`, the first and third numbers in the slice have precision 
too low to be included in the centered sum of squares. 
+/
assert(x.variance(false).approxEqual(2.0e208 / 3));
assert(x.variance(true).approxEqual(2.0e208 / 4));

assert(x.variance!("online").approxEqual(2.0e208 / 3));
assert(x.variance!("online", "kbn").approxEqual(2.0e208 / 3));
assert(x.variance!("online", "kb2").approxEqual(2.0e208 / 3));
assert(x.variance!("online", "precise").approxEqual(2.0e208 / 3));
assert(x.variance!(double, "online", "precise").approxEqual(2.0e208 / 3));
assert(x.variance!(double, "online", "precise")(true).approxEqual(2.0e208 / 4));

auto y = uint.max.repeat(3);
auto z = y.variance!ulong;
assert(z == 0.0);
static assert(is(typeof(z) == double));
Examples:
For integral slices, pass output type as template parameter to ensure output type is correct.
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [0, 1, 1, 2, 4, 4,
          2, 7, 5, 1, 2, 0].sliced;

auto y = x.variance;
assert(y.approxEqual(50.91667 / 11));
static assert(is(typeof(y) == double));

assert(x.variance!float.approxEqual(50.91667 / 11));
Examples:
Variance works for complex numbers and other user-defined types (provided they can be converted to a floating point or complex type)
import mir.complex.math: approxEqual;
import mir.ndslice.slice: sliced;
import mir.complex;
alias C = Complex!double;

auto x = [C(1, 2), C(2, 3), C(3, 4), C(4, 5)].sliced;
assert(x.variance.approxEqual((C(0, 10)) / 3));
Examples:
Compute variance along specified dimention of tensors
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;
import mir.ndslice.topology: as, iota, alongDim, map, repeat;

auto x = [
    [0.0, 1, 2],
    [3.0, 4, 5]
].fuse;

assert(x.variance.approxEqual(17.5 / 5));

auto m0 = [4.5, 4.5, 4.5];
assert(x.alongDim!0.map!variance.all!approxEqual(m0));
assert(x.alongDim!(-2).map!variance.all!approxEqual(m0));

auto m1 = [1.0, 1.0];
assert(x.alongDim!1.map!variance.all!approxEqual(m1));
assert(x.alongDim!(-1).map!variance.all!approxEqual(m1));

assert(iota(2, 3, 4, 5).as!double.alongDim!0.map!variance.all!approxEqual(repeat(3600.0 / 2, 3, 4, 5)));
Examples:
Arbitrary variance
assert(variance(1.0, 2, 3) == 1.0);
assert(variance!float(1, 2, 3) == 1f);
meanType!F variance(Range)(Range r, bool isPopulation = false)
if (isIterable!Range);
Parameters:
Range r range, must be finite iterable
bool isPopulation true if population variance, false if sample variance (default)
meanType!F variance(scope const F[] ar...);
Parameters:
F[] ar values
template stdevType(T)
template standardDeviation(F, VarianceAlgo varianceAlgo = VarianceAlgo.hybrid, Summation summation = Summation.appropriate)

template standardDeviation(VarianceAlgo varianceAlgo = VarianceAlgo.hybrid, Summation summation = Summation.appropriate)

template standardDeviation(F, string varianceAlgo, string summation = "appropriate")

template standardDeviation(string varianceAlgo, string summation = "appropriate")
Calculates the standard deviation of the input
By default, if F is not floating point type, then the result will have a double type if F is implicitly convertible to a floating point type.
Parameters:
F controls type of output
varianceAlgo algorithm for calculating variance (default: VarianceAlgo.hybrid)
summation algorithm for calculating sums (default: Summation.appropriate)
Returns:
The standard deviation of the input, must be floating point type type
Examples:
import mir.math.common: approxEqual, sqrt;
import mir.ndslice.slice: sliced;

assert(standardDeviation([1.0, 2, 3]).approxEqual(sqrt(2.0 / 2)));
assert(standardDeviation([1.0, 2, 3], true).approxEqual(sqrt(2.0 / 3)));

assert(standardDeviation!float([0, 1, 2, 3, 4, 5].sliced(3, 2)).approxEqual(sqrt(17.5 / 5)));

static assert(is(typeof(standardDeviation!float([1, 2, 3])) == float));
Examples:
Standard deviation of vector
import mir.math.common: approxEqual, sqrt;
import mir.ndslice.slice: sliced;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

assert(x.standardDeviation.approxEqual(sqrt(54.76562 / 11)));
Examples:
Standard deviation of matrix
import mir.math.common: approxEqual, sqrt;
import mir.ndslice.fuse: fuse;

auto x = [
    [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
].fuse;

assert(x.standardDeviation.approxEqual(sqrt(54.76562 / 11)));
Examples:
Column standard deviation of matrix
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual, sqrt;
import mir.ndslice.fuse: fuse;
import mir.ndslice.topology: alongDim, byDim, map;

auto x = [
    [0.0,  1.0, 1.5, 2.0], 
    [3.5, 4.25, 2.0, 7.5],
    [5.0,  1.0, 1.5, 0.0]
].fuse;
auto result = [13.16667 / 2, 7.041667 / 2, 0.1666667 / 2, 30.16667 / 2].map!sqrt;

// Use byDim or alongDim with map to compute standardDeviation of row/column.
assert(x.byDim!1.map!standardDeviation.all!approxEqual(result));
assert(x.alongDim!0.map!standardDeviation.all!approxEqual(result));

// FIXME
// Without using map, computes the standardDeviation of the whole slice
// assert(x.byDim!1.standardDeviation == x.sliced.standardDeviation);
// assert(x.alongDim!0.standardDeviation == x.sliced.standardDeviation);
Examples:
Can also set algorithm type
import mir.math.common: approxEqual, sqrt;
import mir.ndslice.slice: sliced;

auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

auto x = a + 1_000_000_000;

auto y = x.standardDeviation;
assert(y.approxEqual(sqrt(54.76562 / 11)));

// The naive algorithm is numerically unstable in this case
auto z0 = x.standardDeviation!"naive";
assert(!z0.approxEqual(y));

// But the two-pass algorithm provides a consistent answer
auto z1 = x.standardDeviation!"twoPass";
assert(z1.approxEqual(y));
Examples:
Can also set algorithm or output type
import mir.math.common: approxEqual, sqrt;
import mir.ndslice.slice: sliced;
import mir.ndslice.topology: repeat;

//Set population standard deviation, standardDeviation algorithm, sum algorithm or output type

auto a = [1.0, 1e100, 1, -1e100].sliced;
auto x = a * 10_000;

/++
Due to Floating Point precision, when centering `x`, subtracting the mean 
from the second and fourth numbers has no effect. Further, after centering 
and squaring `x`, the first and third numbers in the slice have precision 
too low to be included in the centered sum of squares. 
+/
assert(x.standardDeviation(false).approxEqual(sqrt(2.0e208 / 3)));
assert(x.standardDeviation(true).approxEqual(sqrt(2.0e208 / 4)));

assert(x.standardDeviation!("online").approxEqual(sqrt(2.0e208 / 3)));
assert(x.standardDeviation!("online", "kbn").approxEqual(sqrt(2.0e208 / 3)));
assert(x.standardDeviation!("online", "kb2").approxEqual(sqrt(2.0e208 / 3)));
assert(x.standardDeviation!("online", "precise").approxEqual(sqrt(2.0e208 / 3)));
assert(x.standardDeviation!(double, "online", "precise").approxEqual(sqrt(2.0e208 / 3)));
assert(x.standardDeviation!(double, "online", "precise")(true).approxEqual(sqrt(2.0e208 / 4)));

auto y = uint.max.repeat(3);
auto z = y.standardDeviation!ulong;
assert(z == 0.0);
static assert(is(typeof(z) == double));
Examples:
For integral slices, pass output type as template parameter to ensure output type is correct.
import mir.math.common: approxEqual, sqrt;
import mir.ndslice.slice: sliced;

auto x = [0, 1, 1, 2, 4, 4,
          2, 7, 5, 1, 2, 0].sliced;

auto y = x.standardDeviation;
assert(y.approxEqual(sqrt(50.91667 / 11)));
static assert(is(typeof(y) == double));

assert(x.standardDeviation!float.approxEqual(sqrt(50.91667 / 11)));
Examples:
Variance works for other user-defined types (provided they can be converted to a floating point)
import mir.ndslice.slice: sliced;

static struct Foo {
    float x;
    alias x this;
}

Foo[] foo = [Foo(1f), Foo(2f), Foo(3f)];
assert(foo.standardDeviation == 1f);
Examples:
Compute standard deviation along specified dimention of tensors
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual, sqrt;
import mir.ndslice.fuse: fuse;
import mir.ndslice.topology: as, iota, alongDim, map, repeat;

auto x = [
    [0.0, 1, 2],
    [3.0, 4, 5]
].fuse;

assert(x.standardDeviation.approxEqual(sqrt(17.5 / 5)));

auto m0 = repeat(sqrt(4.5), 3);
assert(x.alongDim!0.map!standardDeviation.all!approxEqual(m0));
assert(x.alongDim!(-2).map!standardDeviation.all!approxEqual(m0));

auto m1 = [1.0, 1.0];
assert(x.alongDim!1.map!standardDeviation.all!approxEqual(m1));
assert(x.alongDim!(-1).map!standardDeviation.all!approxEqual(m1));

assert(iota(2, 3, 4, 5).as!double.alongDim!0.map!standardDeviation.all!approxEqual(repeat(sqrt(3600.0 / 2), 3, 4, 5)));
Examples:
Arbitrary standard deviation
import mir.math.common: sqrt;

assert(standardDeviation(1.0, 2, 3) == 1.0);
assert(standardDeviation!float(1, 2, 3) == 1f);
stdevType!F standardDeviation(Range)(Range r, bool isPopulation = false)
if (isIterable!Range);
Parameters:
Range r range, must be finite iterable
bool isPopulation true if population standard deviation, false if sample standard deviation (default)
stdevType!F standardDeviation(scope const F[] ar...);
Parameters:
F[] ar values
enum QuantileAlgo: int;
Algorithms used to calculate the quantile of an input x at probability p.
These algorithms match the same provided in R's (as of version 3.6.2) quantile function. In turn, these were discussed in Hyndman and Fan (1996).
All sample quantiles are defined as weighted averages of consecutive order statistics. For each quantileAlgo, the sample quantile is given by (using R's 1-based indexing notation):
(1 - gamma) * xj + gamma * xj + 1
where xj is the jth order statistic. gamma is a function of j = floor(np + m) and g = np + m - j where n is the sample size, p is the probability, and m is a constant determined by the quantile type.
Type m gamma
    Discontinuous sample quantile
type1 0 0 if g = 0 and 1 otherwise.
type2 0 0.5 if g = 0 and 1 otherwise.
type3 -0.5 0 if g = 0 and j is even and 1 otherwise.
    Continuous sample quantile
type4 0 gamma = g
type5 0.5 gamma = g
type6 p gamma = g
type7 1 - p gamma = g
type8 (p + 1) / 3 gamma = g
type9 p / 4 + 3 / 8 gamma = g

References Hyndman, R. J. and Fan, Y. (1996) Sample quantiles in statistical packages, American Statistician 50, 361--365. 10.2307/2684934.

See Also:
type1

Discontinuous sample quantile

Inverse of empirical distribution function.
type2
Similar to type1, but averages at discontinuities.
type3
SAS definition: nearest even order statistic.
type4

Continuous sample quantile

Linear interpolation of the empirical cdf.
type5
A piece-wise linear function hwere the knots are the values midway through the steps of the empirical cdf. Popular amongst hydrologists.
type6
Used by Minitab and by SPSS.
type7
This is used by S and is the default for R.
type8
The resulting quantile estimates are approximately median-unbiased regardless of the distribution of the input. Preferred by Hyndman and Fan (1996).
type9
The resulting quantile estimates are approximately unbiased for the expected order statistics of the input is normally distributed.
template quantileType(T, QuantileAlgo quantileAlgo)
For all QuantileAlgo except QuantileAlgo.type1 and QuantileAlgo.type3, this is an alias to the meanType  of T
For QuantileAlgo.type1 and QuantileAlgo.type3, this is an alias to the elementType  of T.
template quantile(F, QuantileAlgo quantileAlgo = QuantileAlgo.type7, bool allowModifySlice = false, bool allowModifyProbability = false) if (isFloatingPoint!F || (quantileAlgo == QuantileAlgo.type1 || quantileAlgo == QuantileAlgo.type3))
Computes the quantile(s) of the input, given one or more probabilities p.
By default, if p is a Slice , built-in dynamic array, or type with asSlice, then the output type is a reference-counted copy of the input. A compile-time parameter is provided to instead overwrite the input in-place.
For all QuantileAlgo except QuantileAlgo.type1 and QuantileAlgo.type3, by default, if F is not floating point type or complex type, then the result will have a double type if F is implicitly convertible to a floating point type or a type for which isComplex!F is true.
For QuantileAlgo.type1 and QuantileAlgo.type3, the return type is the elementType  of the input.
Parameters:
F controls type of output
quantileAlgo algorithm for calculating quantile (default: QuantileAlgo.type7)
allowModifySlice controls whether the input is modified in place, default is false
Returns:
The quantile of all the elements in the input at probability p.
quantileType!(F, quantileAlgo) quantile(Iterator, size_t N, SliceKind kind, G)(Slice!(Iterator, N, kind) slice, G p)
if (isFloatingPoint!(Unqual!G));
Parameters:
Slice!(Iterator, N, kind) slice slice
G p probability
auto quantile(IteratorA, size_t N, SliceKind kindA, IteratorB, SliceKind kindB)(Slice!(IteratorA, N, kindA) slice, Slice!(IteratorB, 1, kindB) p)
if (isFloatingPoint!(elementType!(Slice!IteratorB)));

auto quantile(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice, scope const F[] p...)
if (isFloatingPoint!(elementType!(F[])));

auto quantile(SliceLike, G)(SliceLike x, G p)
if (isConvertibleToSlice!SliceLike && !isSlice!SliceLike && isFloatingPoint!(Unqual!G));

auto quantile(SliceLikeX, SliceLikeP)(SliceLikeX x, SliceLikeP p)
if (isConvertibleToSlice!SliceLikeX && !isSlice!SliceLikeX && isConvertibleToSlice!SliceLikeP && !isSlice!SliceLikeP);
Parameters:
Slice!(IteratorA, N, kindA) slice slice
Slice!(IteratorB, 1, kindB) p probability slice
template quantile(QuantileAlgo quantileAlgo = QuantileAlgo.type7, bool allowModifySlice = false, bool allowModifyProbability = false)

template quantile(F, string quantileAlgo, bool allowModifySlice = false, bool allowModifyProbability = false)

template quantile(string quantileAlgo, bool allowModifySlice = false, bool allowModifyProbability = false)
Examples:
Simple example
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [3.0, 1.0, 4.0, 2.0, 0.0].sliced;
          
assert(x.quantile(0.5).approxEqual(2.0));

auto qtile = [0.25, 0.75].sliced;

assert(x.quantile(qtile).all!approxEqual([1.0, 3.0]));
assert(x.quantile(0.25, 0.75).all!approxEqual([1.0, 3.0]));
Examples:
Modify probability in place
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [3.0, 1.0, 4.0, 2.0, 0.0].sliced;

auto qtile = [0.25, 0.75].sliced;
auto qtile_copy = qtile.dup;

x.quantile!("type7", false, true)(qtile);
assert(qtile.all!approxEqual([1.0, 3.0]));
assert(!qtile.all!approxEqual(qtile_copy));
Examples:
Quantile of vector
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [1.0, 9.8, 0.2, 8.5, 5.8, 3.5, 4.5, 8.2, 5.2, 5.2,
          2.5, 1.8, 2.2, 3.8, 5.2, 9.2, 6.2, 9.2, 9.2, 8.5].sliced;

assert(x.quantile(0.5).approxEqual(5.20));

auto qtile = [0.25, 0.75].sliced;

assert(x.quantile(qtile).all!approxEqual([3.250, 8.500]));
Examples:
Quantile of matrix
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;
import mir.ndslice.slice: sliced;

auto x = [
    [1.0, 9.8, 0.2, 8.5, 5.8, 3.5, 4.5, 8.2, 5.2, 5.2],
    [2.5, 1.8, 2.2, 3.8, 5.2, 9.2, 6.2, 9.2, 9.2, 8.5]
].fuse;

assert(x.quantile(0.5).approxEqual(5.20));

auto qtile = [0.25, 0.75].sliced;

assert(x.quantile(qtile).all!approxEqual([3.250, 8.500]));
Examples:
Row quantile of matrix
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;
import mir.ndslice.slice: sliced;
import mir.ndslice.topology: alongDim, byDim, map, flattened;

auto x = [
    [1.0, 9.8, 0.2, 8.5, 5.8, 3.5, 4.5, 8.2, 5.2, 5.2],
    [2.5, 1.8, 2.2, 3.8, 5.2, 9.2, 6.2, 9.2, 9.2, 8.5]
].fuse;

auto result0 = [5.200, 5.700];

// Use byDim or alongDim with map to compute median of row/column.
assert(x.byDim!0.map!(a => a.quantile(0.5)).all!approxEqual(result0));
assert(x.alongDim!1.map!(a => a.quantile(0.5)).all!approxEqual(result0));

auto qtile = [0.25, 0.75].sliced;
auto result1 = [[3.750, 7.600], [2.825, 9.025]];

assert(x.byDim!0.map!(a => a.quantile(qtile)).all!(all!approxEqual)(result1));
Examples:
Allow modification of input
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [3.0, 1.0, 4.0, 2.0, 0.0].sliced;
auto x_copy = x.dup;

auto result = x.quantile!(QuantileAlgo.type7, true)(0.5);
assert(!x.all!approxEqual(x_copy));
Examples:
Double-check probability is not modified
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [3.0, 1.0, 4.0, 2.0, 0.0].sliced;

auto qtile = [0.25, 0.75].sliced;
auto qtile_copy = qtile.dup;

auto result = x.quantile!("type7", false, false)(qtile);
assert(result.all!approxEqual([1.0, 3.0]));
assert(qtile.all!approxEqual(qtile_copy));
Examples:
Can also set algorithm type
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [1.0, 9.8, 0.2, 8.5, 5.8, 3.5, 4.5, 8.2, 5.2, 5.2,
          2.5, 1.8, 2.2, 3.8, 5.2, 9.2, 6.2, 9.2, 9.2, 8.5].sliced;

assert(x.quantile!"type1"(0.5).approxEqual(5.20));
assert(x.quantile!"type2"(0.5).approxEqual(5.20));
assert(x.quantile!"type3"(0.5).approxEqual(5.20));
assert(x.quantile!"type4"(0.5).approxEqual(5.20));
assert(x.quantile!"type5"(0.5).approxEqual(5.20));
assert(x.quantile!"type6"(0.5).approxEqual(5.20));
assert(x.quantile!"type7"(0.5).approxEqual(5.20));
assert(x.quantile!"type8"(0.5).approxEqual(5.20));
assert(x.quantile!"type9"(0.5).approxEqual(5.20));
Examples:
Can also set algorithm or output type
import mir.ndslice.slice: sliced;

auto a = [1, 1e100, 1, -1e100].sliced;

auto x = a * 10_000;

auto result0 = x.quantile!float(0.5);
assert(result0 == 10_000f);
static assert(is(typeof(result0) == float));

auto result1 = x.quantile!(float, "type8")(0.5);
assert(result1 == 10_000f);
static assert(is(typeof(result1) == float));
Examples:
Support for integral and user-defined types for type 1 & 3
import mir.ndslice.topology: repeat;

auto x = uint.max.repeat(3);
assert(x.quantile!(uint, "type1")(0.5) == uint.max);
assert(x.quantile!(uint, "type3")(0.5) == uint.max);

static struct Foo {
    float x;
    alias x this;
}

Foo[] foo = [Foo(1f), Foo(2f), Foo(3f)];
assert(foo.quantile!"type1"(0.5) == 2f);
assert(foo.quantile!"type3"(0.5) == 2f);
Examples:
Compute quantile along specified dimention of tensors
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;
import mir.ndslice.topology: as, iota, alongDim, map, repeat;

auto x = [
    [0.0, 1, 3],
    [4.0, 5, 7]
].fuse;

assert(x.quantile(0.5).approxEqual(3.5));

auto m0 = [2.0, 3.0, 5.0];
assert(x.alongDim!0.map!(a => a.quantile(0.5)).all!approxEqual(m0));
assert(x.alongDim!(-2).map!(a => a.quantile(0.5)).all!approxEqual(m0));

auto m1 = [1.0, 5.0];
assert(x.alongDim!1.map!(a => a.quantile(0.5)).all!approxEqual(m1));
assert(x.alongDim!(-1).map!(a => a.quantile(0.5)).all!approxEqual(m1));

assert(iota(2, 3, 4, 5).as!double.alongDim!0.map!(a => a.quantile(0.5)).all!approxEqual(iota([3, 4, 5], 3 * 4 * 5 / 2)));
Examples:
Support for array
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;

double[] x = [3.0, 1.0, 4.0, 2.0, 0.0];
          
assert(x.quantile(0.5).approxEqual(2.0));

double[] qtile = [0.25, 0.75];

assert(x.quantile(qtile).all!approxEqual([1.0, 3.0]));
quantileType!(Slice!Iterator, quantileAlgo) quantile(Iterator, size_t N, SliceKind kind, G)(Slice!(Iterator, N, kind) slice, G p)
if (isFloatingPoint!(Unqual!G));

auto quantile(IteratorA, size_t N, SliceKind kindA, IteratorB, SliceKind kindB)(Slice!(IteratorA, N, kindA) slice, Slice!(IteratorB, 1, kindB) p)
if (isFloatingPoint!(elementType!(Slice!IteratorB)));

auto quantile(Iterator, size_t N, SliceKind kind, G)(Slice!(Iterator, N, kind) slice, scope G[] p...)
if (isFloatingPoint!(elementType!(G[])));

auto quantile(SliceLike, G)(SliceLike x, G p)
if (isConvertibleToSlice!SliceLike && !isSlice!SliceLike && isFloatingPoint!(Unqual!G));

auto quantile(SliceLikeX, SliceLikeP)(SliceLikeX x, SliceLikeP p)
if (isConvertibleToSlice!SliceLikeX && !isSlice!SliceLikeX && isConvertibleToSlice!SliceLikeP && !isSlice!SliceLikeP);
Parameters:
Slice!(Iterator, N, kind) slice slice
G p probability
template interquartileRange(F, QuantileAlgo quantileAlgo = QuantileAlgo.type7, bool allowModifySlice = false)

template interquartileRange(QuantileAlgo quantileAlgo = QuantileAlgo.type7, bool allowModifySlice = false)

template interquartileRange(F, string quantileAlgo, bool allowModifySlice = false)

template interquartileRange(string quantileAlgo, bool allowModifySlice = false)
Computes the interquartile range of the input.
By default, this function computes the result using quantile, i.e. result = quantile(x, 0.75) - quantile(x, 0.25). There are also overloads for providing a low value, as in result = quantile(x, 1 - low) - quantile(x, low) and both a low and high value, as in result = quantile(x, high) - quantile(x, low).
For all QuantileAlgo except QuantileAlgo.type1 and QuantileAlgo.type3, by default, if F is not floating point type or complex type, then the result will have a double type if F is implicitly convertible to a floating point type or a type for which isComplex!F is true.
For QuantileAlgo.type1 and QuantileAlgo.type3, the return type is the elementType  of the input.
Parameters:
F controls type of output
quantileAlgo algorithm for calculating quantile (default: QuantileAlgo.type7)
allowModifySlice controls whether the input is modified in place, default is false
Returns:
The interquartile range of the input.
See Also:
Examples:
Simple example
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [3.0, 1.0, 4.0, 2.0, 0.0].sliced;

assert(x.interquartileRange.approxEqual(2.0));
assert(x.interquartileRange(0.25).approxEqual(2.0));
assert(x.interquartileRange(0.25, 0.75).approxEqual(2.0));
Examples:
Interquartile Range of vector
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [1.0, 9.8, 0.2, 8.5, 5.8, 3.5, 4.5, 8.2, 5.2, 5.2,
          2.5, 1.8, 2.2, 3.8, 5.2, 9.2, 6.2, 9.2, 9.2, 8.5].sliced;

assert(x.interquartileRange.approxEqual(5.25));
Examples:
Interquartile Range of matrix
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;
import mir.ndslice.slice: sliced;

auto x = [
    [1.0, 9.8, 0.2, 8.5, 5.8, 3.5, 4.5, 8.2, 5.2, 5.2],
    [2.5, 1.8, 2.2, 3.8, 5.2, 9.2, 6.2, 9.2, 9.2, 8.5]
].fuse;

assert(x.interquartileRange.approxEqual(5.25));
Examples:
Allow modification of input
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [3.0, 1.0, 4.0, 2.0, 0.0].sliced;
auto x_copy = x.dup;

auto result = x.interquartileRange!(QuantileAlgo.type7, true);
assert(!x.all!approxEqual(x_copy));
Examples:
Can also set algorithm type
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [1.0, 9.8, 0.2, 8.5, 5.8, 3.5, 4.5, 8.2, 5.2, 5.2,
          2.5, 1.8, 2.2, 3.8, 5.2, 9.2, 6.2, 9.2, 9.2, 8.5].sliced;

assert(x.interquartileRange!"type1".approxEqual(6.0));
assert(x.interquartileRange!"type2".approxEqual(5.5));
assert(x.interquartileRange!"type3".approxEqual(6.0));
assert(x.interquartileRange!"type4".approxEqual(6.0));
assert(x.interquartileRange!"type5".approxEqual(5.5));
assert(x.interquartileRange!"type6".approxEqual(5.75));
assert(x.interquartileRange!"type7".approxEqual(5.25));
assert(x.interquartileRange!"type8".approxEqual(5.583333));
assert(x.interquartileRange!"type9".approxEqual(5.5625));
Examples:
Can also set algorithm or output type
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto a = [1, 1e34, 1, -1e34, 0].sliced;

auto x = a * 10_000;

auto result0 = x.interquartileRange!float;
assert(result0.approxEqual(10_000));
static assert(is(typeof(result0) == float));

auto result1 = x.interquartileRange!(float, "type8");
assert(result1.approxEqual(6.666667e37));
static assert(is(typeof(result1) == float));
Examples:
Support for array
import mir.math.common: approxEqual;

double[] x = [3.0, 1.0, 4.0, 2.0, 0.0];
          
assert(x.interquartileRange.approxEqual(2.0));
quantileType!(F, quantileAlgo) interquartileRange(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);
Parameters:
Slice!(Iterator, N, kind) slice slice
quantileType!(F, quantileAlgo) interquartileRange(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice, F lo = 0.25);
Parameters:
Slice!(Iterator, N, kind) slice slice
F lo low value
quantileType!(F, quantileAlgo) interquartileRange(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice, F lo, F hi);
Parameters:
Slice!(Iterator, N, kind) slice slice
F lo low value
F hi high value
quantileType!(F[], quantileAlgo) interquartileRange(scope F[] array...);

auto interquartileRange(SliceLike)(SliceLike x)
if (isConvertibleToSlice!SliceLike && !isSlice!SliceLike);
Parameters:
F[] array array
template medianAbsoluteDeviation(F)

meanType!(Slice!(Iterator, N, kind)) medianAbsoluteDeviation(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);

meanType!(T[]) medianAbsoluteDeviation(T)(scope const T[] ar...);

auto medianAbsoluteDeviation(SliceLike)(SliceLike x)
if (isConvertibleToSlice!SliceLike && !isSlice!SliceLike);
Calculates the median absolute deviation about the median of the input.
By default, if F is not floating point type, then the result will have a double type if F is implicitly convertible to a floating point type.
Parameters:
F output type
Returns:
The median absolute deviation of the input
Examples:
medianAbsoluteDeviation of vector
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

assert(x.medianAbsoluteDeviation.approxEqual(1.25));
Examples:
Median Absolute Deviation of matrix
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;

auto x = [
    [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
].fuse;

assert(x.medianAbsoluteDeviation.approxEqual(1.25));
Examples:
Median Absolute Deviation of dynamic array
import mir.math.common: approxEqual;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0];

assert(x.medianAbsoluteDeviation.approxEqual(1.25));
meanType!F medianAbsoluteDeviation(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);
Parameters:
Slice!(Iterator, N, kind) slice slice
template dispersion(alias centralTendency = mean, alias transform = "a * a", alias summarize = mean)
Calculates the dispersion of the input.
For an input x, this function first centers x by subtracting each e in x by the result of centralTendency, then it transforms the centered values using the function transform, and then finally summarizes that information using the summarize funcion.
The default functions provided are equivalent to calculating the population variance. The centralTendency default is the mean function, which results in the input being centered about the mean. The default transform function will square the centered values. The default summarize function is mean, which will return the mean of the squared centered values.
Parameters:
centralTendency function that will produce the value that the input is centered about, default is mean
transform function to transform centered values, default squares the centered values
summarize function to summarize the transformed centered values, default is mean
Returns:
The dispersion of the input
Examples:
Simple examples
import mir.complex: Complex;
import mir.complex.math: capproxEqual = approxEqual;
import mir.functional: naryFun;
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

alias C = Complex!double;

assert(dispersion([1.0, 2, 3]).approxEqual(2.0 / 3));

assert(dispersion([C(1.0, 3), C(2), C(3)]).capproxEqual(C(-4, -6) / 3));

assert(dispersion!(mean!float, "a * a", mean!float)([0, 1, 2, 3, 4, 5].sliced(3, 2)).approxEqual(17.5 / 6));

static assert(is(typeof(dispersion!(mean!float, "a ^^ 2", mean!float)([1, 2, 3])) == float));
Examples:
Dispersion of vector
import mir.math.common: approxEqual;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0];

assert(x.dispersion.approxEqual(54.76562 / 12));
Examples:
Dispersion of matrix
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;

auto x = [
    [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
].fuse;

assert(x.dispersion.approxEqual(54.76562 / 12));
Examples:
Column dispersion of matrix
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;
import mir.ndslice.topology: alongDim, byDim, map;

auto x = [
    [0.0,  1.0, 1.5, 2.0], 
    [3.5, 4.25, 2.0, 7.5],
    [5.0,  1.0, 1.5, 0.0]
].fuse;
auto result = [13.16667 / 3, 7.041667 / 3, 0.1666667 / 3, 30.16667 / 3];

// Use byDim or alongDim with map to compute dispersion of row/column.
assert(x.byDim!1.map!dispersion.all!approxEqual(result));
assert(x.alongDim!0.map!dispersion.all!approxEqual(result));

// FIXME
// Without using map, computes the dispersion of the whole slice
// assert(x.byDim!1.dispersion == x.sliced.dispersion);
// assert(x.alongDim!0.dispersion == x.sliced.dispersion);
Examples:
Can also set functions to change type of dispersion that is used
import mir.functional: naryFun;
import mir.math.common: approxEqual, fabs, sqrt;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0];
          
alias square = naryFun!"a * a";

// Other population variance examples
assert(x.dispersion.approxEqual(54.76562 / 12));
assert(x.dispersion!mean.approxEqual(54.76562 / 12));
assert(x.dispersion!(mean, square).approxEqual(54.76562 / 12));
assert(x.dispersion!(mean, square, mean).approxEqual(54.76562 / 12));

// Population standard deviation
assert(x.dispersion!(mean, square, mean).sqrt.approxEqual(sqrt(54.76562 / 12)));

// Mean absolute deviation about the mean
assert(x.dispersion!(mean, fabs, mean).approxEqual(21.0 / 12));
//Mean absolute deviation about the median
assert(x.dispersion!(median, fabs, mean).approxEqual(19.25000 / 12));
//Median absolute deviation about the mean
assert(x.dispersion!(mean, fabs, median).approxEqual(1.43750));
//Median absolute deviation about the median
assert(x.dispersion!(median, fabs, median).approxEqual(1.25000));
Examples:
For integral slices, pass output type to centralTendency, transform, and summary functions as template parameter to ensure output type is correct.
import mir.functional: naryFun;
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [0, 1, 1, 2, 4, 4,
          2, 7, 5, 1, 2, 0].sliced;

alias square = naryFun!"a * a";

auto y = x.dispersion;
assert(y.approxEqual(50.91667 / 12));
static assert(is(typeof(y) == double));

assert(x.dispersion!(mean!float, square, mean!float).approxEqual(50.91667 / 12));
Examples:
Dispersion works for complex numbers and other user-defined types (provided that the centralTendency, transform, and summary functions are defined for those types)
import mir.ndslice.slice: sliced;
import std.complex: Complex;
import std.math.operations: isClose;

auto x = [Complex!double(1, 2), Complex!double(2, 3), Complex!double(3, 4), Complex!double(4, 5)].sliced;
assert(x.dispersion.isClose(Complex!double(0, 10) / 4));
Examples:
Compute mean tensors along specified dimention of tensors
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;
import mir.ndslice.topology: as, iota, alongDim, map, repeat;

auto x = [
    [0.0, 1, 2],
    [3.0, 4, 5]
].fuse;

assert(x.dispersion.approxEqual(17.5 / 6));

auto m0 = [2.25, 2.25, 2.25];
assert(x.alongDim!0.map!dispersion.all!approxEqual(m0));
assert(x.alongDim!(-2).map!dispersion.all!approxEqual(m0));

auto m1 = [2.0 / 3, 2.0 / 3];
assert(x.alongDim!1.map!dispersion.all!approxEqual(m1));
assert(x.alongDim!(-1).map!dispersion.all!approxEqual(m1));

assert(iota(2, 3, 4, 5).as!double.alongDim!0.map!dispersion.all!approxEqual(repeat(1800.0 / 2, 3, 4, 5)));
Examples:
Arbitrary dispersion
import mir.functional: naryFun;
import mir.math.common: approxEqual;

alias square = naryFun!"a * a";

assert(dispersion(1.0, 2, 3).approxEqual(2.0 / 3));
assert(dispersion!(mean!float, square, mean!float)(1, 2, 3).approxEqual(2f / 3));
auto dispersion(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);

auto dispersion(T)(scope const T[] ar...);

auto dispersion(SliceLike)(SliceLike x)
if (isConvertibleToSlice!SliceLike && !isSlice!SliceLike);
Parameters:
Slice!(Iterator, N, kind) slice slice
enum SkewnessAlgo: int;
Skewness algorithms.
online
Similar to Welford's algorithm for updating variance, but adjusted for skewness. Can also put another SkewnessAccumulator of the same type, which uses the parallel algorithm from Terriberry that extends the work of Chan et al.
naive
Calculates skewness using (E(x^^3) - 3 * mu * sigma ^^ 2 + mu ^^ 3) / (sigma ^^ 3)
This algorithm can be numerically unstable.
twoPass
Calculates skewness by first calculating the mean, then calculating E((x - E(x)) ^^ 3) / (E((x - E(x)) ^^ 2) ^^ 1.5)
threePass
Calculates skewness by first calculating the mean, then the standard deviation, then calculating E(((x - E(x)) / (E((x - E(x)) ^^ 2) ^^ 0.5)) ^^ 3)
assumeZeroMean
Calculates skewness assuming the mean of the input is zero.
hybrid
When slices, slice-like objects, or ranges are the inputs, uses the two-pass algorithm. When an individual data-point is added, uses the online algorithm.
struct SkewnessAccumulator(T, SkewnessAlgo skewnessAlgo, Summation summation) if (isMutable!T && (skewnessAlgo == SkewnessAlgo.naive));
Examples:
naive
import mir.math.common: pow;
import mir.math.sum: Summation;
import mir.ndslice.slice: sliced;
import mir.test: shouldApprox;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

SkewnessAccumulator!(double, SkewnessAlgo.naive, Summation.naive) v;
v.put(x);
v.skewness(true).shouldApprox == (117.005859 / 12) / pow(54.765625 / 12, 1.5);
v.skewness(false).shouldApprox == (117.005859 / 12) / pow(54.765625 / 11, 1.5) * (12.0 ^^ 2) / (11.0 * 10.0);

v.put(4.0);
v.skewness(true).shouldApprox == (100.238166 / 13) / pow(57.019231 / 13, 1.5);
v.skewness(false).shouldApprox == (100.238166 / 13) / pow(57.019231 / 12, 1.5) * (13.0 ^^ 2) / (12.0 * 11.0);
alias S = Summator!(T, summation);
this(Range)(Range r)
if (isIterable!Range);
this()(T x);
void put(Range)(Range r)
if (isIterable!Range);
void put()(T x);
@property size_t count();
@property F mean(F = T)();
@property F variance(F = T)(bool isPopulation);
F sumOfCubes(F = T)();
F sumOfSquares(F = T)();
F centeredSumOfSquares(F = T)();
F centeredSumOfCubes(F = T)();
F scaledSumOfCubes(F = T)(bool isPopulation);
F skewness(F = T)(bool isPopulation);
struct SkewnessAccumulator(T, SkewnessAlgo skewnessAlgo, Summation summation) if (isMutable!T && (skewnessAlgo == SkewnessAlgo.online));
Examples:
online
import mir.math.common: approxEqual, pow;
import mir.math.sum: Summation;
import mir.ndslice.slice: sliced;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

SkewnessAccumulator!(double, SkewnessAlgo.online, Summation.naive) v;
v.put(x);
assert(v.skewness(true).approxEqual((117.005859 / 12) / pow(54.765625 / 12, 1.5)));
assert(v.skewness(false).approxEqual((117.005859 / 12) / pow(54.765625 / 11, 1.5) * (12.0 ^^ 2) / (11.0 * 10.0)));

v.put(4.0);
assert(v.skewness(true).approxEqual((100.238166 / 13) / pow(57.019231 / 13, 1.5)));
assert(v.skewness(false).approxEqual((100.238166 / 13) / pow(57.019231 / 12, 1.5) * (13.0 ^^ 2) / (12.0 * 11.0)));
alias S = Summator!(T, summation);
this(Range)(Range r)
if (isIterable!Range);
this()(T x);
void put(Range)(Range r)
if (isIterable!Range);
void put()(T x);
void put(U, SkewnessAlgo skewAlgo, Summation sumAlgo)(SkewnessAccumulator!(U, skewAlgo, sumAlgo) v)
if (skewAlgo != SkewnessAlgo.assumeZeroMean);
@property size_t count();
@property F mean(F = T)();
@property F variance(F = T)(bool isPopulation);
F centeredSumOfSquares(F = T)();
F centeredSumOfCubes(F = T)();
F scaledSumOfCubes(F = T)(bool isPopulation);
F skewness(F = T)(bool isPopulation);
struct SkewnessAccumulator(T, SkewnessAlgo skewnessAlgo, Summation summation) if (isMutable!T && (skewnessAlgo == SkewnessAlgo.twoPass));
Examples:
twoPass
import mir.math.common: approxEqual, sqrt;
import mir.math.sum: Summation;
import mir.ndslice.slice: sliced;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

auto v = SkewnessAccumulator!(double, SkewnessAlgo.twoPass, Summation.naive)(x);
assert(v.skewness(true).approxEqual(12.000999 / 12));
assert(v.skewness(false).approxEqual(12.000999 / 12 * sqrt(12.0 * 11.0) / 10.0));
alias S = Summator!(T, summation);
this(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);
this(SliceLike)(SliceLike x)
if (isConvertibleToSlice!SliceLike && !isSlice!SliceLike);
this(Range)(Range range)
if (isInputRange!Range && !isConvertibleToSlice!Range && is(elementType!Range : T));
size_t count()();
F mean(F = T)();
F variance(F = T)(bool isPopulation);
F centeredSumOfSquares(F = T)();
F centeredSumOfCubes(F = T)();
F scaledSumOfCubes(F = T)(bool isPopulation);
F skewness(F = T)(bool isPopulation);
struct SkewnessAccumulator(T, SkewnessAlgo skewnessAlgo, Summation summation) if (isMutable!T && (skewnessAlgo == SkewnessAlgo.threePass));
Examples:
threePass
import mir.math.common: approxEqual, sqrt;
import mir.math.sum: Summation;
import mir.ndslice.slice: sliced;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

auto v = SkewnessAccumulator!(double, SkewnessAlgo.threePass, Summation.naive)(x);
assert(v.skewness(true).approxEqual(12.000999 / 12));
assert(v.skewness(false).approxEqual(12.000999 / 12 * sqrt(12.0 * 11.0) / 10.0));
alias S = Summator!(T, summation);
this(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);
this(SliceLike)(SliceLike x)
if (isConvertibleToSlice!SliceLike && !isSlice!SliceLike);
this(Range)(Range range)
if (isInputRange!Range && !isConvertibleToSlice!Range && is(elementType!Range : T));
size_t count()();
F mean(F = T)();
F variance(F = T)(bool isPopulation);
F centeredSumOfSquares(F = T)();
F centeredSumOfCubes(F = T)();
F scaledSumOfCubes(F = T)();
F skewness(F = T)(bool isPopulation);
struct SkewnessAccumulator(T, SkewnessAlgo skewnessAlgo, Summation summation) if (isMutable!T && (skewnessAlgo == SkewnessAlgo.assumeZeroMean));
Examples:
assumeZeroMean
import mir.math.common: approxEqual, pow;
import mir.math.sum: Summation;
import mir.ndslice.slice: sliced;
import mir.stat.transform: center;

auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
auto x = a.center;

SkewnessAccumulator!(double, SkewnessAlgo.assumeZeroMean, Summation.naive) v;
v.put(x);
assert(v.skewness(true).approxEqual((117.005859 / 12) / pow(54.765625 / 12, 1.5)));
assert(v.skewness(false).approxEqual((117.005859 / 12) / pow(54.765625 / 11, 1.5) * 12.0 ^^ 2 / (11.0 * 10.0)));

v.put(4.0);
assert(v.skewness(true).approxEqual((181.005859 / 13) / pow(70.765625 / 13, 1.5)));
assert(v.skewness(false).approxEqual((181.005859 / 13) / pow(70.765625 / 12, 1.5) * 13.0 ^^ 2 / (12.0 * 11.0)));
alias S = Summator!(T, summation);
this(Range)(Range r)
if (isIterable!Range);
this()(T x);
void put(Range)(Range r)
if (isIterable!Range);
void put()(T x);
void put(U, Summation sumAlgo)(SkewnessAccumulator!(U, skewnessAlgo, sumAlgo) v);
@property size_t count();
@property F mean(F = T)();
@property F variance(F = T)(bool isPopulation);
@property F centeredSumOfCubes(F = T)();
@property F centeredSumOfSquares(F = T)();
@property F scaledSumOfCubes(F = T)(bool isPopulation);
F skewness(F = T)(bool isPopulation);
struct SkewnessAccumulator(T, SkewnessAlgo skewnessAlgo, Summation summation) if (isMutable!T && (skewnessAlgo == SkewnessAlgo.hybrid));
Examples:
hybrid
import mir.math.common: approxEqual, pow;
import mir.math.sum: Summation;
import mir.ndslice.slice: sliced;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

auto v = SkewnessAccumulator!(double, SkewnessAlgo.hybrid, Summation.naive)(x);
assert(v.skewness(true).approxEqual((117.005859 / 12) / pow(54.765625 / 12, 1.5)));
assert(v.skewness(false).approxEqual((117.005859 / 12) / pow(54.765625 / 11, 1.5) * (12.0 ^^ 2) / (11.0 * 10.0)));

v.put(4.0);
assert(v.skewness(true).approxEqual((100.238166 / 13) / pow(57.019231 / 13, 1.5)));
assert(v.skewness(false).approxEqual((100.238166 / 13) / pow(57.019231 / 12, 1.5) * (13.0 ^^ 2) / (12.0 * 11.0)));
alias S = Summator!(T, summation);
this(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);
this(SliceLike)(SliceLike x)
if (isConvertibleToSlice!SliceLike && !isSlice!SliceLike);
this(Range)(Range range)
if (isIterable!Range && !isConvertibleToSlice!Range);
this()(T x);
void put(Range)(Range r)
if (isIterable!Range);
void put()(T x);
void put(U, SkewnessAlgo skewAlgo, Summation sumAlgo)(SkewnessAccumulator!(U, skewAlgo, sumAlgo) v)
if (skewAlgo != SkewnessAlgo.assumeZeroMean);
@property size_t count();
@property F mean(F = T)();
@property F variance(F = T)(bool isPopulation);
F centeredSumOfSquares(F = T)();
F centeredSumOfCubes(F = T)();
F scaledSumOfCubes(F = T)(bool isPopulation);
F skewness(F = T)(bool isPopulation);
template skewness(F, SkewnessAlgo skewnessAlgo = SkewnessAlgo.hybrid, Summation summation = Summation.appropriate)

template skewness(SkewnessAlgo skewnessAlgo = SkewnessAlgo.hybrid, Summation summation = Summation.appropriate)

template skewness(F, string skewnessAlgo, string summation = "appropriate")

template skewness(string skewnessAlgo, string summation = "appropriate")
Calculates the skewness of the input
By default, if F is not floating point type, then the result will have a double type if F is implicitly convertible to a floating point type.
Parameters:
F controls type of output
skewnessAlgo algorithm for calculating skewness (default: SkewnessAlgo.hybrid)
summation algorithm for calculating sums (default: Summation.appropriate)
Returns:
The skewness of the input, must be floating point or complex type
See Also:
Examples:
Simple example
import mir.math.common: approxEqual, pow;
import mir.ndslice.slice: sliced;

assert(skewness([1.0, 2, 3]).approxEqual(0.0));

assert(skewness([1.0, 2, 4]).approxEqual((2.222222 / 3) / pow(4.666667 / 2, 1.5) * (3.0 ^^ 2) / (2.0 * 1.0)));
assert(skewness([1.0, 2, 4], true).approxEqual((2.222222 / 3) / pow(4.666667 / 3, 1.5)));

assert(skewness!float([0, 1, 2, 3, 4, 6].sliced(3, 2)).approxEqual(0.462910));

static assert(is(typeof(skewness!float([1, 2, 3])) == float));
Examples:
Skewness of vector
import mir.math.common: approxEqual, pow;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0];

assert(x.skewness.approxEqual((117.005859 / 12) / pow(54.765625 / 11, 1.5) * (12.0 ^^ 2) / (11.0 * 10.0)));
Examples:
Skewness of matrix
import mir.math.common: approxEqual, pow;
import mir.ndslice.fuse: fuse;

auto x = [
    [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
].fuse;

assert(x.skewness.approxEqual((117.005859 / 12) / pow(54.765625 / 11, 1.5) * (12.0 ^^ 2) / (11.0 * 10.0)));
Examples:
Column skewness of matrix
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual, pow;
import mir.ndslice.fuse: fuse;
import mir.ndslice.topology: alongDim, byDim, map;

auto x = [
    [0.0,  1.0, 1.5, 2.0], 
    [3.5, 4.25, 2.0, 7.5],
    [5.0,  1.0, 1.5, 0.0]
].fuse;
auto result = [-1.090291, 1.732051, 1.732051, 1.229809];

// Use byDim or alongDim with map to compute skewness of row/column.
assert(x.byDim!1.map!skewness.all!approxEqual(result));
assert(x.alongDim!0.map!skewness.all!approxEqual(result));

// FIXME
// Without using map, computes the skewness of the whole slice
// assert(x.byDim!1.skewness == x.sliced.skewness);
// assert(x.alongDim!0.skewness == x.sliced.skewness);
Examples:
Can also set algorithm type
import mir.math.common: approxEqual, pow, sqrt;
import mir.ndslice.slice: sliced;

auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

auto x = a + 100_000_000_000;

// The online algorithm is numerically unstable in this case
auto y = x.skewness!"online";
assert(!y.approxEqual((117.005859 / 12) / pow(54.765625 / 11, 1.5) * (12.0 ^^ 2) / (11.0 * 10.0)));

// The naive algorithm has an assert error in this case because standard
// deviation is calculated naively as zero. The skewness formula would then
// be dividing by zero. 
//auto z0 = x.skewness!(real, "naive");

// However, the two-pass and three-pass algorithms are numerically stable in this case
auto z1 = x.skewness!"twoPass";
assert(z1.approxEqual(12.000999 / 12 * sqrt(12.0 * 11.0) / 10.0));
assert(!z1.approxEqual(y));
auto z2 = x.skewness!"threePass";
assert(z2.approxEqual((12.000999 / 12) * sqrt(12.0 * 11.0) / 10.0));
assert(!z2.approxEqual(y));

// And the assumeZeroMean algorithm provides the incorrect answer, as expected
auto z3 = x.skewness!"assumeZeroMean";
assert(!z3.approxEqual(y));
Examples:
Can also set algorithm or output type
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;
import mir.ndslice.topology: repeat;

//Set population skewness, skewness algorithm, sum algorithm or output type

auto a = [1.0, 1e98, 1, -1e98].sliced;
auto x = a * 10_000;

/++
Due to Floating Point precision, when centering `x`, subtracting the mean 
from the second and fourth numbers has no effect. Further, after centering 
and squaring `x`, the first and third numbers in the slice have precision 
too low to be included in the centered sum of squares. 
+/
assert(x.skewness(false).approxEqual(0.0));
assert(x.skewness(true).approxEqual(0.0));

assert(x.skewness!("online").approxEqual(0.0));
assert(x.skewness!("online", "kbn").approxEqual(0.0));
assert(x.skewness!("online", "kb2").approxEqual(0.0));
assert(x.skewness!("online", "precise").approxEqual(0.0));
assert(x.skewness!(double, "online", "precise").approxEqual(0.0));
assert(x.skewness!(double, "online", "precise")(true).approxEqual(0.0));

auto y = [uint.max - 2, uint.max - 1, uint.max].sliced;
auto z = y.skewness!(ulong, "threePass");
assert(z == 0.0);
static assert(is(typeof(z) == double));
Examples:
For integral slices, can pass output type as template parameter to ensure output type is correct.
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [0, 1, 1, 2, 4, 4,
          2, 7, 5, 1, 2, 0].sliced;

auto y = x.skewness;
assert(y.approxEqual(0.925493));
static assert(is(typeof(y) == double));

assert(x.skewness!float.approxEqual(0.925493));
Examples:
Skewness works for other user-defined types (provided they can be converted to a floating point)
static struct Foo {
    float x;
    alias x this;
}

Foo[] foo = [Foo(1f), Foo(2f), Foo(3f)];
assert(foo.skewness == 0f);
Examples:
Compute skewness along specified dimention of tensors
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;
import mir.ndslice.topology: as, iota, alongDim, map, repeat;

auto x = [
    [0.0, 1, 3],
    [3.0, 4, 5],
    [6.0, 7, 7],
].fuse;

assert(x.skewness.approxEqual(-0.308571));

auto m0 = [0, 0.0, 0.0];
assert(x.alongDim!0.map!skewness.all!approxEqual(m0));
assert(x.alongDim!(-2).map!skewness.all!approxEqual(m0));

auto m1 = [0.935220, 0.0, -1.732051];
assert(x.alongDim!1.map!skewness.all!approxEqual(m1));
assert(x.alongDim!(-1).map!skewness.all!approxEqual(m1));
assert(iota(3, 4, 5, 6).as!double.alongDim!0.map!skewness.all!approxEqual(repeat(0.0, 4, 5, 6)));
Examples:
Arbitrary skewness
assert(skewness(1.0, 2, 3) == 0.0);
assert(skewness!float(1, 2, 3) == 0f);
stdevType!F skewness(Range)(Range r, bool isPopulation = false)
if (isIterable!Range);
Parameters:
Range r range, must be finite iterable
bool isPopulation true if population skewness, false if sample skewness (default)
stdevType!F skewness(scope const F[] ar...);
Parameters:
F[] ar values
enum KurtosisAlgo: int;
Kurtosis algorithms.
online
Similar to Welford's algorithm for updating variance, but adjusted for kurtosis. Can also put another KurtosisAccumulator of the same type, which uses the parallel algorithm from Terriberry that extends the work of Chan et al.
naive
Calculates kurtosis using (E(x^^4) - 4 * E(x) * E(x ^^ 3) + 6 * (E(x) ^^ 2) E(X ^^ 2) + 3 E(x) ^^ 4) / sigma ^ 2 (allowing for adjustments for population/sample kurtosis).
This algorithm can be numerically unstable.
twoPass
Calculates kurtosis by first calculating the mean, then calculating E((x - E(x)) ^^ 4) / (E((x - E(x)) ^^ 2) ^^ 2)
threePass
Calculates kurtosis by first calculating the mean, then the standard deviation, then calculating E(((x - E(x)) / (E((x - E(x)) ^^ 2) ^^ 0.5)) ^^ 4)
assumeZeroMean
Calculates kurtosis assuming the mean of the input is zero.
hybrid
When slices, slice-like objects, or ranges are the inputs, uses the two-pass algorithm. When an individual data-point is added, uses the online algorithm.
struct KurtosisAccumulator(T, KurtosisAlgo kurtosisAlgo, Summation summation) if (isMutable!T && (kurtosisAlgo == KurtosisAlgo.naive));
Examples:
naive
import mir.math.common: pow;
import mir.ndslice.slice: sliced;
import mir.test: shouldApprox;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

KurtosisAccumulator!(double, KurtosisAlgo.naive, Summation.naive) v;
v.put(x);

v.kurtosis(true, true).shouldApprox == (792.784119 / 12) / pow(54.765625 / 12, 2.0);
v.kurtosis(true, false).shouldApprox == (792.784119 / 12) / pow(54.765625 / 12, 2.0) - 3;
v.kurtosis(false, false).shouldApprox == (792.784119 / 12) / pow(54.765625 / 12, 2.0) * (11.0 * 13.0) / (10.0 * 9.0) - 3.0 * (11.0 * 11.0) / (10.0 * 9.0);
v.kurtosis(false, true).shouldApprox == (792.784119 / 12) / pow(54.765625 / 12, 2.0) * (11.0 * 13.0) / (10.0 * 9.0) - 3.0 * (11.0 * 11.0) / (10.0 * 9.0) + 3;

v.skewness(true).shouldApprox == (117.005859 / 12) / pow(54.765625 / 12, 1.5);

v.put(4.0);
v.kurtosis(true, true).shouldApprox == (745.608180 / 13) / pow(57.019231 / 13, 2.0);
v.kurtosis(true, false).shouldApprox == (745.608180 / 13) / pow(57.019231 / 13, 2.0) - 3;
v.kurtosis(false, false).shouldApprox == (745.608180 / 13) / pow(57.019231 / 13, 2.0) * (12.0 * 14.0) / (11.0 * 10.0) - 3.0 * (12.0 * 12.0) / (11.0 * 10.0);
v.kurtosis(false, true).shouldApprox == (745.608180 / 13) / pow(57.019231 / 13, 2.0) * (12.0 * 14.0) / (11.0 * 10.0) - 3.0 * (12.0 * 12.0) / (11.0 * 10.0) + 3;

v.skewness(true).shouldApprox == (100.238166 / 13) / pow(57.019231 / 13, 1.5);
alias S = Summator!(T, summation);
this(Range)(Range r)
if (isIterable!Range);
this()(T x);
void put(Range)(Range r)
if (isIterable!Range);
void put()(T x);
void put(U, Summation sumAlgo)(KurtosisAccumulator!(U, kurtosisAlgo, sumAlgo) v);
size_t count();
F mean(F = T)();
@property F variance(F = T)(bool isPopulation);
F sumOfSquares(F = T)();
F sumOfCubes(F = T)();
F sumOfQuarts(F = T)();
F centeredSumOfSquares(F = T)();
F centeredSumOfCubes(F = T)();
F centeredSumOfQuarts(F = T)();
F scaledSumOfCubes(F = T)(bool isPopulation);
F scaledSumOfQuarts(F = T)(bool isPopulation);
F skewness(F = T)(bool isPopulation);
F kurtosis(F = T)(bool isPopulation, bool isRaw);
struct KurtosisAccumulator(T, KurtosisAlgo kurtosisAlgo, Summation summation) if (isMutable!T && (kurtosisAlgo == KurtosisAlgo.online));
Examples:
online
import mir.math.common: approxEqual, pow;
import mir.ndslice.slice: sliced;
import mir.test: shouldApprox;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

KurtosisAccumulator!(double, KurtosisAlgo.online, Summation.naive) v;
v.put(x);
v.kurtosis(true, true).shouldApprox == (792.784119 / 12) / pow(54.765625 / 12, 2.0);
v.kurtosis(true, false).shouldApprox == (792.784119 / 12) / pow(54.765625 / 12, 2.0) - 3;
v.kurtosis(false, false).shouldApprox == (792.784119 / 12) / pow(54.765625 / 12, 2.0) * (11.0 * 13.0) / (10.0 * 9.0) - 3.0 * (11.0 * 11.0) / (10.0 * 9.0);
v.kurtosis(false, true).shouldApprox == (792.784119 / 12) / pow(54.765625 / 12, 2.0) * (11.0 * 13.0) / (10.0 * 9.0) - 3.0 * (11.0 * 11.0) / (10.0 * 9.0) + 3;

v.put(4.0);
v.kurtosis(true, true).shouldApprox == (745.608180 / 13) / pow(57.019231 / 13, 2.0);
v.kurtosis(true, false).shouldApprox == (745.608180 / 13) / pow(57.019231 / 13, 2.0) - 3;
v.kurtosis(false, false).shouldApprox == (745.608180 / 13) / pow(57.019231 / 13, 2.0) * (12.0 * 14.0) / (11.0 * 10.0) - 3.0 * (12.0 * 12.0) / (11.0 * 10.0);
v.kurtosis(false, true).shouldApprox == (745.608180 / 13) / pow(57.019231 / 13, 2.0) * (12.0 * 14.0) / (11.0 * 10.0) - 3.0 * (12.0 * 12.0) / (11.0 * 10.0) + 3;
alias S = Summator!(T, summation);
this(Range)(Range r)
if (isIterable!Range);
this()(T x);
void put(Range)(Range r)
if (isIterable!Range);
void put()(T x);
void put(U, KurtosisAlgo kurtAlgo, Summation sumAlgo)(KurtosisAccumulator!(U, kurtAlgo, sumAlgo) v);
size_t count();
F centeredSumOfQuarts(F = T)();
F centeredSumOfCubes(F = T)();
F centeredSumOfSquares(F = T)();
F scaledSumOfCubes(F = T)(bool isPopulation);
F scaledSumOfQuarts(F = T)(bool isPopulation);
F mean(F = T)();
F variance(F = T)(bool isPopulation);
F skewness(F = T)(bool isPopulation);
F kurtosis(F = T)(bool isPopulation, bool isRaw);
struct KurtosisAccumulator(T, KurtosisAlgo kurtosisAlgo, Summation summation) if (isMutable!T && (kurtosisAlgo == KurtosisAlgo.twoPass));
Examples:
twoPass
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

auto v = KurtosisAccumulator!(double, KurtosisAlgo.twoPass, Summation.naive)(x);
assert(v.kurtosis(true, true).approxEqual(38.062853 / 12));
assert(v.kurtosis(true, false).approxEqual(38.062853 / 12 - 3.0));
assert(v.kurtosis(false, true).approxEqual(38.062853 / 12 * (11.0 * 13.0) / (10.0 * 9.0) - 3.0 * (11.0 * 11.0) / (10.0 * 9.0)) + 3.0);
assert(v.kurtosis(false, false).approxEqual(38.062853 / 12 * (11.0 * 13.0) / (10.0 * 9.0) - 3.0 * (11.0 * 11.0) / (10.0 * 9.0)));
alias S = Summator!(T, summation);
this(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);
this(SliceLike)(SliceLike x)
if (isConvertibleToSlice!SliceLike && !isSlice!SliceLike);
this(Range)(Range range)
if (isInputRange!Range && !isConvertibleToSlice!Range && is(elementType!Range : T));
size_t count()();
F mean(F = T)();
F variance(F = T)(bool isPopulation);
F centeredSumOfSquares(F = T)();
F centeredSumOfCubes(F = T)();
F centeredSumOfQuarts(F = T)();
F scaledSumOfCubes(F = T)(bool isPopulation);
F scaledSumOfQuarts(F = T)(bool isPopulation);
F skewness(F = T)(bool isPopulation);
F kurtosis(F = T)(bool isPopulation, bool isRaw);
struct KurtosisAccumulator(T, KurtosisAlgo kurtosisAlgo, Summation summation) if (isMutable!T && (kurtosisAlgo == KurtosisAlgo.threePass));
Examples:
threePass
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

auto v = KurtosisAccumulator!(double, KurtosisAlgo.threePass, Summation.naive)(x);
assert(v.kurtosis(true, true).approxEqual(38.062853 / 12));
assert(v.kurtosis(true, false).approxEqual(38.062853 / 12 - 3.0));
assert(v.kurtosis(false, true).approxEqual(38.062853 / 12 * (11.0 * 13.0) / (10.0 * 9.0) - 3.0 * (11.0 * 11.0) / (10.0 * 9.0)) + 3.0);
assert(v.kurtosis(false, false).approxEqual(38.062853 / 12 * (11.0 * 13.0) / (10.0 * 9.0) - 3.0 * (11.0 * 11.0) / (10.0 * 9.0)));
alias S = Summator!(T, summation);
this(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);
this(SliceLike)(SliceLike x)
if (isConvertibleToSlice!SliceLike && !isSlice!SliceLike);
this(Range)(Range range)
if (isInputRange!Range && !isConvertibleToSlice!Range && is(elementType!Range : T));
size_t count()();
F mean(F = T)();
F variance(F = T)(bool isPopulation);
F centeredSumOfSquares(F = T)();
F centeredSumOfCubes(F = T)();
F centeredSumOfQuarts(F = T)();
F scaledSumOfCubes(F = T)();
F scaledSumOfQuarts(F = T)();
F scaledSumOfCubes(F = T)(bool isPopulation);
F scaledSumOfQuarts(F = T)(bool isPopulation);
F skewness(F = T)(bool isPopulation);
F kurtosis(F = T)(bool isPopulation, bool isRaw);
struct KurtosisAccumulator(T, KurtosisAlgo kurtosisAlgo, Summation summation) if (isMutable!T && (kurtosisAlgo == KurtosisAlgo.assumeZeroMean));
Examples:
assumeZeroMean
import mir.math.common: approxEqual, pow;
import mir.ndslice.slice: sliced;
import mir.stat.transform: center;

auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
auto x = a.center;

KurtosisAccumulator!(double, KurtosisAlgo.assumeZeroMean, Summation.naive) v;
v.put(x);
assert(v.kurtosis(true, true).approxEqual((792.784119 / 12) / pow(54.765625 / 12, 2.0)));
assert(v.kurtosis(true, false).approxEqual((792.784119 / 12) / pow(54.765625 / 12, 2.0) - 3.0));
assert(v.kurtosis(false, false).approxEqual(792.784119 / pow(54.765625 / 11, 2.0) * (12.0 * 13.0) / (11.0 * 10.0 * 9.0) - 3.0 * (11.0 * 11.0) / (10.0 * 9.0)));
assert(v.kurtosis(false, true).approxEqual(792.784119 / pow(54.765625 / 11, 2.0) * (12.0 * 13.0) / (11.0 * 10.0 * 9.0) - 3.0 * (11.0 * 11.0) / (10.0 * 9.0) + 3.0));

v.put(4.0);
assert(v.kurtosis(true, true).approxEqual((1048.784119 / 13) / pow(70.765625 / 13, 2.0)));
assert(v.kurtosis(true, false).approxEqual((1048.784119 / 13) / pow(70.765625 / 13, 2.0) - 3.0));
assert(v.kurtosis(false, false).approxEqual(1048.784119 / pow(70.765625 / 12, 2.0) * (13.0 * 14.0) / (12.0 * 11.0 * 10.0) - 3.0 * (12.0 * 12.0) / (11.0 * 10.0)));
assert(v.kurtosis(false, true).approxEqual(1048.784119 / pow(70.765625 / 12, 2.0) * (13.0 * 14.0) / (12.0 * 11.0 * 10.0) - 3.0 * (12.0 * 12.0) / (11.0 * 10.0) + 3.0));
alias S = Summator!(T, summation);
this(Range)(Range r)
if (isIterable!Range);
this()(T x);
void put(Range)(Range r)
if (isIterable!Range);
void put()(T x);
void put(U, Summation sumAlgo)(KurtosisAccumulator!(U, kurtosisAlgo, sumAlgo) v);
@property size_t count();
@property F mean(F = T)();
@property F variance(F = T)(bool isPopulation);
@property F centeredSumOfQuarts(F = T)();
@property F centeredSumOfCubes(F = T)();
@property F centeredSumOfSquares(F = T)();
F scaledSumOfCubes(F = T)(bool isPopulation);
F scaledSumOfQuarts(F = T)(bool isPopulation);
F skewness(F = T)(bool isPopulation);
F kurtosis(F = T)(bool isPopulation, bool isRaw);
struct KurtosisAccumulator(T, KurtosisAlgo kurtosisAlgo, Summation summation) if (isMutable!T && (kurtosisAlgo == KurtosisAlgo.hybrid));
Examples:
hybrid
import mir.math.common: approxEqual, pow;
import mir.ndslice.slice: sliced;
import mir.test: shouldApprox;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

auto v = KurtosisAccumulator!(double, KurtosisAlgo.hybrid, Summation.naive)(x);
v.kurtosis(true, true).shouldApprox == (792.784119 / 12) / pow(54.765625 / 12, 2.0);
v.kurtosis(true, false).shouldApprox == (792.784119 / 12) / pow(54.765625 / 12, 2.0) - 3;
v.kurtosis(false, false).shouldApprox == (792.784119 / 12) / pow(54.765625 / 12, 2.0) * (11.0 * 13.0) / (10.0 * 9.0) - 3.0 * (11.0 * 11.0) / (10.0 * 9.0);
v.kurtosis(false, true).shouldApprox == (792.784119 / 12) / pow(54.765625 / 12, 2.0) * (11.0 * 13.0) / (10.0 * 9.0) - 3.0 * (11.0 * 11.0) / (10.0 * 9.0) + 3;

v.put(4.0);
v.kurtosis(true, true).shouldApprox == (745.608180 / 13) / pow(57.019231 / 13, 2.0);
v.kurtosis(true, false).shouldApprox == (745.608180 / 13) / pow(57.019231 / 13, 2.0) - 3;
v.kurtosis(false, false).shouldApprox == (745.608180 / 13) / pow(57.019231 / 13, 2.0) * (12.0 * 14.0) / (11.0 * 10.0) - 3.0 * (12.0 * 12.0) / (11.0 * 10.0);
v.kurtosis(false, true).shouldApprox == (745.608180 / 13) / pow(57.019231 / 13, 2.0) * (12.0 * 14.0) / (11.0 * 10.0) - 3.0 * (12.0 * 12.0) / (11.0 * 10.0) + 3;
alias S = Summator!(T, summation);
this(Iterator, size_t N, SliceKind kind)(Slice!(Iterator, N, kind) slice);
this(SliceLike)(SliceLike x)
if (isConvertibleToSlice!SliceLike && !isSlice!SliceLike);
this(Range)(Range range)
if (isIterable!Range && !isConvertibleToSlice!Range);
this()(T x);
void put(Range)(Range r)
if (isIterable!Range);
void put()(T x);
void put(U, KurtosisAlgo kurtAlgo, Summation sumAlgo)(KurtosisAccumulator!(U, kurtAlgo, sumAlgo) v);
size_t count();
F centeredSumOfQuarts(F = T)();
F centeredSumOfCubes(F = T)();
F centeredSumOfSquares(F = T)();
F scaledSumOfCubes(F = T)(bool isPopulation);
F scaledSumOfQuarts(F = T)(bool isPopulation);
F mean(F = T)();
F variance(F = T)(bool isPopulation);
F skewness(F = T)(bool isPopulation);
F kurtosis(F = T)(bool isPopulation, bool isRaw);
template kurtosis(F, KurtosisAlgo kurtosisAlgo = KurtosisAlgo.hybrid, Summation summation = Summation.appropriate)

template kurtosis(KurtosisAlgo kurtosisAlgo = KurtosisAlgo.hybrid, Summation summation = Summation.appropriate)

template kurtosis(F, string kurtosisAlgo, string summation = "appropriate")

template kurtosis(string kurtosisAlgo, string summation = "appropriate")
Calculates the kurtosis of the input
By default, if F is not floating point type, then the result will have a double type if F is implicitly convertible to a floating point type.
Parameters:
F controls type of output
kurtosisAlgo algorithm for calculating kurtosis (default: KurtosisAlgo.hybrid)
summation algorithm for calculating sums (default: Summation.appropriate)
Returns:
The kurtosis of the input, must be floating point
See Also:
Examples:
Simple example
import mir.math.common: approxEqual, pow;
import mir.ndslice.slice: sliced;

assert(kurtosis([1.0, 2, 3, 4]).approxEqual(-1.2));

assert(kurtosis([1.0, 2, 4, 5]).approxEqual((34.0 / 4) / pow(10.0 / 4, 2.0) * (3.0 * 5.0) / (2.0 * 1.0) - 3.0 * (3.0 * 3.0) / (2.0 * 1.0)));
// population excess kurtosis
assert(kurtosis([1.0, 2, 4, 5], true).approxEqual((34.0 / 4) / pow(10.0 / 4, 2.0) - 3.0));
// sample raw kurtosis
assert(kurtosis([1.0, 2, 4, 5], false, true).approxEqual((34.0 / 4) / pow(10.0 / 4, 2.0) * (3.0 * 5.0) / (2.0 * 1.0) - 3.0 * (3.0 * 3.0) / (2.0 * 1.0) + 3.0));
// population raw kurtosis
assert(kurtosis([1.0, 2, 4, 5], true, true).approxEqual((34.0 / 4) / pow(10.0 / 4, 2.0)));

assert(kurtosis!float([0, 1, 2, 3, 4, 6].sliced(3, 2)).approxEqual(-0.2999999));

static assert(is(typeof(kurtosis!float([1, 2, 3])) == float));
Examples:
Kurtosis of vector
import mir.math.common: approxEqual, pow;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0];

assert(x.kurtosis.approxEqual((792.784119 / 12) / pow(54.765625 / 12, 2.0) * (11.0 * 13.0) / (10.0 * 9.0) - 3.0 * (11.0 * 11.0) / (10.0 * 9.0)));
Examples:
Kurtosis of matrix
import mir.math.common: approxEqual, pow;
import mir.ndslice.fuse: fuse;

auto x = [
    [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
].fuse;

assert(x.kurtosis.approxEqual((792.784119 / 12) / pow(54.765625 / 12, 2.0) * (11.0 * 13.0) / (10.0 * 9.0) - 3.0 * (11.0 * 11.0) / (10.0 * 9.0)));
Examples:
Column kurtosis of matrix
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual, pow;
import mir.ndslice.fuse: fuse;
import mir.ndslice.topology: alongDim, byDim, map;

auto x = [
    [0.0,  1.0,  1.5, 2.0], 
    [3.5, 4.25,  2.0, 7.5],
    [5.0,  1.0,  1.5, 0.0],
    [1.5,  4.5, 4.75, 0.5]
].fuse;
auto result = [-2.067182, -5.918089, 3.504056, 2.690240];

// Use byDim or alongDim with map to compute kurtosis of row/column.
assert(x.byDim!1.map!kurtosis.all!approxEqual(result));
assert(x.alongDim!0.map!kurtosis.all!approxEqual(result));

// FIXME
// Without using map, computes the kurtosis of the whole slice
// assert(x.byDim!1.kurtosis == x.sliced.kurtosis);
// assert(x.alongDim!0.kurtosis == x.sliced.kurtosis);
Examples:
Can also set algorithm type
import mir.math.common: approxEqual, pow;
import mir.ndslice.slice: sliced;

auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

auto x = a + 100_000_000_000;

// The online algorithm is numerically unstable in this case
auto y = x.kurtosis!"online";
assert(!y.approxEqual((792.78411865 / 12) / pow(54.76562500 / 12, 2.0) * (11.0 * 13.0) / (10.0 * 9.0) - 3.0 * (11.0 * 11.0) / (10.0 * 9.0)));

// The naive algorithm has an assert error in this case because standard
// deviation is calculated naively as zero. The kurtosis formula would then
// be dividing by zero. 
//auto z0 = x.kurtosis!(real, "naive");

// The two-pass algorithm is also numerically unstable in this case
auto z1 = x.kurtosis!"twoPass";
assert(!z1.approxEqual(38.062853 / 12 * (11.0 * 13.0) / (10.0 * 9.0) - 3.0 * (11.0 * 11.0) / (10.0 * 9.0)) + 3.0);
assert(!z1.approxEqual(y));

// However, the three-pass algorithm is numerically stable in this case
auto z2 = x.kurtosis!"threePass";
assert(z2.approxEqual(38.062853 / 12 * (11.0 * 13.0) / (10.0 * 9.0) - 3.0 * (11.0 * 11.0) / (10.0 * 9.0)) + 3.0);
assert(!z2.approxEqual(y));

// And the assumeZeroMean algorithm provides the incorrect answer, as expected
auto z3 = x.kurtosis!"assumeZeroMean";
assert(!z3.approxEqual(y));
Examples:
Can also set algorithm or output type
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;
import mir.ndslice.topology: repeat;

// Set population/sample kurtosis, excess/raw kurtosis, kurtosis algorithm,
// sum algorithm or output type

auto a = [1.0, 1e72, 1, -1e72].sliced;
auto x = a * 10_000;

/++
Due to Floating Point precision, when centering `x`, subtracting the mean 
from the second and fourth numbers has no effect. Further, after centering 
and taking `x` to the fourth power, the first and third numbers in the slice
have precision too low to be included in the centered sum of cubes. 
+/
assert(x.kurtosis.approxEqual(1.5));
assert(x.kurtosis(false).approxEqual(1.5));
assert(x.kurtosis(true).approxEqual(-1.0));
assert(x.kurtosis(true, true).approxEqual(2.0));
assert(x.kurtosis(false, true).approxEqual(4.5));

assert(x.kurtosis!("online").approxEqual(1.5));
assert(x.kurtosis!("online", "kbn").approxEqual(1.5));
assert(x.kurtosis!("online", "kb2").approxEqual(1.5));
assert(x.kurtosis!("online", "precise").approxEqual(1.5));
assert(x.kurtosis!(double, "online", "precise").approxEqual(1.5));
assert(x.kurtosis!(double, "online", "precise")(true).approxEqual(-1.0));
assert(x.kurtosis!(double, "online", "precise")(true, true).approxEqual(2.0));

auto y = [uint.max - 3, uint.max - 2, uint.max - 1, uint.max].sliced;
auto z = y.kurtosis!(ulong, "threePass");
assert(z.approxEqual(-1.2));
static assert(is(typeof(z) == double));
Examples:
For integral slices, can pass output type as template parameter to ensure output type is correct.
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [0, 1, 1, 2, 4, 4,
          2, 7, 5, 1, 2, 0].sliced;

auto y = x.kurtosis;
assert(y.approxEqual(0.223394));
static assert(is(typeof(y) == double));

assert(x.kurtosis!float.approxEqual(0.223394));
Examples:
Kurtosis works for other user-defined types (provided they can be converted to a floating point)
import mir.math.common: approxEqual;

static struct Foo {
    float x;
    alias x this;
}

Foo[] foo = [Foo(1f), Foo(2f), Foo(3f), Foo(4f)];
assert(foo.kurtosis.approxEqual(-1.2f));
Examples:
Compute kurtosis along specified dimention of tensors
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;
import mir.ndslice.topology: as, iota, alongDim, map, repeat;

auto x = [
    [0.0,  1,  3,  5],
    [3.0,  4,  5,  7],
    [6.0,  7, 10, 11],
    [9.0, 12, 15, 12]
].fuse;

assert(x.kurtosis.approxEqual(-0.770040));

auto m0 = [-1.200000, -0.152893, -1.713859, -3.869005];
assert(x.alongDim!0.map!kurtosis.all!approxEqual(m0));
assert(x.alongDim!(-2).map!kurtosis.all!approxEqual(m0));

auto m1 = [-1.699512, 0.342857, -4.339100, 1.500000];
assert(x.alongDim!1.map!kurtosis.all!approxEqual(m1));
assert(x.alongDim!(-1).map!kurtosis.all!approxEqual(m1));

assert(iota(4, 5, 6, 7).as!double.alongDim!0.map!kurtosis.all!approxEqual(repeat(-1.2, 5, 6, 7)));
Examples:
Arbitrary kurtosis
import mir.math.common: approxEqual;

assert(kurtosis(1.0, 2, 3, 4).approxEqual(-1.2));
assert(kurtosis!float(1, 2, 3, 4).approxEqual(-1.2f));
stdevType!F kurtosis(Range)(Range r, bool isPopulation = false, bool isRaw = false)
if (isIterable!Range);
Parameters:
Range r range, must be finite iterable
bool isPopulation true if population kurtosis, false if sample kurtosis (default)
bool isRaw true if raw kurtosis, false if excess kurtosis (default)
stdevType!F kurtosis(scope const F[] ar...);
Parameters:
F[] ar values
struct EntropyAccumulator(T, Summation summation);
Examples:
test basic functionality
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

EntropyAccumulator!(double, Summation.pairwise) x;
x.put([0.1, 0.2, 0.3].sliced);
assert(x.entropy.approxEqual(-0.913338));
x.put(0.4);
assert(x.entropy.approxEqual(-1.279854));
Summator!(T, summation) summator;
const pure nothrow @nogc @property @safe F entropy(F = T)();
void put(Range)(Range r)
if (isIterable!Range);
void put()(T x);
void put(U)(EntropyAccumulator!(U, summation) e);
template entropyType(T)
If T is a floating point type, this is an alias to the unqualified type. If T is not a floating point type, this will alias a double type if T is summable and implicitly convertible to a floating point type.
template entropy(F, Summation summation = Summation.appropriate)
Computes the entropy of the input. By default, if F is not a floating point type, then the result will have a double type if F is implicitly convertible to a floating point type.
Parameters:
F controls type of output
summation algorithm for summing the individual entropy values (default: Summation.appropriate)
Returns:
The entropy of all the elements in the input, must be floating point type
See Also:
entropyType!Range entropy(Range)(Range r)
if (isIterable!Range);
Parameters:
Range r range, must be finite iterable
entropyType!F entropy(scope const F[] ar...);
Parameters:
F[] ar values
template entropy(Summation summation = Summation.appropriate)

template entropy(F, string summation)

template entropy(string summation)
Examples:
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

assert(entropy([0.166667, 0.333333, 0.50]).approxEqual(-1.011404));

assert(entropy!float([0.05, 0.1, 0.15, 0.2, 0.25, 0.25].sliced(3, 2)).approxEqual(-1.679648));

static assert(is(typeof(entropy!float([0.166667, 0.333333, 0.50])) == float));
Examples:
Entropy of vector
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

double[] a = [1.0, 2, 3,  4,  5,  6, 7, 8, 9, 10, 11, 12];
a[] /= 78.0;

auto x = a.sliced;
assert(x.entropy.approxEqual(-2.327497));
Examples:
Entropy of matrix
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;

double[] a = [1.0, 2, 3,  4,  5,  6, 7, 8, 9, 10, 11, 12];
a[] /= 78.0;

auto x = a.fuse;
assert(x.entropy.approxEqual(-2.327497));
Examples:
Column entropy of matrix
import mir.algorithm.iteration: all;
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;
import mir.ndslice.topology: alongDim, byDim, map;

double[][] a = [
    [1.0, 2, 3,  4,  5,  6], 
    [7.0, 8, 9, 10, 11, 12]
];
a[0][] /= 78.0;
a[1][] /= 78.0;

auto x = a.fuse;
auto result = [-0.272209, -0.327503, -0.374483, -0.415678, -0.452350, -0.485273];

// Use byDim or alongDim with map to compute entropy of row/column.
assert(x.byDim!1.map!entropy.all!approxEqual(result));
assert(x.alongDim!0.map!entropy.all!approxEqual(result));

// FIXME
// Without using map, computes the entropy of the whole slice
// assert(x.byDim!1.entropy == x.sliced.entropy);
// assert(x.alongDim!0.entropy == x.sliced.entropy);
Examples:
Can also set algorithm or output type
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;
import mir.ndslice.topology: repeat;

auto a = [1, 1e100, 1, 1e100].sliced;

auto x = a * 10_000;

assert(x.entropy!"kbn".approxEqual(4.789377e106));
assert(x.entropy!"kb2".approxEqual(4.789377e106));
assert(x.entropy!"precise".approxEqual(4.789377e106));
assert(x.entropy!(double, "precise").approxEqual(4.789377e106));
Examples:
For integral slices, pass output type as template parameter to ensure output type is correct.
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [3, 1, 1, 2, 4, 4,
          2, 7, 5, 1, 2, 3].sliced;

auto y = x.entropy;
assert(y.approxEqual(43.509472));
static assert(is(typeof(y) == double));

assert(x.entropy!float.approxEqual(43.509472f));
Examples:
Arbitrary entropy
import mir.math.common: approxEqual;

assert(entropy(0.25, 0.25, 0.25, 0.25).approxEqual(-1.386294));
assert(entropy!float(0.25, 0.25, 0.25, 0.25).approxEqual(-1.386294));
entropyType!Range entropy(Range)(Range r)
if (isIterable!Range);
Parameters:
Range r range, must be finite iterable
entropyType!T entropy(T)(scope const T[] ar...);
Parameters:
T[] ar values
template coefficientOfVariation(F, VarianceAlgo varianceAlgo = VarianceAlgo.hybrid, Summation summation = Summation.appropriate)

template coefficientOfVariation(VarianceAlgo varianceAlgo = VarianceAlgo.hybrid, Summation summation = Summation.appropriate)
Calculates the coefficient of variation of the input.
The coefficient of variation is calculated by dividing either the population or sample (default) standard deviation by the mean of the input. According to wikipedia, "the coefficient of variation should be computed computed for data measured on a ratio scale, that is, scales that have a meaningful zero and hence allow for relative comparison of two measurements." In addition, for "small- and moderately-sized datasets", the coefficient of variation is biased, even when using the sample standard deviation.
By default, if F is not floating point type, then the result will have a double type if F is implicitly convertible to a floating point type.
Parameters:
F controls type of output
varianceAlgo algorithm for calculating variance (default: VarianceAlgo.hybrid)
summation algorithm for calculating sums (default: Summation.appropriate)
Returns:
The coefficient of varition of the input, must be floating point type
stdevType!F coefficientOfVariation(Range)(Range r, bool isPopulation = false)
if (isIterable!Range);
Parameters:
Range r range, must be finite iterable
bool isPopulation true if population variance, false if sample variance (default)
stdevType!F coefficientOfVariation(scope const F[] ar...);
Parameters:
F[] ar values
template coefficientOfVariation(F, string varianceAlgo, string summation = "appropriate")

template coefficientOfVariation(string varianceAlgo, string summation = "appropriate")
Examples:
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

assert(coefficientOfVariation([1.0, 2, 3]).approxEqual(1.0 / 2.0));
assert(coefficientOfVariation([1.0, 2, 3], true).approxEqual(0.816497 / 2.0));

assert(coefficientOfVariation!float([0, 1, 2, 3, 4, 5].sliced(3, 2)).approxEqual(1.870829 / 2.5));

static assert(is(typeof(coefficientOfVariation!float([1, 2, 3])) == float));
Examples:
Coefficient of variation of vector
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

assert(x.coefficientOfVariation.approxEqual(2.231299 / 2.437500));
Examples:
Coefficient of variation of matrix
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;

auto x = [
    [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
].fuse;

assert(x.coefficientOfVariation.approxEqual(2.231299 / 2.437500));
Examples:
Can also set algorithm type
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

auto x = a + 1_000_000_000;

auto y = x.coefficientOfVariation;
assert(y.approxEqual(2.231299 / 1_000_000_002.437500));

// The naive variance algorithm is numerically unstable in this case, but
// the difference is small as coefficientOfVariation is a ratio
auto z0 = x.coefficientOfVariation!"naive";
assert(!z0.approxEqual(y, 0x1p-20f, 0x1p-30f));

// But the two-pass algorithm provides a consistent answer
auto z1 = x.coefficientOfVariation!"twoPass";
assert(z1.approxEqual(y));
Examples:
Can also set algorithm or output type
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

// Set population standard deviation, standardDeviation algorithm, sum algorithm or output type

auto a = [1.0, 1e100, 1, -1e100].sliced;
auto x = a * 10_000;

bool populationTrue = true;

/++
For this case, failing to use a summation algorithm results in an assert
error because the mean is zero due to floating point precision issues.
+/
//assert(x.coefficientOfVariation!("online").approxEqual(8.164966e103 / 0.0));

/++
Due to Floating Point precision, when centering `x`, subtracting the mean 
from the second and fourth numbers has no effect. Further, after centering 
and squaring `x`, the first and third numbers in the slice have precision 
too low to be included in the centered sum of squares. 
+/
assert(x.coefficientOfVariation!("online", "kbn").approxEqual(8.164966e103 / 5000.0));
assert(x.coefficientOfVariation!("online", "kb2").approxEqual(8.164966e103 / 5000.0));
assert(x.coefficientOfVariation!("online", "precise").approxEqual(8.164966e103 / 5000.0));
assert(x.coefficientOfVariation!(double, "online", "precise").approxEqual(8.164966e103 / 5000.0));
assert(x.coefficientOfVariation!(double, "online", "precise")(populationTrue).approxEqual(7.071068e103 / 5000.0));


auto y = [uint.max - 2, uint.max - 1, uint.max].sliced;
auto z = y.coefficientOfVariation!ulong;
assert(z == (1.0 / (cast(double) uint.max - 1)));
static assert(is(typeof(z) == double));
assert(y.coefficientOfVariation!(ulong, "online") == (1.0 / (cast(double) uint.max - 1)));
Examples:
For integral slices, pass output type as template parameter to ensure output type is correct.
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [0, 1, 1, 2, 4, 4,
          2, 7, 5, 1, 2, 0].sliced;

auto y = x.coefficientOfVariation;
assert(y.approxEqual(2.151462f / 2.416667));
static assert(is(typeof(y) == double));

assert(x.coefficientOfVariation!float.approxEqual(2.151462f / 2.416667));
Examples:
coefficientOfVariation works for other user-defined types (provided they can be converted to a floating point)
import mir.math.common: approxEqual;

static struct Foo {
    float x;
    alias x this;
}

Foo[] foo = [Foo(1f), Foo(2f), Foo(3f)];
assert(foo.coefficientOfVariation.approxEqual(1f / 2f));
Examples:
Arbitrary coefficientOfVariation
import mir.math.common: approxEqual;

assert(coefficientOfVariation(1.0, 2, 3).approxEqual(1.0 / 2.0));
assert(coefficientOfVariation!float(1, 2, 3).approxEqual(1f / 2f));
struct MomentAccumulator(T, size_t N, Summation summation) if (N > 0 && isMutable!T);
Examples:
Raw moment
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;
import mir.stat.transform: center;

auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
auto x = a.center;

MomentAccumulator!(double, 2, Summation.naive) v;
v.put(x);

assert(v.moment.approxEqual(54.76562 / 12));

v.put(4.0);
assert(v.moment.approxEqual(70.76562 / 13));
Examples:
Central moment
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;
import mir.stat.transform: center;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

MomentAccumulator!(double, 2, Summation.naive) v;
auto m = mean(x);
v.put(x, m);
assert(v.moment.approxEqual(54.76562 / 12));
Examples:
Standardized moment with scaled calculation
import mir.math.common: approxEqual, sqrt;
import mir.ndslice.slice: sliced;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

auto u = VarianceAccumulator!(double, VarianceAlgo.twoPass, Summation.naive)(x);
MomentAccumulator!(double, 3, Summation.naive) v;
v.put(x, u.mean, u.variance(true).sqrt);
assert(v.moment.approxEqual(12.000999 / 12));
assert(v.count == 12);
Summator!(T, summation) summator;
size_t count;
const pure nothrow @nogc @property @safe F moment(F = T)();
const pure nothrow @nogc @property @safe F sumOfPower(F = T)();
void put(Range)(Range r)
if (isIterable!Range);
void put(Range)(Range r, T m)
if (isIterable!Range);
void put(Range)(Range r, T m, T s)
if (isIterable!Range);
void put()(T x);
void put()(MomentAccumulator!(T, N, summation) m);
this(Range)(Range r)
if (isIterable!Range);
this(Range)(Range r, T m)
if (isIterable!Range);
this(Range)(Range r, T m, T s)
if (isIterable!Range);
this()(T x);
this()(T x, T m);
this()(T x, T m, T s);
template rawMoment(F, size_t N, Summation summation = Summation.appropriate) if (N > 0)

template rawMoment(size_t N, Summation summation = Summation.appropriate) if (N > 0)

template rawMoment(F, size_t N, string summation) if (N > 0)

template rawMoment(size_t N, string summation) if (N > 0)
Calculates the n-th raw moment of the input.
By default, if F is not floating point type or complex type, then the result will have a double type if F is implicitly convertible to a floating point type or a type for which isComplex!F is true.
Parameters:
F controls type of output
N controls n-th raw moment
summation algorithm for calculating sums (default: Summation.appropriate)
Returns:
The n-th raw moment of the input, must be floating point or complex type
Examples:
Basic implementation
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

assert(rawMoment!2([1.0, 2, 3]).approxEqual(14.0 / 3));
assert(rawMoment!3([1.0, 2, 3]).approxEqual(36.0 / 3));

assert(rawMoment!(float, 2)([0, 1, 2, 3, 4, 5].sliced(3, 2)).approxEqual(55f / 6));
static assert(is(typeof(rawMoment!(float, 2)([1, 2, 3])) == float));
Examples:
Raw Moment of vector
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;
import mir.stat.transform: center;

auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;
auto x = a.center;

assert(x.rawMoment!2.approxEqual(54.76562 / 12));
Examples:
Raw Moment of matrix
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;
import mir.stat.transform: center;

auto a = [
    [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
].fuse;
auto x = a.center;

assert(x.rawMoment!2.approxEqual(54.76562 / 12));
Examples:
Can also set algorithm or output type
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;
import mir.ndslice.topology: repeat;
import mir.stat.transform: center;

//Set sum algorithm or output type

auto a = [1.0, 1e100, 1, -1e100].sliced;
auto b = a * 10_000;
auto x = b.center;

/++
Due to Floating Point precision, when centering `x`, subtracting the mean 
from the second and fourth numbers has no effect. Further, after centering 
and squaring `x`, the first and third numbers in the slice have precision 
too low to be included in the centered sum of squares. 
+/
assert(x.rawMoment!2.approxEqual(2.0e208 / 4));

assert(x.rawMoment!(2, "kbn").approxEqual(2.0e208 / 4));
assert(x.rawMoment!(2, "kb2").approxEqual(2.0e208 / 4));
assert(x.rawMoment!(2, "precise").approxEqual(2.0e208 / 4));
assert(x.rawMoment!(double, 2, "precise").approxEqual(2.0e208 / 4));

auto y = uint.max.repeat(3);
auto z = y.rawMoment!(ulong, 2);
assert(z.approxEqual(cast(double) (cast(ulong) uint.max) ^^ 2u));
static assert(is(typeof(z) == double));
Examples:
rawMoment works for complex numbers and other user-defined types (that are either implicitly convertible to floating point or if isComplex is true)
import mir.ndslice.slice: sliced;
import std.complex: Complex;
import std.math.operations: isClose;

auto x = [Complex!double(1, 2), Complex!double(2, 3), Complex!double(3, 4), Complex!double(4, 5)].sliced;
assert(x.rawMoment!2.isClose(Complex!double(-24, 80)/ 4));
Examples:
Arbitrary raw moment
import mir.math.common: approxEqual;

assert(rawMoment!2(1.0, 2, 3).approxEqual(14.0 / 3));
assert(rawMoment!(float, 2)(1, 2, 3).approxEqual(14f / 3));
meanType!F rawMoment(Range)(Range r)
if (isIterable!Range);
Parameters:
Range r range, must be finite iterable
meanType!F rawMoment(scope const F[] ar...);
Parameters:
F[] ar values
template centralMoment(F, size_t N, Summation summation = Summation.appropriate) if (N > 0)

template centralMoment(size_t N, Summation summation = Summation.appropriate) if (N > 0)

template centralMoment(F, size_t N, string summation) if (N > 0)

template centralMoment(size_t N, string summation) if (N > 0)
Calculates the n-th central moment of the input.
By default, if F is not floating point type or complex type, then the result will have a double type if F is implicitly convertible to a floating point type or a type for which isComplex!F is true.
Parameters:
F controls type of output
N controls n-th central moment
summation algorithm for calculating sums (default: Summation.appropriate)
Returns:
The n-th central moment of the input, must be floating point or complex type
Examples:
Basic implementation
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

assert(centralMoment!2([1.0, 2, 3]).approxEqual(2.0 / 3));
assert(centralMoment!3([1.0, 2, 3]).approxEqual(0.0 / 3));

assert(centralMoment!(float, 2)([0, 1, 2, 3, 4, 5].sliced(3, 2)).approxEqual(17.5f / 6));
static assert(is(typeof(centralMoment!(float, 2)([1, 2, 3])) == float));
Examples:
Central Moment of vector
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

assert(x.centralMoment!2.approxEqual(54.76562 / 12));
Examples:
Central Moment of matrix
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;

auto x = [
    [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
].fuse;

assert(x.centralMoment!2.approxEqual(54.76562 / 12));
Examples:
Can also set algorithm or output type
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;
import mir.ndslice.topology: repeat;
import mir.stat.transform: center;

//Set sum algorithm or output type

auto a = [1.0, 1e100, 1, -1e100].sliced;
auto b = a * 10_000;
auto x = b.center;

/++
Due to Floating Point precision, when centering `x`, subtracting the mean 
from the second and fourth numbers has no effect. Further, after centering 
and squaring `x`, the first and third numbers in the slice have precision 
too low to be included in the centered sum of squares. 
+/
assert(x.centralMoment!2.approxEqual(2.0e208 / 4));

assert(x.centralMoment!(2, "kbn").approxEqual(2.0e208 / 4));
assert(x.centralMoment!(2, "kb2").approxEqual(2.0e208 / 4));
assert(x.centralMoment!(2, "precise").approxEqual(2.0e208 / 4));
assert(x.centralMoment!(double, 2, "precise").approxEqual(2.0e208 / 4));

auto y = uint.max.repeat(3);
auto z = y.centralMoment!(ulong, 2);
assert(z.approxEqual(0.0));
static assert(is(typeof(z) == double));
Examples:
centralMoment works for complex numbers and other user-defined types (that are either implicitly convertible to floating point or if isComplex is true)
import mir.ndslice.slice: sliced;
import std.complex: Complex;
import std.math.operations: isClose;

auto x = [Complex!double(1, 2), Complex!double(2, 3), Complex!double(3, 4), Complex!double(4, 5)].sliced;
assert(x.centralMoment!2.isClose(Complex!double(0, 10) / 4));
Examples:
Arbitrary central moment
import mir.math.common: approxEqual;

assert(centralMoment!2(1.0, 2, 3).approxEqual(2.0 / 3));
assert(centralMoment!(float, 2)(1, 2, 3).approxEqual(2f / 3));
meanType!F centralMoment(Range)(Range r)
if (isIterable!Range);
Parameters:
Range r range, must be finite iterable
meanType!F centralMoment(scope const F[] ar...);
Parameters:
F[] ar values
enum StandardizedMomentAlgo: int;
scaled
Calculates n-th standardized moment as E(((x - u) / sigma) ^^ N)
centered
Calculates n-th standardized moment as E(((x - u) ^^ N) / ((x - u) ^^ (N / 2)))
template standardizedMoment(F, size_t N, StandardizedMomentAlgo standardizedMomentAlgo = StandardizedMomentAlgo.scaled, VarianceAlgo varianceAlgo = VarianceAlgo.twoPass, Summation summation = Summation.appropriate) if (N > 0)

template standardizedMoment(size_t N, StandardizedMomentAlgo standardizedMomentAlgo = StandardizedMomentAlgo.scaled, VarianceAlgo varianceAlgo = VarianceAlgo.twoPass, Summation summation = Summation.appropriate) if (N > 0)

template standardizedMoment(F, size_t N, string standardizedMomentAlgo, string varianceAlgo = "twoPass", string summation = "appropriate") if (N > 0)

template standardizedMoment(size_t N, string standardizedMomentAlgo, string varianceAlgo = "twoPass", string summation = "appropriate") if (N > 0)
Calculates the n-th standardized moment of the input.
By default, if F is not floating point type, then the result will have a double type if F is implicitly convertible to a floating point type.
Parameters:
F controls type of output
N controls n-th standardized moment
summation algorithm for calculating sums (default: Summation.appropriate)
Returns:
The n-th standardized moment of the input, must be floating point
Examples:
Basic implementation
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

assert(standardizedMoment!1([1.0, 2, 3]).approxEqual(0.0));
assert(standardizedMoment!2([1.0, 2, 3]).approxEqual(1.0));
assert(standardizedMoment!3([1.0, 2, 3]).approxEqual(0.0 / 3));
assert(standardizedMoment!4([1.0, 2, 3]).approxEqual(4.5 / 3));

assert(standardizedMoment!(float, 2)([0, 1, 2, 3, 4, 5].sliced(3, 2)).approxEqual(6f / 6));
static assert(is(typeof(standardizedMoment!(float, 2)([1, 2, 3])) == float));
Examples:
Standardized Moment of vector
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

assert(x.standardizedMoment!3.approxEqual(12.000999 / 12));
Examples:
Standardized Moment of matrix
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;

auto x = [
    [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
].fuse;

assert(x.standardizedMoment!3.approxEqual(12.000999 / 12));
Examples:
Can also set algorithm type
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto a = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

auto x = a + 100_000_000_000;

// The default algorithm is numerically stable in this case
auto y = x.standardizedMoment!3;
assert(y.approxEqual(12.000999 / 12));

// The online algorithm is numerically unstable in this case
auto z1 = x.standardizedMoment!(3, "scaled", "online");
assert(!z1.approxEqual(12.000999 / 12));
assert(!z1.approxEqual(y));

// It is also numerically unstable when using StandardizedMomentAlgo.centered
auto z2 = x.standardizedMoment!(3, "centered", "online");
assert(!z2.approxEqual(12.000999 / 12));
assert(!z2.approxEqual(y));
Examples:
Can also set algorithm or output type
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

//Set standardized moment algorithm, variance algorithm, sum algorithm, or output type

auto a = [1.0, 1e98, 1, -1e98].sliced;
auto x = a * 10_000;

/++
Due to Floating Point precision, when centering `x`, subtracting the mean 
from the second and fourth numbers has no effect. Further, after centering 
and squaring `x`, the first and third numbers in the slice have precision 
too low to be included in the centered sum of squares. 
+/
assert(x.standardizedMoment!3.approxEqual(0.0));

assert(x.standardizedMoment!(3, "scaled", "online").approxEqual(0.0));
assert(x.standardizedMoment!(3, "centered", "online").approxEqual(0.0));
assert(x.standardizedMoment!(3, "scaled", "online", "kbn").approxEqual(0.0));
assert(x.standardizedMoment!(3, "scaled", "online", "kb2").approxEqual(0.0));
assert(x.standardizedMoment!(3, "scaled", "online", "precise").approxEqual(0.0));
assert(x.standardizedMoment!(double, 3, "scaled", "online", "precise").approxEqual(0.0));

auto y = [uint.max - 2, uint.max - 1, uint.max].sliced;
auto z = y.standardizedMoment!(ulong, 3);
assert(z == 0.0);
static assert(is(typeof(z) == double));
Examples:
For integral slices, can pass output type as template parameter to ensure output type is correct. By default, they get converted to double.
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [0, 1, 1, 2, 4, 4,
          2, 7, 5, 1, 2, 0].sliced;

auto y = x.standardizedMoment!3;
assert(y.approxEqual(9.666455 / 12));
static assert(is(typeof(y) == double));

assert(x.standardizedMoment!(float, 3).approxEqual(9.666455f / 12));
Examples:
Arbitrary standardized moment
import mir.math.common: approxEqual;

assert(standardizedMoment!3(1.0, 2, 3).approxEqual(0.0 / 3));
assert(standardizedMoment!(float, 3)(1, 2, 3).approxEqual(0f / 3));
assert(standardizedMoment!(float, 3, "centered")(1, 2, 3).approxEqual(0f / 3));
stdevType!F standardizedMoment(Range)(Range r)
if (isIterable!Range);
Parameters:
Range r range, must be finite iterable
stdevType!F standardizedMoment(scope const F[] ar...);
Parameters:
F[] ar values
enum MomentAlgo: int;
raw
nth raw moment, E(x ^^ n)
central
nth central moment, E((x - u) ^^ n)
standardized
nth standardized moment, E(((x - u) / sigma) ^^ n)
template moment(F, size_t N, MomentAlgo momentAlgo, Summation summation = Summation.appropriate)

template moment(size_t N, MomentAlgo momentAlgo, Summation summation = Summation.appropriate)

template moment(F, size_t N, string momentAlgo, string summation = "appropriate")

template moment(size_t N, string momentAlgo, string summation = "appropriate")
Calculates the n-th moment of the input.
Parameters:
F controls type of output
N controls n-th standardized moment
momentAlgo type of moment to be calculated
summation algorithm for calculating sums (default: Summation.appropriate)
Returns:
The n-th moment of the input, must be floating point or complex type
Examples:
Basic implementation
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

assert(moment!(1, "raw")([1.0, 2, 3]).approxEqual(6.0 / 3));
assert(moment!(2, "raw")([1.0, 2, 3]).approxEqual(14.0 / 3));
assert(moment!(3, "raw")([1.0, 2, 3]).approxEqual(36.0 / 3));
assert(moment!(4, "raw")([1.0, 2, 3]).approxEqual(98.0 / 3));

assert(moment!(1, "central")([1.0, 2, 3]).approxEqual(0.0 / 3));
assert(moment!(2, "central")([1.0, 2, 3]).approxEqual(2.0 / 3));
assert(moment!(3, "central")([1.0, 2, 3]).approxEqual(0.0 / 3));
assert(moment!(4, "central")([1.0, 2, 3]).approxEqual(2.0 / 3));

assert(moment!(1, "standardized")([1.0, 2, 3]).approxEqual(0.0));
assert(moment!(2, "standardized")([1.0, 2, 3]).approxEqual(1.0));
assert(moment!(3, "standardized")([1.0, 2, 3]).approxEqual(0.0 / 3));
assert(moment!(4, "standardized")([1.0, 2, 3]).approxEqual(4.5 / 3));

assert(moment!(float, 2, "standardized")([0, 1, 2, 3, 4, 5].sliced(3, 2)).approxEqual(6f / 6));
static assert(is(typeof(moment!(float, 2, "standardized")([1, 2, 3])) == float));
Examples:
Standardized Moment of vector
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [0.0, 1.0, 1.5, 2.0, 3.5, 4.25,
          2.0, 7.5, 5.0, 1.0, 1.5, 0.0].sliced;

assert(x.moment!(3, "standardized").approxEqual(12.000999 / 12));
Examples:
Standardized Moment of matrix
import mir.math.common: approxEqual;
import mir.ndslice.fuse: fuse;

auto x = [
    [0.0, 1.0, 1.5, 2.0, 3.5, 4.25],
    [2.0, 7.5, 5.0, 1.0, 1.5, 0.0]
].fuse;

assert(x.moment!(3, "standardized").approxEqual(12.000999 / 12));
Examples:
For integral slices, can pass output type as template parameter to ensure output type is correct. By default, they get converted to double.
import mir.math.common: approxEqual;
import mir.ndslice.slice: sliced;

auto x = [0, 1, 1, 2, 4, 4,
          2, 7, 5, 1, 2, 0].sliced;

auto y = x.moment!(3, "standardized");
assert(y.approxEqual(9.666455 / 12));
static assert(is(typeof(y) == double));

assert(x.moment!(float, 3, "standardized").approxEqual(9.666455f / 12));
Examples:
Arbitrary standardized moment
import mir.math.common: approxEqual;

assert(moment!(3, "standardized")(1.0, 2, 3).approxEqual(0.0 / 3));
assert(moment!(float, 3, "standardized")(1, 2, 3).approxEqual(0f / 3));
meanType!F moment(Range)(Range r)
if (isIterable!Range && (momentAlgo != MomentAlgo.standardized));
Parameters:
Range r range, must be finite iterable
stdevType!F moment(Range)(Range r)
if (isIterable!Range && (momentAlgo == MomentAlgo.standardized));
Parameters:
Range r range, must be finite iterable
meanType!F moment()(scope const F[] ar...)
if (momentAlgo != MomentAlgo.standardized);
Parameters:
F[] ar values
stdevType!F moment()(scope const F[] ar...)
if (momentAlgo == MomentAlgo.standardized);
Parameters:
F[] ar values