import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {

    public static void main(String[] args) {
        Main app = new Main();
        app.run();
    }

    private void run() {
        System.out.println(compress("tttaaabbbcedg"));
        System.out.println(decompress("t2a12bce"));

        BigInt left = new BigInt("-701");
        BigInt right = new BigInt("-29");
        System.out.println(minus(left, right));
    }

    private class CharacterCount {
        private char character;
        private int count;

        CharacterCount(char character) {
            this(character, 1);
        }

        CharacterCount(char character, int count) {
            this.character = character;
            this.count = count;
        }

        void increment() {
            this.count++;
        }

        void setCount(int count) {
            this.count = count;
        }

        int getCount() {
            return count;
        }

        char getCharacter() {
            return character;
        }

        @Override
        public String toString() {
            return "" + character + (count < 2 ? "" : count);
        }

        String toRawString() {
            return String.join("", Collections.nCopies(this.count, "" + this.character));
        }
    }

    private String compress(String plainText) {
        List<CharacterCount> characters = new ArrayList<>();
        CharacterCount currentCharacter = new CharacterCount(plainText.charAt(0));

        int plainTextLength = plainText.length();

        for (int i = 1; i < plainTextLength; i++) {
            final char character = plainText.charAt(i);

            if (character == currentCharacter.getCharacter()) {
                currentCharacter.increment();
                continue;
            }

            characters.add(currentCharacter);
            currentCharacter = new CharacterCount(character);
        }

        characters.add(currentCharacter);

        return characters.stream().reduce("", (acc, c) -> acc + c, (l, r) -> l + r);
    }

    private String decompress(String encryptText) {
        List<CharacterCount> characters = new ArrayList<>();
        CharacterCount currentCharacter = new CharacterCount(encryptText.charAt(0));
        boolean isDigit = true;

        int encryptTextLength = encryptText.length();

        for (int i = 1; i < encryptTextLength; i++) {
            final char character = encryptText.charAt(i);

            if (Character.isDigit(character)) {
                final int count = Character.getNumericValue(character);

                if (isDigit) {
                    currentCharacter.setCount(count);
                    isDigit = false;
                    continue;
                }

                currentCharacter.setCount(currentCharacter.getCount() * 10 + count);
                continue;
            }

            characters.add(currentCharacter);
            currentCharacter = new CharacterCount(character);
            isDigit = true;
        }

        characters.add(currentCharacter);

        return characters.stream().reduce("", (acc, c) -> acc + c.toRawString(), (l, r) -> l + r);
    }

    private class BigInt {
        private String number;
        private int sign; // 0: positive, 1: negative

        BigInt(String number) {
            final char signCharacter = number.charAt(0);
            if (signCharacter == '+') {
                this.number = number.substring(1);
                this.sign = 0;
            } else if (signCharacter == '-') {
                this.number = number.substring(1);
                this.sign = 1;
            } else {
                this.number = number;
                this.sign = 0;
            }

            this.number = new StringBuilder(this.number).reverse().toString();
        }

        private BigInt(String number, int sign) {
            this.number = number;
            this.sign = sign;
        }

        String getNumber() {
            return new StringBuilder(number).reverse().toString();
        }

        void setNumber(String number) {
            this.number = new StringBuilder(number).reverse().toString();
        }

        int getSign() {
            return sign;
        }

        void setSign(int sign) {
            this.sign = sign;
        }

        @Override
        public String toString() {
            return (sign == 0 ? "" : "-") + getNumber();
        }

        BigInt minus(BigInt right) {
            final int leftSign = this.sign;
            final int rightSign = right.sign;

            if (leftSign == rightSign && this.number.equals(right.number)) {
                return new BigInt("0");
            }

            if (leftSign == 1 && rightSign == 1) {
                final BigInt positiveLeft = new BigInt(this.number, 0);
                final BigInt positiveRight = new BigInt(right.number, 0);
                return positiveRight.minus(positiveLeft);
            }

            if (leftSign == 0 && rightSign == 1) {
                return new BigInt(this.addTwoPositive(right.number), 0);
            } else if (leftSign == 1 && rightSign == 0) {
                return new BigInt(this.addTwoPositive(right.number), 1);
            }

            // Case: Positive Left and Positive Right
            if (this.number.length() < right.number.length()) {
                return new BigInt(right.minusTwoPositive(this.number), 1);
            } else if (this.number.length() > right.number.length()) {
                return new BigInt(this.minusTwoPositive(right.number), 0);
            }

            int comparableResult = this.compareReverseNumber(right.number);
            if (comparableResult == -1) {
                return new BigInt(right.minusTwoPositive(this.number), 1);
            }

            if (comparableResult == 1) {
                return new BigInt(this.minusTwoPositive(right.number), 0);
            }

            return null;
        }

        private String addTwoPositive(String right) {
            String left = this.number;

            final int leftLength = left.length();
            final int rightLength = right.length();
            final int length = Math.max(leftLength, rightLength);

            if (leftLength < rightLength) {
                left = new StringBuilder(left).append(String.join(
                        "", Collections.nCopies(rightLength - leftLength, "0")
                )).toString();
            }

            if (leftLength > rightLength) {
                right = new StringBuilder(right).append(String.join(
                        "", Collections.nCopies(leftLength - rightLength, "0")
                )).toString();
            }

            StringBuilder builder = new StringBuilder();
            int remainder = 0;

            for (int i = 0; i < length; i++) {
                int leftDigit = Character.getNumericValue(left.charAt(i));
                int rightDigit = Character.getNumericValue(right.charAt(i));

                Digit digit = add(leftDigit, rightDigit, remainder);
                builder.append(digit.getDigit());
                remainder = digit.getRemainder();
            }

            if (remainder > 0) {
                builder.append(remainder);
            }

            return builder.toString();
        }

        private String minusTwoPositive(String right) {
            String left = this.number;

            final int length = left.length();

            if (length > right.length()) {
                right = new StringBuilder(right).append(String.join(
                        "", Collections.nCopies(length - right.length(), "0")
                )).toString();
            }

            StringBuilder builder = new StringBuilder();
            int remainder = 0;

            for (int i = 0; i < length; i++) {
                int leftDigit = Character.getNumericValue(left.charAt(i));
                int rightDigit = Character.getNumericValue(right.charAt(i));

                Digit digit = minus(leftDigit, rightDigit, remainder);
                builder.append(digit.getDigit());
                remainder = digit.getRemainder();
            }

            return builder.toString();
        }

        private Digit add(int left, int right, int remainder) {
            int result = left + right + remainder;
            if (result > 10) {
                return new Digit(result - 10, 1);
            }
            return new Digit(result, 0);
        }

        private Digit minus(int left, int right, int remainder) {
            right += remainder;
            if (left < right) {
                return new Digit(left + 10 - right, 1);
            }
            return new Digit(left - right, 0);
        }

        private int compareReverseNumber(String right) {
            int length = this.number.length();
            for (int i = length - 1; i >= 0; i--) {
                char leftChar = this.number.charAt(i);
                char rightChar = right.charAt(i);

                if (leftChar < rightChar) {
                    return -1;
                } else if (leftChar > rightChar) {
                    return 1;
                }
            }

            return 0;
        }
    }

    private class Digit {
        private int digit;
        private int remainder;

        Digit(int digit, int remainder) {
            this.digit = digit;
            this.remainder = remainder;
        }

        int getDigit() {
            return digit;
        }

        void setDigit(int digit) {
            this.digit = digit;
        }

        int getRemainder() {
            return remainder;
        }

        void setRemainder(int remainder) {
            this.remainder = remainder;
        }
    }

    private BigInt minus(BigInt left, BigInt right) {
        return left.minus(right);
    }
}