import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

/**
 * You are a professional robber planning to rob houses along a street. Each house has a certain amount of money
 * stashed, the only constraint stopping you from robbing each of them is that adjacent houses have security system
 * connected and it will automatically contact the police if two adjacent houses were broken into on the same night.
 * <p>
 * Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount
 * of money you can rob tonight without alerting the police.
 * <p>
 * Tags: Dynamic Programming
 * <p>
 * Similar Problems: (M) Maximum Product Subarray (M) House Robber II (M) Paint House (E) Paint Fence (M) House
 * Robber III
 */
public class HouseRobber {

    private HouseRobber hr;

    /**
     * Recurrence relation: max[n] = max(max[n - 2] + nums[n], max[n - 1])
     * Optimization: use constant variables instead of an array
     */
    public int rob(int[] nums) {
        if (nums == null || nums.length == 0) return 0;
        if (nums.length == 1) return nums[0];
        int preMax = nums[0];
        int max = Math.max(nums[0], nums[1]);
        for (int i = 2; i < nums.length; i++) {
            int temp = preMax;
            preMax = max;
            max = Math.max(temp + nums[i], preMax);
        }
        return max;
    }

    @Before
    public void setUp() {
        hr = new HouseRobber();
    }

    @Test
    public void testInvalidInput() {
        Assert.assertEquals(0, hr.rob(null));
    }

    @Test
    public void testEdgeCases() {
        int[] input = {};
        Assert.assertEquals(0, hr.rob(input));
        input = new int[]{1};
        Assert.assertEquals(1, hr.rob(input));
        // 10 -> 10
        input = new int[]{10};
        Assert.assertEquals(10, hr.rob(input));
        // 1, 2 -> 2
        input = new int[]{1, 2};
        Assert.assertEquals(2, hr.rob(input));
        // 2, 1 -> 2
        input = new int[]{2, 1};
        Assert.assertEquals(2, hr.rob(input));
    }

    @Test
    public void testWithExamples() {
        // 1, 2, 3 -> 4
        int[] input = {1, 2, 3};
        Assert.assertEquals(4, hr.rob(input));
        // 1, 2, 3, 4 -> 6
        input = new int[]{1, 2, 3, 4};
        Assert.assertEquals(6, hr.rob(input));
        // 2, 8, 3, 6, 5 -> 14
        input = new int[]{2, 8, 3, 6, 5};
        Assert.assertEquals(14, hr.rob(input));
    }

    @After
    public void tearDown() {
        hr = null;
    }
}

add house robber