'I can't think of a condition to increase the decrement in Gradually Decreasing Carousel app

Decrementing Carousel is a container, accepting int elements. DecrementingCarousel has a maximum capacity, specified via the constructor. When created, DecrementingCarousel is in accumulating state: you may add elements via the addElement method and can produce a CarouselRun object via the run method. Once the run method is called, DecrementingCarousel is in running state: it refuses adding more elements.

The CarouselRun allows to iterate over elements of the carousel decrementing them one by one with the next method. The next returns the value of the current element.

The CarouselRun iterates over elements in the order of their insertion. When an element is decreased to zero, the CarouselRun will skip it in further iterations. When there are no more elements available for decrementing, the CarouselRun returns -1.

The CarouselRun also has the isFinished method, which indicates, if the carousel has run out of the lements to decrement.

Specification Details

boolean addElement(int element) - adds an element. If element is negative or zero, do not add the element. If container is full, do not add the element. If the run method was called to create a CarouselRun, do not add the element. If element is added successfully, return true. Return false otherwise. CarouselRun run() - returns a CarouselRun to iterate over the elements. If the run method has already been called earlier, it must return null: DecrementingCarousel may generate only one CarouselRun object.

CarouselRun has two public methods:

int next() - returns the current value of the current element, then decreases the current element by one and switches to the next element in insertion order. Skips zero elements. When there is no more elements to decrease, returns -1. boolean isFinished() - when there is no more elements to decrease, returns true. Otherwise, returns false. The code:

public class DecrementingCarousel {
    private final int capacity;
    static int[] carousel;
    int index;
    boolean isRun;

    {
        index = 0;
        isRun = false;
    }

    public DecrementingCarousel(int capacity) {
        this.capacity = capacity;
        carousel = new int[capacity];
    }

    public boolean addElement(int element){
        if (element > 0 && index < capacity && !isRun) {
            carousel[index++] = element;
            return true;
        }
        return false;
    }

    public CarouselRun run() {
        if (!isRun) {
            isRun = true;
            return new CarouselRun();
        }
        return null;
    }
}

Methods in CarouselRun:

public class CarouselRun {
    protected final int[] array = DecrementingCarousel.carousel.clone();
    protected int position = 0;

    public int next() {
        if (isFinished())
            return -1;
        else {
            while (array[position %= array.length] <= 0) {
                position++;
            }
        }
        return array[position++]--;
    }

    public boolean isFinished() {
        for (int el : array)
            if (el > 0)
                return false;
        return true;
    }
}

So this subclass must decrement elements by gradually increasing decrement. When you need to decrement an element for the first time, decrease it by 1. Next time you need to decrement the same element, decrease it by 2. Next time decrease by 3, then by 4 and so on.

public class GraduallyDecreasingCarousel extends DecrementingCarousel{
    public GraduallyDecreasingCarousel(final int capacity) {
        super(capacity);
    }

    @Override
    public CarouselRun run() {
        if (!isRun) {
            isRun = true;
            return new GraduallyDecreasingCarouselRun();
        }
        return null;
    }
}

public class GraduallyDecreasingCarouselRun extends CarouselRun {
    int decrement = 1;

    @Override
    public int next() {
        int beforeDecreasing;
        if (isFinished())
            return -1;
        else {
            while (array[position %= array.length] <= 0) {
                position++;
                if (position == array.length) {
                    decrement++;
                }
            }
        }
        beforeDecreasing = array[position];
        array[position++] -= decrement;
        return beforeDecreasing;
    }

The main method:

public class Main {
    public static void main(String[] args) {
        DecrementingCarousel carousel = new GraduallyDecreasingCarousel(7);

        carousel.addElement(20);
        carousel.addElement(30);
        carousel.addElement(10);

        CarouselRun run = carousel.run();

        System.out.println(run.isFinished()); //false

        System.out.println(run.next()); //20
        System.out.println(run.next()); //30
        System.out.println(run.next()); //10

        System.out.println(">>>>>>>>>>");

        System.out.println(run.next()); //19
        System.out.println(run.next()); //29
        System.out.println(run.next()); //9

        System.out.println(">>>>>>>>>>");

        System.out.println(run.next()); //17
        System.out.println(run.next()); //27
        System.out.println(run.next()); //7

        System.out.println(">>>>>>>>>>");

        System.out.println(run.next()); //14
        System.out.println(run.next()); //24
        System.out.println(run.next()); //4

        System.out.println(">>>>>>>>>>");

        System.out.println(run.next()); //10
        System.out.println(run.next()); //20

        System.out.println(">>>>>>>>>>");

        System.out.println(run.next()); //5
        System.out.println(run.next()); //15

        System.out.println(">>>>>>>>>>");

        System.out.println(run.next()); //9

        System.out.println(">>>>>>>>>>");

        System.out.println(run.next()); //2

        System.out.println(run.isFinished()); //true
        System.out.println(run.next()); //-1
    }
}

it works fine in main but wont pass the tests IDK how to change next method in GraduallyDecreasingCarouselRun

tried counting next calls and reset the counter when its greater than amount of elements to decrease but when theres one element left its wasnt working correctly



Solution 1:[1]

Wow. Needed a really long time to fully understand the problem. The problem occurs when you fill the capacity of your array completely.

In that case your check for incrementing the value of decrease inside the while loop can fail, because the modulo assignment will "reset" the position to 0 without going inside the loop and therefore skipping the increment of decrease.

The solution to make the code understandable easier would be to decouple the modulo assignment from the while condition. This is slower and uses more code, but makes it easier to understand and also handles position as the "next" position like stated in the specification. Optimizations are possible, but I just post the unoptimized code here:

public int next() {
    int beforeDecreasing;
    if (isFinished())
        return -1;
    else {
        beforeDecreasing = array[position];
        array[position] -= decrement;
        do {
            position++;
            if (position == array.length) {
                decrement++;
                position = 0;
            }
        } while ((array[position] <= 0) && !isFinished());
    }
    return beforeDecreasing;
}

Solution 2:[2]

For a "quick win" I would suggest another solution: change the place and check for the increment of decrease: (only snippet from inside the else branch)

int oldPosition = position;
while (array[position %= array.length] <= 0) {
    position++;
}
if (oldPosition > position) {
    decrement++;
}

Solution 3:[3]

The most complicated question, in this exercise is how to track the index of the carousel. I recommend decoupling index tracking from next() method. Such feature should be based on storing the values of both "old decrement position" and "new decrement position". This way, you can control better, when to decrease "decrement" variable from -1 to -2 (and program knows what to do in case the next value in the array is equal to 0). In the presented solution, index tracking feature is introduced by calculateNewIndexDecrementPosition() method. I also show next() and isFinished() methods to make the whole CarouselRun class implementation clearer.

    private int calculateNewIndexDecrementPosition(int currentIndexDecrementPostion) {
        int newIndexDecrementPosition;

//If no more elements to decrease are present. Meaning the situation where there are only zeroes in the array.
        if (elementsToDecreaseWhileRunning == 0) {
//Basically get out. Could be any other value in return statement.
            return 0;
        }
//If you reached end of the array, go back to the beginning (0 index). THIS IS THE PLACE WHERE YOU DECREASE THE VALUE FROM -1 TO -2, -2 T0 -3 AND SO ON...
        if (karuzel.size() - 1 == currentIndexDecrementPostion) {
            newIndexDecrementPosition = 0;
            this.amountToDecrease -= 1;
        } else {
//otherwise go through the elements in the increasing order
            newIndexDecrementPosition = currentIndexDecrementPostion + 1;
        }
//If the value of the next element in the array also equals zero, call the function again to recalculate (increment) the index.
        if (karuzel.get(newIndexDecrementPosition) == 0) {
            newIndexDecrementPosition = calculateNewIndexDecrementPosition(newIndexDecrementPosition);
        }
        return newIndexDecrementPosition;
    }



       public int next() {
//The value of elementsToDecreaseWhileRunning comes from the addElement(int element) method, which was defined in CarouselRun Class in the following way. Every time the new element has been added to the carousel the method increases ElementsToDecrease while running by one: carouselRun.setElementsToDecreaseWhileRunning(carouselRun.getElementsToDecreaseWhileRunning() + 1).
//The variable "elementsToDecreaseWhileRunning" stores the information about how many positive numbers are still in the array. The total amount of positive numbers in the array, allows to control when to stop the whole process of decrementing.
//If ElementsToDecreaseWhileRunning equals zero, it means that all the elements in the array were brought to value zero.
        if (karuzel.isEmpty() || elementsToDecreaseWhileRunning == 0) {
            return -1;
        }

        int oldValue = karuzel.get(currentIndexDecrementPostion);

//"newValue" initialization;
        int newValue = 0;

        if (typeOfCarousel.equals("decrementing")) {
            newValue = karuzel.get(currentIndexDecrementPostion) - 1;
        } else if (typeOfCarousel.equals("gradually")) {
            newValue = karuzel.get(currentIndexDecrementPostion) + this.amountToDecrease;

//Protects against undesired, negative values in the carousel
            if (newValue < 0) {
                newValue = 0;
            }
        }
        karuzel.set(currentIndexDecrementPostion, newValue);

//The value of elementsToDecreaseWhileRunning comes from the addElement(int element) method, which was defined in CarouselRun Class in the following way. Every time the new element has been added to the carousel, counter ElementsToDecrese increases: carouselRun.setElementsToDecreaseWhileRunning(carouselRun.getElementsToDecreaseWhileRunning() + 1);
//If new value equals zero, the value of "iteration until the end" (elementsToDecreaseWhileRunning) is decreased by one. It means that, there's one less positive element in the array. The total amount of positive numbers in the array, allows to control when to stop the whole process of decrementing.
        if (newValue == 0) {
            elementsToDecreaseWhileRunning--;
        }
 //Index tracking feature
        this.currentIndexDecrementPostion = calculateNewIndexDecrementPosition(this.currentIndexDecrementPostion);

//next() based on the instructions should return value of the current element
        return oldValue;
    }
      public boolean isFinished() {
            return this.elementsToDecreaseWhileRunning <= 0;
        }

I added comments to almost each line of code, because I'm aware that the exercise comes from the Java Training Program, which accepts also beginning programmers. I hope that this way the solution might be understood by everyone.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 cyberbrain
Solution 2 cyberbrain
Solution 3 JacobTheKnitter