/// <summary>
        ///	Finds the optimal (maximal) position for the cut.
        /// </summary>
        private Single Maximize(WuColorCube cube, Int32 direction, Int32 first, Int32 last, IList<Int32> cut, Int64 wholeRed, Int64 wholeGreen, Int64 wholeBlue, Int64 wholeWeight)
        {
            Int64 bottomRed = Bottom(cube, direction, momentsRed);
            Int64 bottomGreen = Bottom(cube, direction, momentsGreen);
            Int64 bottomBlue = Bottom(cube, direction, momentsBlue);
            Int64 bottomWeight = Bottom(cube, direction, weights);

            Single result = 0.0f;
            cut[0] = -1;

            for (Int32 position = first; position < last; ++position)
            {
                // determines the cube cut at a certain position
                Int64 halfRed = bottomRed + Top(cube, direction, position, momentsRed);
                Int64 halfGreen = bottomGreen + Top(cube, direction, position, momentsGreen);
                Int64 halfBlue = bottomBlue + Top(cube, direction, position, momentsBlue);
                Int64 halfWeight = bottomWeight + Top(cube, direction, position, weights);

                // the cube cannot be cut at bottom (this would lead to empty cube)
                if (halfWeight != 0)
                {
                    Single halfDistance = halfRed*halfRed + halfGreen*halfGreen + halfBlue*halfBlue;
                    Single temp = halfDistance/halfWeight;

                    halfRed = wholeRed - halfRed;
                    halfGreen = wholeGreen - halfGreen;
                    halfBlue = wholeBlue - halfBlue;
                    halfWeight = wholeWeight - halfWeight;

                    if (halfWeight != 0)
                    {
                        halfDistance = halfRed * halfRed + halfGreen * halfGreen + halfBlue * halfBlue;
                        temp += halfDistance / halfWeight;

                        if (temp > result)
                        {
                            result = temp;
                            cut[0] = position;
                        }
                    }
                }
            }

            return result;
        }
        /// <summary>
        /// Calculates statistical variance for a given cube.
        /// </summary>
        private Single CalculateVariance(WuColorCube cube)
        {
            Single volumeRed = Volume(cube, momentsRed);
            Single volumeGreen = Volume(cube, momentsGreen);
            Single volumeBlue = Volume(cube, momentsBlue);
            Single volumeMoment = VolumeFloat(cube, moments);
            Single volumeWeight = Volume(cube, weights);

            Single distance = volumeRed*volumeRed + volumeGreen*volumeGreen + volumeBlue*volumeBlue;

            return volumeMoment - (distance/volumeWeight);
        }
        /// <summary>
        /// Cuts a cube with another one.
        /// </summary>
        private Boolean Cut(WuColorCube first, WuColorCube second)
        {
            Int32 direction;

            Int32[] cutRed = { 0 };
            Int32[] cutGreen = { 0 };
            Int32[] cutBlue = { 0 };

            Int64 wholeRed = Volume(first, momentsRed);
            Int64 wholeGreen = Volume(first, momentsGreen);
            Int64 wholeBlue = Volume(first, momentsBlue);
            Int64 wholeWeight = Volume(first, weights);

            Single maxRed = Maximize(first, Red, first.RedMinimum + 1, first.RedMaximum, cutRed, wholeRed, wholeGreen, wholeBlue, wholeWeight);
            Single maxGreen = Maximize(first, Green, first.GreenMinimum + 1, first.GreenMaximum, cutGreen, wholeRed, wholeGreen, wholeBlue, wholeWeight);
            Single maxBlue = Maximize(first, Blue, first.BlueMinimum + 1, first.BlueMaximum, cutBlue, wholeRed, wholeGreen, wholeBlue, wholeWeight);

            if ((maxRed >= maxGreen) && (maxRed >= maxBlue))
            {
                direction = Red;

                // cannot split empty cube
                if (cutRed[0] < 0) return false;
            }
            else
            {
                if ((maxGreen >= maxRed) && (maxGreen >= maxBlue))
                {
                    direction = Green;
                }
                else
                {
                    direction = Blue;
                }
            }

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

            // cuts in a certain direction
            switch (direction)
            {
                case Red:
                    second.RedMinimum = first.RedMaximum = cutRed[0];
                    second.GreenMinimum = first.GreenMinimum;
                    second.BlueMinimum = first.BlueMinimum;
                    break;

                case Green:
                    second.GreenMinimum = first.GreenMaximum = cutGreen[0];
                    second.RedMinimum = first.RedMinimum;
                    second.BlueMinimum = first.BlueMinimum;
                    break;

                case Blue:
                    second.BlueMinimum = first.BlueMaximum = cutBlue[0];
                    second.RedMinimum = first.RedMinimum;
                    second.GreenMinimum = first.GreenMinimum;
                    break;
            }

            // determines the volumes after cut
            first.Volume = (first.RedMaximum - first.RedMinimum)*(first.GreenMaximum - first.GreenMinimum)*(first.BlueMaximum - first.BlueMinimum);
            second.Volume = (second.RedMaximum - second.RedMinimum)*(second.GreenMaximum - second.GreenMinimum)*(second.BlueMaximum - second.BlueMinimum);

            // the cut was successfull
            return true;
        }
 /// <summary>
 /// Computes the volume of the cube in a specific moment. For the floating-point values.
 /// </summary>
 private static Single VolumeFloat(WuColorCube cube, Single[,,] moment)
 {
     return moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] -
            moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
            moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
            moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] -
            moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
            moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
            moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] -
            moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum];
 }
 /// <summary>
 /// Computes the volume of the cube in a specific moment.
 /// </summary>
 private static Int64 Volume(WuColorCube cube, Int64[,,] moment)
 {
     return moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMaximum] -
            moment[cube.RedMaximum, cube.GreenMaximum, cube.BlueMinimum] -
            moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMaximum] +
            moment[cube.RedMaximum, cube.GreenMinimum, cube.BlueMinimum] -
            moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMaximum] +
            moment[cube.RedMinimum, cube.GreenMaximum, cube.BlueMinimum] +
            moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMaximum] -
            moment[cube.RedMinimum, cube.GreenMinimum, cube.BlueMinimum];
 }
        /// <summary>
        /// Splits the cube in given position, and color direction.
        /// </summary>
        private static Int64 Top(WuColorCube cube, Int32 direction, Int32 position, Int64[,,] moment)
        {
            switch (direction)
            {
                case Red:
                    return (moment[position, cube.GreenMaximum, cube.BlueMaximum] -
                            moment[position, cube.GreenMaximum, cube.BlueMinimum] -
                            moment[position, cube.GreenMinimum, cube.BlueMaximum] +
                            moment[position, cube.GreenMinimum, cube.BlueMinimum]);

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

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

                default:
                    return 0;
            }
        }
 /// <summary>
 /// Marks all the tags with a given label.
 /// </summary>
 private static void Mark(WuColorCube cube, Int32 label, IList<Int32> tag)
 {
     for (Int32 redIndex = cube.RedMinimum + 1; redIndex <= cube.RedMaximum; ++redIndex)
     {
         for (Int32 greenIndex = cube.GreenMinimum + 1; greenIndex <= cube.GreenMaximum; ++greenIndex)
         {
             for (Int32 blueIndex = cube.BlueMinimum + 1; blueIndex <= cube.BlueMaximum; ++blueIndex)
             {
                 tag[(redIndex << 10) + (redIndex << 6) + redIndex + (greenIndex << 5) + greenIndex + blueIndex] = label;
             }
         }
     }
 }
        /// <summary>
        /// See <see cref="BaseColorQuantizer.OnPrepare"/> for more details.
        /// </summary>
        protected override void OnPrepare(ImageBuffer image)
        {
            // creates all the cubes
            cubes = new WuColorCube[MaxColor];

            // initializes all the cubes
            for (Int32 cubeIndex = 0; cubeIndex < MaxColor; cubeIndex++)
            {
                cubes[cubeIndex] = new WuColorCube();
            }

            // resets the reference minimums
            cubes[0].RedMinimum = 0;
            cubes[0].GreenMinimum = 0;
            cubes[0].BlueMinimum = 0;

            // resets the reference maximums
            cubes[0].RedMaximum = MaxSideIndex;
            cubes[0].GreenMaximum = MaxSideIndex;
            cubes[0].BlueMaximum = MaxSideIndex;

            weights = new Int64[SideSize, SideSize, SideSize];
            momentsRed = new Int64[SideSize, SideSize, SideSize];
            momentsGreen = new Int64[SideSize, SideSize, SideSize];
            momentsBlue = new Int64[SideSize, SideSize, SideSize];
            moments = new Single[SideSize, SideSize, SideSize];

            table = new Int32[256];

            for (Int32 tableIndex = 0; tableIndex < 256; ++tableIndex)
            {
                table[tableIndex] = tableIndex * tableIndex;
            }

            pixelIndex = 0;
            imageWidth = image.Width;
            imageSize = image.Width * image.Height;

            quantizedPixels = new Int32[imageSize];
            pixels = new Int32[imageSize];
        }