public function doIndent($input)
    {
        $subject = $input;

        $output = '';

        $nextLineIndentationLevel = 0;

        do {
            $indentationLevel = $nextLineIndentationLevel;

            $patterns = $this->getTagPatterns();

            $rules = ['NO', 'DECREASE', 'INCREASE', 'DISCARD'];

            $match = [];

            foreach ($patterns as $pattern => $rule) {
                if ($match = preg_match($pattern, $subject, $matches)) {
                    $this->log[] = [
                        'rule' => $rules[$rule],
                        'pattern' => $pattern,
                        'subject' => $subject,
                        'match' => $matches[0],
                    ];

                    $subject = mb_substr($subject, mb_strlen($matches[0]));

                    if ($rule === static::MATCH_DISCARD) {
                        break;
                    }

                    if ($rule === static::MATCH_INDENT_NO) {
                    } else {
                        if ($rule === static::MATCH_INDENT_DECREASE) {
                            $nextLineIndentationLevel--;
                            $indentationLevel--;
                        } else {
                            $nextLineIndentationLevel++;
                        }
                    }

                    if ($indentationLevel < 0) {
                        $indentationLevel = 0;
                    }

                    $output .= str_repeat(
                        $this->options['indentation_character'],
                        $indentationLevel
                    ) . $matches[0] . "\n";

                    break;
                }
            }
        } while ($match);

        $interpretedInput = '';

        foreach ($this->log as $e) {
            $interpretedInput .= $e['match'];
        }

        if ($interpretedInput !== $input) {
            throw new \RuntimeException('Did not reproduce the exact input.');
        }

        $output = preg_replace('/(<(\w+)[^>]*>)\s*(<\/\2>)/', '\\1\\3', $output);

        return $output;
    }