public void TestFirstFitDecreasingHeight()
 {
     var ffdh = new FirstFitDecreasingHeight();
     ffdh.FindSolution(1.0f, this.testInstance);
     CheckSolution(1.0f, ffdh);
 }
        /// <summary>
        /// Finds a solution for the passed packing problem instance data.
        /// </summary>
        /// <param name="stripWidth">
        /// Width of the strip the items have to be packed into.
        /// </param>
        /// <param name="items">
        /// Items that have to be packed.
        /// </param>
        /// <returns>
        /// Maximum strip height that has been used.
        /// </returns>
        public override float FindSolution(float stripWidth, ICollection<PackingItem> items)
        {
            // Check strip width.
            if (!stripWidth.Equals(AssumedStripWidth))
            {
                throw new ArgumentOutOfRangeException(
                    "stripWidth", string.Format("This algorithm assumes a strip width of {0}.", AssumedStripWidth));
            }

            // Construct lists for big and very small items
            var bigItems = new List<PackingItem>();
            var verySmallItems = new List<PackingItem>();

            // Iterate over all items.
            foreach (var item in items)
            {
                // Check their dimensions.
                double width = item.Rectangle.Width;
                double height = item.Rectangle.Height;

                if ((height > AssumedMaximumItemWidthAndHeight) || (width > AssumedMaximumItemWidthAndHeight))
                {
                    throw new ArgumentOutOfRangeException(
                        string.Format(
                            "This algorithm assumes item " + "widths and heights of at most {0}.",
                            AssumedMaximumItemWidthAndHeight));
                }

                if ((width > BigItemWidth) && (height > BigItemHeight))
                {
                    // Item is a big item.
                    bigItems.Add(item);

                    // Rotate it so that its width is not smaller than its height.
                    if (width < height)
                    {
                        item.Rotate();
                    }
                }
                else if (height < width)
                {
                    // Rotate items so that their height is greater than their width as specified by the long version of the paper.
                    item.Rotate();
                }

                // Check if we have a very small item now.
                if (item.Rectangle.Width <= VerySmallItemWidth)
                {
                    verySmallItems.Add(item);
                }
            }

            // Remove all items that have been assigned to other lists.
            items = new List<PackingItem>(items.Except(bigItems));
            items = new List<PackingItem>(items.Except(verySmallItems));

            // Sort big items by decreasing width.
            bigItems = bigItems.OrderByDescending(item => item.Rectangle.Width).ToList();

            // Remember the height of the strip used for the big items only.
            float h1 = 0;

            // Remember the height of the strip the first item of width at most 2/3 is packed (h1' in the paper).
            float h2 = 0;

            // 1. Place all big items
            foreach (var bigItem in bigItems)
            {
                // Construct new level for the current big item.
                var level = this.AddLevel(h1);

                // Place the item.
                bigItem.Rectangle.X = 0;
                bigItem.Rectangle.Y = h1;

                level.AddItem(bigItem);

                h1 += bigItem.Rectangle.Height;

                // Check whether h2 (h1' in the paper) has to be set.
                if ((h2 <= 0f) && (bigItem.Rectangle.Width <= ItemWidthForBeginOfSubstrip))
                {
                    h2 = h1;
                }
            }

            if (h2 <= 0f)
            {
                h2 = h1;
            }

            // 2. Pack very small items into a substrip, if possible.

            // Remember the height of the substrip for step 3.
            float substripHeight = 0;

            if (h2 < h1)
            {
                // Pack items that have widths in (0, 1/6] inside substrip of width 1/3 using FFDH.
                var ffdh = new FirstFitDecreasingHeight();
                substripHeight = ffdh.FindSolution(SubstripWidth, verySmallItems, h1 - h2);

                // Make substrip start at height h1', at the right side of the strip.
                var substripLevels = ffdh.Levels;

                foreach (var substripLevel in substripLevels)
                {
                    // Construct new mainstrip level for the current substrip level.
                    var level = this.AddLevel(h2 + substripLevel.Y);

                    // Iterate over all items of the substrip level.
                    foreach (var substripItem in substripLevel.Items)
                    {
                        // Adjust position.
                        substripItem.Rectangle.X += ItemWidthForBeginOfSubstrip;
                        substripItem.Rectangle.Y += h2;

                        level.AddItem(substripItem);

                        // Remove item because is has been packed with FFDH.
                        verySmallItems.Remove(substripItem);
                    }
                }
            }

            // 3. If all items that have width in (0, 1/6] have now been packed:
            if (verySmallItems.Count == 0)
            {
                // (a) Stack the remaining items up to height h1.

                // Construct new substrip level.
                var currentLevel = this.AddLevel(h2 + substripHeight);

                // Initialize coordinates for the next item to be placed.
                var nextLevelY = currentLevel.Y;
                var nextItemRightBorderX = AssumedStripWidth;

                // Place items in order of increasing width.
                foreach (var currentItem in items.Reverse())
                {
                    // Check whether it overlaps with previously placed items.
                    currentItem.Rectangle.X = nextItemRightBorderX - currentItem.Rectangle.Width;
                    currentItem.Rectangle.Y = currentLevel.Y;

                    var intersects = false;

                    foreach (var existingLevel in this.Levels)
                    {
                        foreach (var placedItem in existingLevel.Items)
                        {
                            intersects |= currentItem.Rectangle.Intersects(placedItem.Rectangle);

                            if (intersects)
                            {
                                break;
                            }
                        }

                        if (intersects)
                        {
                            break;
                        }
                    }

                    if (intersects)
                    {
                        // If yes, construct new substrip level.
                        currentLevel = this.AddLevel(nextLevelY);
                        nextItemRightBorderX = AssumedStripWidth;
                        currentItem.Rectangle.X = nextItemRightBorderX - currentItem.Rectangle.Width;
                        currentItem.Rectangle.Y = currentLevel.Y;
                    }

                    // Check whether it would be placed (partially) above h1.
                    if (currentLevel.Y + currentItem.Rectangle.Height > h1)
                    {
                        // If yes, stop packing and proceed with (b).
                        break;
                    }

                    // Place the item.
                    currentLevel.AddItem(currentItem);

                    // Compute the x-coordinate of the next item.
                    nextItemRightBorderX -= currentItem.Rectangle.Width;

                    // Update the y-coordinate of the next level, if necessary.
                    nextLevelY = Math.Max(nextLevelY, currentLevel.Y + currentItem.Rectangle.Height);

                    // Mark item as packed.
                    items.Remove(currentItem);
                }

                // (b) Place the unpacked items of width in (1/3, 1/2] in two stacks.

                // Get unpacked items of width in (1/3, 1/2].
                var greaterThanOneThird =
                    items.Where(item => item.Rectangle.Width > ItemWidthForTwostrippacking).ToList();

                items = items.Except(greaterThanOneThird).ToList();

                // Initialize coordinates for the next item to be placed.
                var currentLevelLeftY = h1;
                var currentLevelRightY = h1;

                // Pack the items.
                foreach (var item in greaterThanOneThird)
                {
                    // Add new level.
                    currentLevel = this.AddLevel(h1);

                    if (currentLevelLeftY <= currentLevelRightY)
                    {
                        // Add to the left stack.
                        item.Rectangle.X = 0;
                        item.Rectangle.Y = currentLevelLeftY;

                        currentLevel.AddItem(item);

                        // Adjust height of the left stack.
                        currentLevelLeftY += item.Rectangle.Height;
                    }
                    else
                    {
                        // Add to the right stack.
                        item.Rectangle.X = 0.5f;
                        item.Rectangle.Y = currentLevelRightY;

                        currentLevel.AddItem(item);

                        // Adjust height of the left stack.
                        currentLevelRightY += item.Rectangle.Height;
                    }
                }

                // Get the height of the higher stack.
                var finalStackY = Math.Max(currentLevelLeftY, currentLevelRightY);

                // Pack the unpacked items of width in (1/6, 1/3] using FFDH.
                var ffdh = new FirstFitDecreasingHeight();
                ffdh.FindSolution(AssumedStripWidth, items);

                // Adapt the y-coordinates of all items.
                var remainingLevels = ffdh.Levels;

                foreach (var remainingLevel in remainingLevels)
                {
                    // Construct new mainstrip level for the current level.
                    var level = this.AddLevel(remainingLevel.Y + finalStackY);

                    // Iterate over all items.
                    foreach (var remainingItem in remainingLevel.Items)
                    {
                        // Adjust position.
                        remainingItem.Rectangle.Y += finalStackY;

                        // Place the item.
                        level.AddItem(remainingItem);
                    }
                }
            }
            else
            {
                // 4. Else: Place all remaining items above height h1 with FFDH.
                items = items.Union(verySmallItems).ToList();

                var ffdh = new FirstFitDecreasingHeight();
                ffdh.FindSolution(AssumedStripWidth, items);

                // Adapt the y-coordinates of all items.
                var remainingLevels = ffdh.Levels;

                foreach (var remainingLevel in remainingLevels)
                {
                    // Construct new mainstrip level for the current level.
                    var level = this.AddLevel(h1);

                    // Iterate over all items
                    foreach (var remainingItem in remainingLevel.Items)
                    {
                        // Adjust position.
                        remainingItem.Rectangle.Y += h1;

                        // Place the item.
                        level.AddItem(remainingItem);
                    }
                }
            }

            // Finish and return solution.
            return this.ComputeValue();
        }
        public void TestFfdhWithMaximumStripHeight()
        {
            var ffdh = new FirstFitDecreasingHeight();
            const float MaxStripHeight = 1.2f;

            var value = ffdh.FindSolution(1.0f, this.testInstance, MaxStripHeight);

            Assert.True(value <= MaxStripHeight);
        }