public static void then_should_add_each_property_value_together()
            {
                // Arrange
                var colorMoment1 = new ColorMoment { Alpha = 1, Blue = 2, Green = 3, Moment = 4, Red = 5, Weight = 6};
                var colorMoment2 = new ColorMoment { Alpha = 6, Blue = 5, Green = 4, Moment = 3, Red = 2, Weight = 1};

                // Act
                var addedColor = colorMoment1 + colorMoment2;

                // Assert
                Assert.That(addedColor.Alpha, Is.EqualTo(7));
                Assert.That(addedColor.Blue, Is.EqualTo(7));
                Assert.That(addedColor.Green, Is.EqualTo(7));
                Assert.That(addedColor.Moment, Is.EqualTo(7));
                Assert.That(addedColor.Red, Is.EqualTo(7));
                Assert.That(addedColor.Weight, Is.EqualTo(7));
            }
            public static void then_should_add_each_property_value_together()
            {

                // Arrange
                var colorMoment = new ColorMoment { Alpha = 1, Blue = 2, Green = 3, Moment = 4, Red = 5, Weight = 6 };
                var color32 = new Color32(6, 5, 4, 3);

                // Act
                colorMoment.Add(color32);

                // Assert
                Assert.That(colorMoment.Alpha, Is.EqualTo(7));
                Assert.That(colorMoment.Red, Is.EqualTo(10));
                Assert.That(colorMoment.Green, Is.EqualTo(7));
                Assert.That(colorMoment.Blue, Is.EqualTo(5));
                Assert.That(colorMoment.Moment, Is.EqualTo(90f));
                Assert.That(colorMoment.Weight, Is.EqualTo(7));
            }
            public static void then_should_subtract_each_property()
            {

                // Arrange
                var colorMoment1 = new ColorMoment { Alpha = 6, Blue = 6, Green = 6, Moment = 6, Red = 6, Weight = 6 };
                var colorMoment2 = new ColorMoment { Alpha = 6, Blue = 5, Green = 4, Moment = 3, Red = 2, Weight = 1 };

                // Act
                var addedColor = colorMoment1 - colorMoment2;

                // Assert
                Assert.That(addedColor.Alpha, Is.EqualTo(0));
                Assert.That(addedColor.Blue, Is.EqualTo(1));
                Assert.That(addedColor.Green, Is.EqualTo(2));
                Assert.That(addedColor.Moment, Is.EqualTo(3));
                Assert.That(addedColor.Red, Is.EqualTo(4));
                Assert.That(addedColor.Weight, Is.EqualTo(5));
            }
        private static Color32[] BuildLookups(Box[] cubes, ColorMoment[,,,] moments)
        {
            var lookups = new Color32[cubes.Length];

            for (int cubeIndex = 0; cubeIndex < cubes.Length; cubeIndex++)
            {
                ColorMoment volume = Volume(moments, cubes[cubeIndex]);

                if (volume.Weight <= 0)
                {
                    continue;
                }

                lookups[cubeIndex] = new Color32
                {
                    A = (byte)(volume.Alpha / volume.Weight),
                    R = (byte)(volume.Red / volume.Weight),
                    G = (byte)(volume.Green / volume.Weight),
                    B = (byte)(volume.Blue / volume.Weight)
                };
            }

            return(lookups);
        }
        private static CubeCut Maximize(ColorMoment[,,,] moments, Box cube, int direction, byte first, byte last, ColorMoment whole)
        {
            ColorMoment bottom   = Bottom(cube, direction, moments);
            float       result   = 0.0f;
            byte?       cutPoint = null;

            for (byte position = first; position < last; ++position)
            {
                ColorMoment half = bottom + Top(cube, direction, position, moments);
                if (half.Weight == 0)
                {
                    continue;
                }

                long temp = half.WeightedDistance();

                half = whole - half;
                if (half.Weight != 0)
                {
                    temp += half.WeightedDistance();

                    if (temp > result)
                    {
                        result   = temp;
                        cutPoint = position;
                    }
                }
            }

            return(new CubeCut(cutPoint, result));
        }
        private static Color32[] BuildLookups(Box[] cubes, ColorMoment[, , ,] moments)
        {
            Color32[] lookups = new Color32[cubes.Length];

            for (int cubeIndex = 0; cubeIndex < cubes.Length; cubeIndex++)
            {
                ColorMoment volume = Volume(moments, cubes[cubeIndex]);

                if (volume.Weight <= 0)
                {
                    continue;
                }

                Color32 lookup = new Color32
                {
                    A = (byte)(volume.Alpha / volume.Weight),
                    R = (byte)(volume.Red / volume.Weight),
                    G = (byte)(volume.Green / volume.Weight),
                    B = (byte)(volume.Blue / volume.Weight)
                };

                lookups[cubeIndex] = lookup;
            }

            return lookups;
        }
        private static Box[] SplitData(ref int colorCount, ColorMoment[, , ,] moments)
        {
            --colorCount;
            int next = 0;
            float[] volumeVariance = new float[colorCount];
            Box[] cubes = new Box[colorCount];
            cubes[0].AlphaMaximum = MaxSideIndex;
            cubes[0].RedMaximum = MaxSideIndex;
            cubes[0].GreenMaximum = MaxSideIndex;
            cubes[0].BlueMaximum = MaxSideIndex;
            for (int cubeIndex = 1; cubeIndex < colorCount; ++cubeIndex)
            {
                if (Cut(moments, ref cubes[next], ref cubes[cubeIndex]))
                {
                    volumeVariance[next] = cubes[next].Size > 1 ? CalculateVariance(moments, cubes[next]) : 0.0f;
                    volumeVariance[cubeIndex] = cubes[cubeIndex].Size > 1 ? CalculateVariance(moments, cubes[cubeIndex]) : 0.0f;
                }
                else
                {
                    volumeVariance[next] = 0.0f;
                    cubeIndex--;
                }

                next = 0;
                float temp = volumeVariance[0];

                for (int index = 1; index <= cubeIndex; ++index)
                {
                    if (volumeVariance[index] <= temp)
                    {
                        continue;
                    }

                    temp = volumeVariance[index];
                    next = index;
                }

                if (temp > 0.0)
                {
                    continue;
                }

                colorCount = cubeIndex + 1;
                break;
            }

            return cubes.Take(colorCount).ToArray();
        }
        private static ColorMoment Volume(ColorMoment[, , ,] moments, Box cube)
        {
            return (moments[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] -
                    moments[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] -
                    moments[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
                    moments[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] -
                    moments[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] +
                    moments[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
                    moments[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] -
                    moments[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) -

                   (moments[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
                    moments[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
                    moments[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
                    moments[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] -
                    moments[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
                    moments[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
                    moments[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum] -
                    moments[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);
        }
 private static float CalculateVariance(ColorMoment[, , ,] moments, Box cube)
 {
     ColorMoment volume = Volume(moments, cube);
     return volume.Variance();
 }
            public static void then_should_return_amplitude_divided_by_weight_rounded_down()
            {

                // Arrange
                var colorMoment = new ColorMoment { Alpha = 1, Blue = 2, Green = 3, Moment = 4, Red = 5, Weight = 6 };

                // Act
                var amplitude = colorMoment.WeightedDistance();

                // Assert
                Assert.That(amplitude, Is.EqualTo(6));
            }
        private static ColorMoment Bottom(Box cube, int direction, ColorMoment[, , ,] moment)
        {
            switch (direction)
            {
                case Alpha:
                    return (-moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] +
                            moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
                            moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] -
                            moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) -
                           (-moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] +
                            moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
                            moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] -
                            moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);

                case Red:
                    return (-moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
                            moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] +
                            moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] -
                            moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) -
                           (-moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
                            moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum] +
                            moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] -
                            moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);

                case Green:
                    return (-moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
                            moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] +
                            moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] -
                            moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum]) -
                           (-moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
                            moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum] +
                            moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] -
                            moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);

                case Blue:
                    return (-moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] +
                            moment[cube.AlphaMaximum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
                            moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] -
                            moment[cube.AlphaMaximum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]) -
                           (-moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] +
                            moment[cube.AlphaMinimum, cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] +
                            moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] -
                            moment[cube.AlphaMinimum, cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum]);

                default:
                    return new ColorMoment();
            }
        }
        private static void CalculateMoments(ColorMoment[, , ,] moments)
        {
            ColorMoment[,] areaSquared = new ColorMoment[SideSize, SideSize];
            ColorMoment[] area = new ColorMoment[SideSize];
            for (int alphaIndex = 1; alphaIndex < SideSize; alphaIndex++)
            {
                for (int redIndex = 1; redIndex < SideSize; redIndex++)
                {
                    Array.Clear(area, 0, area.Length);
                    for (int greenIndex = 1; greenIndex < SideSize; greenIndex++)
                    {
                        ColorMoment line = new ColorMoment();
                        for (int blueIndex = 1; blueIndex < SideSize; blueIndex++)
                        {
                            line.AddFast(ref moments[alphaIndex, redIndex, greenIndex, blueIndex]);
                            area[blueIndex].AddFast(ref line);
                            areaSquared[greenIndex, blueIndex].AddFast(ref area[blueIndex]);

                            ColorMoment moment = moments[alphaIndex - 1, redIndex, greenIndex, blueIndex];
                            moment.AddFast(ref areaSquared[greenIndex, blueIndex]);
                            moments[alphaIndex, redIndex, greenIndex, blueIndex] = moment;
                        }
                    }
                }
            }
        }
            public static void then_should_add_each_property_value_together()
            {

                // Arrange
                var colorMoment = new ColorMoment { Alpha = 1, Blue = 2, Green = 3, Moment = 4, Red = 5, Weight = 6 };

                // Act
                colorMoment.AddFast(ref colorMoment);

                // Assert
                Assert.That(colorMoment.Alpha, Is.EqualTo(2));
                Assert.That(colorMoment.Red, Is.EqualTo(10));
                Assert.That(colorMoment.Green, Is.EqualTo(6));
                Assert.That(colorMoment.Blue, Is.EqualTo(4));
                Assert.That(colorMoment.Moment, Is.EqualTo(8.0f));
                Assert.That(colorMoment.Weight, Is.EqualTo(12));
            }
            public static void then_should_return_moment_minus_amplitude_divided_by_weight()
            {

                // Arrange
                var colorMoment = new ColorMoment { Alpha = 1, Blue = 2, Green = 3, Moment = 24, Red = 5, Weight = 6 };

                // Act
                var amplitude = colorMoment.Variance();

                // Assert
                Assert.That(amplitude, Is.EqualTo(17.5f));
            }
        private static bool Cut(ColorMoment[,,,] moments, ref Box first, ref Box second)
        {
            int         direction;
            ColorMoment whole    = Volume(moments, first);
            CubeCut     maxAlpha = Maximize(moments, first, Alpha, (byte)(first.AlphaMinimum + 1), first.AlphaMaximum, whole);
            CubeCut     maxRed   = Maximize(moments, first, Red, (byte)(first.RedMinimum + 1), first.RedMaximum, whole);
            CubeCut     maxGreen = Maximize(moments, first, Green, (byte)(first.GreenMinimum + 1), first.GreenMaximum, whole);
            CubeCut     maxBlue  = Maximize(moments, first, Blue, (byte)(first.BlueMinimum + 1), first.BlueMaximum, whole);

            if ((maxAlpha.Value >= maxRed.Value) && (maxAlpha.Value >= maxGreen.Value) && (maxAlpha.Value >= maxBlue.Value))
            {
                direction = Alpha;
                if (maxAlpha.Position == null)
                {
                    return(false);
                }
            }
            else if ((maxRed.Value >= maxAlpha.Value) && (maxRed.Value >= maxGreen.Value) &&
                     (maxRed.Value >= maxBlue.Value))
            {
                direction = Red;
            }
            else
            {
                if ((maxGreen.Value >= maxAlpha.Value) && (maxGreen.Value >= maxRed.Value) &&
                    (maxGreen.Value >= maxBlue.Value))
                {
                    direction = Green;
                }
                else
                {
                    direction = Blue;
                }
            }

            second.AlphaMaximum = first.AlphaMaximum;
            second.RedMaximum   = first.RedMaximum;
            second.GreenMaximum = first.GreenMaximum;
            second.BlueMaximum  = first.BlueMaximum;

            switch (direction)
            {
            case Alpha:
                if (maxAlpha.Position == null)
                {
                    return(false);
                }

                second.AlphaMinimum = first.AlphaMaximum = (byte)maxAlpha.Position;
                second.RedMinimum   = first.RedMinimum;
                second.GreenMinimum = first.GreenMinimum;
                second.BlueMinimum  = first.BlueMinimum;
                break;

            case Red:
                if (maxRed.Position == null)
                {
                    return(false);
                }

                second.RedMinimum   = first.RedMaximum = (byte)maxRed.Position;
                second.AlphaMinimum = first.AlphaMinimum;
                second.GreenMinimum = first.GreenMinimum;
                second.BlueMinimum  = first.BlueMinimum;
                break;

            case Green:
                if (maxGreen.Position == null)
                {
                    return(false);
                }

                second.GreenMinimum = first.GreenMaximum = (byte)maxGreen.Position;
                second.AlphaMinimum = first.AlphaMinimum;
                second.RedMinimum   = first.RedMinimum;
                second.BlueMinimum  = first.BlueMinimum;
                break;

            case Blue:
                if (maxBlue.Position == null)
                {
                    return(false);
                }

                second.BlueMinimum  = first.BlueMaximum = (byte)maxBlue.Position;
                second.AlphaMinimum = first.AlphaMinimum;
                second.RedMinimum   = first.RedMinimum;
                second.GreenMinimum = first.GreenMinimum;
                break;
            }

            first.Size  = (first.AlphaMaximum - first.AlphaMinimum) * (first.RedMaximum - first.RedMinimum) * (first.GreenMaximum - first.GreenMinimum) * (first.BlueMaximum - first.BlueMinimum);
            second.Size = (second.AlphaMaximum - second.AlphaMinimum) * (second.RedMaximum - second.RedMinimum) * (second.GreenMaximum - second.GreenMinimum) * (second.BlueMaximum - second.BlueMinimum);

            return(true);
        }
        private static CubeCut Maximize(ColorMoment[, , ,] moments, Box cube, int direction, byte first, byte last, ColorMoment whole)
        {
            ColorMoment bottom = Bottom(cube, direction, moments);
            float result = 0.0f;
            byte? cutPoint = null;

            for (byte position = first; position < last; ++position)
            {
                ColorMoment half = bottom + Top(cube, direction, position, moments);
                if (half.Weight == 0)
                {
                    continue;
                }

                long temp = half.WeightedDistance();

                half = whole - half;
                if (half.Weight != 0)
                {
                    temp += half.WeightedDistance();

                    if (temp > result)
                    {
                        result = temp;
                        cutPoint = position;
                    }
                }
            }

            return new CubeCut(cutPoint, result);
        }
        private static float CalculateVariance(ColorMoment[,,,] moments, Box cube)
        {
            ColorMoment volume = Volume(moments, cube);

            return(volume.Variance());
        }
        private static bool Cut(ColorMoment[, , ,] moments, ref Box first, ref Box second)
        {
            int direction;
            ColorMoment whole = Volume(moments, first);
            CubeCut maxAlpha = Maximize(moments, first, Alpha, (byte)(first.AlphaMinimum + 1), first.AlphaMaximum, whole);
            CubeCut maxRed = Maximize(moments, first, Red, (byte)(first.RedMinimum + 1), first.RedMaximum, whole);
            CubeCut maxGreen = Maximize(moments, first, Green, (byte)(first.GreenMinimum + 1), first.GreenMaximum, whole);
            CubeCut maxBlue = Maximize(moments, first, Blue, (byte)(first.BlueMinimum + 1), first.BlueMaximum, whole);

            if ((maxAlpha.Value >= maxRed.Value) && (maxAlpha.Value >= maxGreen.Value) && (maxAlpha.Value >= maxBlue.Value))
            {
                direction = Alpha;
                if (maxAlpha.Position == null)
                {
                    return false;
                }
            }
            else if ((maxRed.Value >= maxAlpha.Value) && (maxRed.Value >= maxGreen.Value)
                     && (maxRed.Value >= maxBlue.Value))
            {
                direction = Red;
            }
            else
            {
                if ((maxGreen.Value >= maxAlpha.Value) && (maxGreen.Value >= maxRed.Value)
                    && (maxGreen.Value >= maxBlue.Value))
                {
                    direction = Green;
                }
                else
                {
                    direction = Blue;
                }
            }

            second.AlphaMaximum = first.AlphaMaximum;
            second.RedMaximum = first.RedMaximum;
            second.GreenMaximum = first.GreenMaximum;
            second.BlueMaximum = first.BlueMaximum;

            switch (direction)
            {
                case Alpha:
                    if (maxAlpha.Position == null)
                    {
                        return false;
                    }

                    second.AlphaMinimum = first.AlphaMaximum = (byte)maxAlpha.Position;
                    second.RedMinimum = first.RedMinimum;
                    second.GreenMinimum = first.GreenMinimum;
                    second.BlueMinimum = first.BlueMinimum;
                    break;

                case Red:
                    if (maxRed.Position == null)
                    {
                        return false;
                    }

                    second.RedMinimum = first.RedMaximum = (byte)maxRed.Position;
                    second.AlphaMinimum = first.AlphaMinimum;
                    second.GreenMinimum = first.GreenMinimum;
                    second.BlueMinimum = first.BlueMinimum;
                    break;

                case Green:
                    if (maxGreen.Position == null)
                    {
                        return false;
                    }

                    second.GreenMinimum = first.GreenMaximum = (byte)maxGreen.Position;
                    second.AlphaMinimum = first.AlphaMinimum;
                    second.RedMinimum = first.RedMinimum;
                    second.BlueMinimum = first.BlueMinimum;
                    break;

                case Blue:
                    if (maxBlue.Position == null)
                    {
                        return false;
                    }

                    second.BlueMinimum = first.BlueMaximum = (byte)maxBlue.Position;
                    second.AlphaMinimum = first.AlphaMinimum;
                    second.RedMinimum = first.RedMinimum;
                    second.GreenMinimum = first.GreenMinimum;
                    break;
            }

            first.Size = (first.AlphaMaximum - first.AlphaMinimum) * (first.RedMaximum - first.RedMinimum) * (first.GreenMaximum - first.GreenMinimum) * (first.BlueMaximum - first.BlueMinimum);
            second.Size = (second.AlphaMaximum - second.AlphaMinimum) * (second.RedMaximum - second.RedMinimum) * (second.GreenMaximum - second.GreenMinimum) * (second.BlueMaximum - second.BlueMinimum);

            return true;
        }
 /// <summary>
 /// Adds a color moment to the current instance more quickly.
 /// </summary>
 /// <param name="moment">
 /// The <see cref="ColorMoment"/> to add.
 /// </param>
 public void AddFast(ref ColorMoment moment)
 {
     this.Alpha += moment.Alpha;
     this.Red += moment.Red;
     this.Green += moment.Green;
     this.Blue += moment.Blue;
     this.Weight += moment.Weight;
     this.Moment += moment.Moment;
 }
            public static void then_should_multiply_each_property_value_together()
            {

                // Arrange
                var colorMoment = new ColorMoment { Alpha = 1, Blue = 2, Green = 3, Moment = 4, Red = 5, Weight = 6 };

                // Act
                var amplitude = colorMoment.Amplitude();

                // Assert
                Assert.That(amplitude, Is.EqualTo(39));
            }