import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Set;
import java.util.stream.Collectors;

class AoC_2018_05 extends Solution {
    static final int DAY = 5;
    static final String TITLE = "Advent of Code 2018 Day 5: Alchemical Reduction";

    public static void main(String[] args) {
        System.out.println(TITLE);

        // Tests
        testStringsShouldEliminate();
        testReducePolymer();
        testUniqueCharacters();
        testBestReduction();

        // Solve
        String puzzleInput = loadPuzzleInputLines(DAY).collect(Collectors.joining());
        int partOneResult = solvePartOne(puzzleInput);
        assert partOneResult == 11546;
        System.out.printf("Part one: %d\n", partOneResult);
        int partTwoResult = solvePartTwo(puzzleInput);
        System.out.printf("Part two: %d\n", partTwoResult);
    }

    static int solvePartOne(String puzzleInput) {
        return reducePolymer(puzzleInput).length();
    }

    static int solvePartTwo(String puzzleInput) {
        return bestReduction(puzzleInput).length();
    }

    static String reducePolymer(String polymer) {
        ArrayDeque<String> stack = new ArrayDeque<>(polymer.length());
        for (String c : polymer.split("")) {
            String stackTop = stack.peekLast();
            if (stackTop != null && stringsShouldEliminate(c, stackTop)) {
                stack.removeLast();
            } else {
                stack.add(c);
            }
        }
        StringBuilder builder = new StringBuilder();
        for (String c : stack) {
            builder.append(c);
        }
        return builder.toString();
    }

    static boolean stringsShouldEliminate(String first, String second) {
        return first.equalsIgnoreCase(second) && !first.equals(second);
    }

    static Set<String> uniqueCharacters(String input) {
        return Arrays.stream(input.split(""))
            .map(s -> s.toLowerCase())
            .collect(Collectors.toSet());
    }

    static String bestReduction(String input) {
        Set<String> characters = uniqueCharacters(input);
        return characters.stream()
            .map(character -> input.replaceAll(character, "").replaceAll(character.toUpperCase(), ""))
            .map(AoC_2018_05::reducePolymer)
            .min(Comparator.comparing(String::length))
            .get();
    }

    static void testReducePolymer() {
        String input = "dabAcCaCBAcCcaDA";
        String expected = "dabCBAcaDA";
        String result = reducePolymer(input);
        assert result.equals(expected) : result;
    }

    static void testStringsShouldEliminate() {
        assert stringsShouldEliminate("a", "A");
        assert !stringsShouldEliminate("a", "a");
        assert stringsShouldEliminate("C", "c");
        assert !stringsShouldEliminate("C", "C");
    }

    static void testUniqueCharacters() {
        assert uniqueCharacters("abcABC").equals(Set.of("a", "b", "c"));
        assert uniqueCharacters("A").equals(Set.of("a")); // lowercases
        assert uniqueCharacters("BC").equals(Set.of("b", "c")); // lowercases
    }

    static void testBestReduction() {
        String input = "dabAcCaCBAcCcaDA";
        String expected = "daDA";
        assert bestReduction(input).equals(expected);
    }

}

@@ -133,16 +133,48 @@
 lectors;
+%0Aimport java.util.stream.Stream;
 %0A%0Aclass 
@@ -849,32 +849,70 @@
 o(puzzleInput);%0A
+        assert partTwoResult == 5124;%0A
         System.o
@@ -1913,18 +1913,21 @@
 static S
-e
 t
+ream
 %3CString%3E
@@ -2062,34 +2062,17 @@
    .
-collect(Collectors.toSet()
+distinct(
 );%0A 
@@ -2137,32 +2137,14 @@
     
-Set%3CString%3E characters =
+return
  uni
@@ -2167,44 +2167,8 @@
 put)
-;%0A        return characters.stream()
 %0A   
@@ -2224,25 +2224,17 @@
 cter
-, %22%22).replaceAll(
+ + %22%7C%22 + 
 char
@@ -2939,24 +2939,78 @@
 rs(%22abcABC%22)
+%0A            .collect(Collectors.toSet())%0A            
 .equals(Set.
@@ -3065,16 +3065,70 @@
 ers(%22A%22)
+%0A            .collect(Collectors.toSet())%0A            
 .equals(
@@ -3192,16 +3192,70 @@
 rs(%22BC%22)
+%0A            .collect(Collectors.toSet())%0A            
 .equals(

Return stream from uniqueCharacters