/*
 * Decompiled with CFR 0.152.
 */
package com.google.common.base;

import com.google.common.annotations.Beta;
import com.google.common.annotations.GwtCompatible;
import com.google.common.annotations.GwtIncompatible;
import com.google.common.base.Platform;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.base.SmallCharMatcher;
import java.util.Arrays;
import java.util.BitSet;
import javax.annotation.CheckReturnValue;

@Beta
@GwtCompatible(emulated=true)
public abstract class CharMatcher
implements Predicate<Character> {
    public static final CharMatcher BREAKING_WHITESPACE = new CharMatcher(){

        @Override
        public boolean matches(char c) {
            switch (c) {
                case '\t': 
                case '\n': 
                case '\u000b': 
                case '\f': 
                case '\r': 
                case ' ': 
                case '\u0085': 
                case '\u1680': 
                case '\u2028': 
                case '\u2029': 
                case '\u205f': 
                case '\u3000': {
                    return true;
                }
                case '\u2007': {
                    return false;
                }
            }
            return c >= '\u2000' && c <= '\u200a';
        }

        @Override
        public String toString() {
            return "CharMatcher.BREAKING_WHITESPACE";
        }
    };
    public static final CharMatcher ASCII = CharMatcher.inRange('\u0000', '\u007f', "CharMatcher.ASCII");
    private static final String ZEROES = "0\u0660\u06f0\u07c0\u0966\u09e6\u0a66\u0ae6\u0b66\u0be6\u0c66\u0ce6\u0d66\u0e50\u0ed0\u0f20\u1040\u1090\u17e0\u1810\u1946\u19d0\u1b50\u1bb0\u1c40\u1c50\ua620\ua8d0\ua900\uaa50\uff10";
    private static final String NINES;
    public static final CharMatcher DIGIT;
    public static final CharMatcher JAVA_DIGIT;
    public static final CharMatcher JAVA_LETTER;
    public static final CharMatcher JAVA_LETTER_OR_DIGIT;
    public static final CharMatcher JAVA_UPPER_CASE;
    public static final CharMatcher JAVA_LOWER_CASE;
    public static final CharMatcher JAVA_ISO_CONTROL;
    public static final CharMatcher INVISIBLE;
    public static final CharMatcher SINGLE_WIDTH;
    public static final CharMatcher ANY;
    public static final CharMatcher NONE;
    final String description;
    private static final int DISTINCT_CHARS = 65536;
    static final String WHITESPACE_TABLE = "\u2002\u3000\r\u0085\u200a\u2005\u2000\u3000\u2029\u000b\u3000\u2008\u2003\u205f\u3000\u1680\t \u2006\u2001\u202f\u00a0\f\u2009\u3000\u2004\u3000\u3000\u2028\n\u2007\u3000";
    static final int WHITESPACE_MULTIPLIER = 1682554634;
    static final int WHITESPACE_SHIFT;
    public static final CharMatcher WHITESPACE;

    private static String showCharacter(char c) {
        String hex = "0123456789ABCDEF";
        char[] tmp = new char[]{'\\', 'u', '\u0000', '\u0000', '\u0000', '\u0000'};
        for (int i = 0; i < 4; ++i) {
            tmp[5 - i] = hex.charAt(c & 0xF);
            c = (char)(c >> 4);
        }
        return String.copyValueOf(tmp);
    }

    public static CharMatcher is(final char match) {
        String string = String.valueOf(String.valueOf(CharMatcher.showCharacter(match)));
        String description = new StringBuilder(18 + string.length()).append("CharMatcher.is('").append(string).append("')").toString();
        return new FastMatcher(description){

            @Override
            public boolean matches(char c) {
                return c == match;
            }

            @Override
            public String replaceFrom(CharSequence sequence, char replacement) {
                return sequence.toString().replace(match, replacement);
            }

            @Override
            public CharMatcher and(CharMatcher other) {
                return other.matches(match) ? this : NONE;
            }

            @Override
            public CharMatcher or(CharMatcher other) {
                return other.matches(match) ? other : super.or(other);
            }

            @Override
            public CharMatcher negate() {
                return 9.isNot(match);
            }

            @Override
            @GwtIncompatible(value="java.util.BitSet")
            void setBits(BitSet table) {
                table.set(match);
            }
        };
    }

    public static CharMatcher isNot(final char match) {
        String string = String.valueOf(String.valueOf(CharMatcher.showCharacter(match)));
        String description = new StringBuilder(21 + string.length()).append("CharMatcher.isNot('").append(string).append("')").toString();
        return new FastMatcher(description){

            @Override
            public boolean matches(char c) {
                return c != match;
            }

            @Override
            public CharMatcher and(CharMatcher other) {
                return other.matches(match) ? super.and(other) : other;
            }

            @Override
            public CharMatcher or(CharMatcher other) {
                return other.matches(match) ? ANY : this;
            }

            @Override
            @GwtIncompatible(value="java.util.BitSet")
            void setBits(BitSet table) {
                table.set(0, match);
                table.set(match + '\u0001', 65536);
            }

            @Override
            public CharMatcher negate() {
                return 10.is(match);
            }
        };
    }

    public static CharMatcher anyOf(CharSequence sequence) {
        switch (sequence.length()) {
            case 0: {
                return NONE;
            }
            case 1: {
                return CharMatcher.is(sequence.charAt(0));
            }
            case 2: {
                return CharMatcher.isEither(sequence.charAt(0), sequence.charAt(1));
            }
        }
        final char[] chars = sequence.toString().toCharArray();
        Arrays.sort(chars);
        StringBuilder description = new StringBuilder("CharMatcher.anyOf(\"");
        for (char c : chars) {
            description.append(CharMatcher.showCharacter(c));
        }
        description.append("\")");
        return new CharMatcher(description.toString()){

            @Override
            public boolean matches(char c) {
                return Arrays.binarySearch(chars, c) >= 0;
            }

            @Override
            @GwtIncompatible(value="java.util.BitSet")
            void setBits(BitSet table) {
                for (char c : chars) {
                    table.set(c);
                }
            }
        };
    }

    private static CharMatcher isEither(final char match1, final char match2) {
        String string = String.valueOf(String.valueOf(CharMatcher.showCharacter(match1)));
        String string2 = String.valueOf(String.valueOf(CharMatcher.showCharacter(match2)));
        String description = new StringBuilder(21 + string.length() + string2.length()).append("CharMatcher.anyOf(\"").append(string).append(string2).append("\")").toString();
        return new FastMatcher(description){

            @Override
            public boolean matches(char c) {
                return c == match1 || c == match2;
            }

            @Override
            @GwtIncompatible(value="java.util.BitSet")
            void setBits(BitSet table) {
                table.set(match1);
                table.set(match2);
            }
        };
    }

    public static CharMatcher noneOf(CharSequence sequence) {
        return CharMatcher.anyOf(sequence).negate();
    }

    public static CharMatcher inRange(char startInclusive, char endInclusive) {
        Preconditions.checkArgument(endInclusive >= startInclusive);
        String string = String.valueOf(String.valueOf(CharMatcher.showCharacter(startInclusive)));
        String string2 = String.valueOf(String.valueOf(CharMatcher.showCharacter(endInclusive)));
        String description = new StringBuilder(27 + string.length() + string2.length()).append("CharMatcher.inRange('").append(string).append("', '").append(string2).append("')").toString();
        return CharMatcher.inRange(startInclusive, endInclusive, description);
    }

    static CharMatcher inRange(final char startInclusive, final char endInclusive, String description) {
        return new FastMatcher(description){

            @Override
            public boolean matches(char c) {
                return startInclusive <= c && c <= endInclusive;
            }

            @Override
            @GwtIncompatible(value="java.util.BitSet")
            void setBits(BitSet table) {
                table.set((int)startInclusive, endInclusive + '\u0001');
            }
        };
    }

    public static CharMatcher forPredicate(final Predicate<? super Character> predicate) {
        Preconditions.checkNotNull(predicate);
        if (predicate instanceof CharMatcher) {
            return (CharMatcher)predicate;
        }
        String string = String.valueOf(String.valueOf(predicate));
        String description = new StringBuilder(26 + string.length()).append("CharMatcher.forPredicate(").append(string).append(")").toString();
        return new CharMatcher(description){

            @Override
            public boolean matches(char c) {
                return predicate.apply(Character.valueOf(c));
            }

            @Override
            public boolean apply(Character character) {
                return predicate.apply(Preconditions.checkNotNull(character));
            }
        };
    }

    CharMatcher(String description) {
        this.description = description;
    }

    protected CharMatcher() {
        this.description = super.toString();
    }

    public abstract boolean matches(char var1);

    public CharMatcher negate() {
        return new NegatedMatcher(this);
    }

    public CharMatcher and(CharMatcher other) {
        return new And(this, Preconditions.checkNotNull(other));
    }

    public CharMatcher or(CharMatcher other) {
        return new Or(this, Preconditions.checkNotNull(other));
    }

    public CharMatcher precomputed() {
        return Platform.precomputeCharMatcher(this);
    }

    CharMatcher withToString(String description) {
        throw new UnsupportedOperationException();
    }

    @GwtIncompatible(value="java.util.BitSet")
    CharMatcher precomputedInternal() {
        String string;
        BitSet table = new BitSet();
        this.setBits(table);
        int totalCharacters = table.cardinality();
        if (totalCharacters * 2 <= 65536) {
            return CharMatcher.precomputedPositive(totalCharacters, table, this.description);
        }
        table.flip(0, 65536);
        int negatedCharacters = 65536 - totalCharacters;
        String suffix = ".negate()";
        if (this.description.endsWith(suffix)) {
            string = this.description.substring(0, this.description.length() - suffix.length());
        } else {
            String string2 = String.valueOf(this.description);
            String string3 = String.valueOf(suffix);
            string = string3.length() != 0 ? string2.concat(string3) : new String(string2);
        }
        String negatedDescription = string;
        return new NegatedFastMatcher(this.toString(), CharMatcher.precomputedPositive(negatedCharacters, table, negatedDescription));
    }

    @GwtIncompatible(value="java.util.BitSet")
    private static CharMatcher precomputedPositive(int totalCharacters, BitSet table, String description) {
        switch (totalCharacters) {
            case 0: {
                return NONE;
            }
            case 1: {
                return CharMatcher.is((char)table.nextSetBit(0));
            }
            case 2: {
                char c1 = (char)table.nextSetBit(0);
                char c2 = (char)table.nextSetBit(c1 + '\u0001');
                return CharMatcher.isEither(c1, c2);
            }
        }
        return CharMatcher.isSmall(totalCharacters, table.length()) ? SmallCharMatcher.from(table, description) : new BitSetMatcher(table, description);
    }

    @GwtIncompatible(value="SmallCharMatcher")
    private static boolean isSmall(int totalCharacters, int tableLength) {
        return totalCharacters <= 1023 && tableLength > totalCharacters * 4 * 16;
    }

    @GwtIncompatible(value="java.util.BitSet")
    void setBits(BitSet table) {
        for (int c = 65535; c >= 0; --c) {
            if (!this.matches((char)c)) continue;
            table.set(c);
        }
    }

    public boolean matchesAnyOf(CharSequence sequence) {
        return !this.matchesNoneOf(sequence);
    }

    public boolean matchesAllOf(CharSequence sequence) {
        for (int i = sequence.length() - 1; i >= 0; --i) {
            if (this.matches(sequence.charAt(i))) continue;
            return false;
        }
        return true;
    }

    public boolean matchesNoneOf(CharSequence sequence) {
        return this.indexIn(sequence) == -1;
    }

    public int indexIn(CharSequence sequence) {
        int length = sequence.length();
        for (int i = 0; i < length; ++i) {
            if (!this.matches(sequence.charAt(i))) continue;
            return i;
        }
        return -1;
    }

    public int indexIn(CharSequence sequence, int start) {
        int length = sequence.length();
        Preconditions.checkPositionIndex(start, length);
        for (int i = start; i < length; ++i) {
            if (!this.matches(sequence.charAt(i))) continue;
            return i;
        }
        return -1;
    }

    public int lastIndexIn(CharSequence sequence) {
        for (int i = sequence.length() - 1; i >= 0; --i) {
            if (!this.matches(sequence.charAt(i))) continue;
            return i;
        }
        return -1;
    }

    public int countIn(CharSequence sequence) {
        int count = 0;
        for (int i = 0; i < sequence.length(); ++i) {
            if (!this.matches(sequence.charAt(i))) continue;
            ++count;
        }
        return count;
    }

    @CheckReturnValue
    public String removeFrom(CharSequence sequence) {
        String string = sequence.toString();
        int pos = this.indexIn(string);
        if (pos == -1) {
            return string;
        }
        char[] chars = string.toCharArray();
        int spread = 1;
        block0: while (true) {
            ++pos;
            while (pos != chars.length) {
                if (!this.matches(chars[pos])) {
                    chars[pos - spread] = chars[pos];
                    ++pos;
                    continue;
                }
                ++spread;
                continue block0;
            }
            break;
        }
        return new String(chars, 0, pos - spread);
    }

    @CheckReturnValue
    public String retainFrom(CharSequence sequence) {
        return this.negate().removeFrom(sequence);
    }

    @CheckReturnValue
    public String replaceFrom(CharSequence sequence, char replacement) {
        String string = sequence.toString();
        int pos = this.indexIn(string);
        if (pos == -1) {
            return string;
        }
        char[] chars = string.toCharArray();
        chars[pos] = replacement;
        for (int i = pos + 1; i < chars.length; ++i) {
            if (!this.matches(chars[i])) continue;
            chars[i] = replacement;
        }
        return new String(chars);
    }

    @CheckReturnValue
    public String replaceFrom(CharSequence sequence, CharSequence replacement) {
        int replacementLen = replacement.length();
        if (replacementLen == 0) {
            return this.removeFrom(sequence);
        }
        if (replacementLen == 1) {
            return this.replaceFrom(sequence, replacement.charAt(0));
        }
        String string = sequence.toString();
        int pos = this.indexIn(string);
        if (pos == -1) {
            return string;
        }
        int len = string.length();
        StringBuilder buf = new StringBuilder(len * 3 / 2 + 16);
        int oldpos = 0;
        do {
            buf.append(string, oldpos, pos);
            buf.append(replacement);
        } while ((pos = this.indexIn(string, oldpos = pos + 1)) != -1);
        buf.append(string, oldpos, len);
        return buf.toString();
    }

    @CheckReturnValue
    public String trimFrom(CharSequence sequence) {
        int last;
        int first;
        int len = sequence.length();
        for (first = 0; first < len && this.matches(sequence.charAt(first)); ++first) {
        }
        for (last = len - 1; last > first && this.matches(sequence.charAt(last)); --last) {
        }
        return sequence.subSequence(first, last + 1).toString();
    }

    @CheckReturnValue
    public String trimLeadingFrom(CharSequence sequence) {
        int len = sequence.length();
        for (int first = 0; first < len; ++first) {
            if (this.matches(sequence.charAt(first))) continue;
            return sequence.subSequence(first, len).toString();
        }
        return "";
    }

    @CheckReturnValue
    public String trimTrailingFrom(CharSequence sequence) {
        int len = sequence.length();
        for (int last = len - 1; last >= 0; --last) {
            if (this.matches(sequence.charAt(last))) continue;
            return sequence.subSequence(0, last + 1).toString();
        }
        return "";
    }

    @CheckReturnValue
    public String collapseFrom(CharSequence sequence, char replacement) {
        int len = sequence.length();
        for (int i = 0; i < len; ++i) {
            char c = sequence.charAt(i);
            if (!this.matches(c)) continue;
            if (!(c != replacement || i != len - 1 && this.matches(sequence.charAt(i + 1)))) {
                ++i;
                continue;
            }
            StringBuilder builder = new StringBuilder(len).append(sequence.subSequence(0, i)).append(replacement);
            return this.finishCollapseFrom(sequence, i + 1, len, replacement, builder, true);
        }
        return sequence.toString();
    }

    @CheckReturnValue
    public String trimAndCollapseFrom(CharSequence sequence, char replacement) {
        int last;
        int first;
        int len = sequence.length();
        for (first = 0; first < len && this.matches(sequence.charAt(first)); ++first) {
        }
        for (last = len - 1; last > first && this.matches(sequence.charAt(last)); --last) {
        }
        return first == 0 && last == len - 1 ? this.collapseFrom(sequence, replacement) : this.finishCollapseFrom(sequence, first, last + 1, replacement, new StringBuilder(last + 1 - first), false);
    }

    private String finishCollapseFrom(CharSequence sequence, int start, int end, char replacement, StringBuilder builder, boolean inMatchingGroup) {
        for (int i = start; i < end; ++i) {
            char c = sequence.charAt(i);
            if (this.matches(c)) {
                if (inMatchingGroup) continue;
                builder.append(replacement);
                inMatchingGroup = true;
                continue;
            }
            builder.append(c);
            inMatchingGroup = false;
        }
        return builder.toString();
    }

    @Override
    @Deprecated
    public boolean apply(Character character) {
        return this.matches(character.charValue());
    }

    public String toString() {
        return this.description;
    }

    static {
        StringBuilder builder = new StringBuilder(ZEROES.length());
        for (int i = 0; i < ZEROES.length(); ++i) {
            builder.append((char)(ZEROES.charAt(i) + 9));
        }
        NINES = builder.toString();
        DIGIT = new RangesMatcher("CharMatcher.DIGIT", ZEROES.toCharArray(), NINES.toCharArray());
        JAVA_DIGIT = new CharMatcher("CharMatcher.JAVA_DIGIT"){

            @Override
            public boolean matches(char c) {
                return Character.isDigit(c);
            }
        };
        JAVA_LETTER = new CharMatcher("CharMatcher.JAVA_LETTER"){

            @Override
            public boolean matches(char c) {
                return Character.isLetter(c);
            }
        };
        JAVA_LETTER_OR_DIGIT = new CharMatcher("CharMatcher.JAVA_LETTER_OR_DIGIT"){

            @Override
            public boolean matches(char c) {
                return Character.isLetterOrDigit(c);
            }
        };
        JAVA_UPPER_CASE = new CharMatcher("CharMatcher.JAVA_UPPER_CASE"){

            @Override
            public boolean matches(char c) {
                return Character.isUpperCase(c);
            }
        };
        JAVA_LOWER_CASE = new CharMatcher("CharMatcher.JAVA_LOWER_CASE"){

            @Override
            public boolean matches(char c) {
                return Character.isLowerCase(c);
            }
        };
        JAVA_ISO_CONTROL = CharMatcher.inRange('\u0000', '\u001f').or(CharMatcher.inRange('\u007f', '\u009f')).withToString("CharMatcher.JAVA_ISO_CONTROL");
        INVISIBLE = new RangesMatcher("CharMatcher.INVISIBLE", "\u0000\u007f\u00ad\u0600\u061c\u06dd\u070f\u1680\u180e\u2000\u2028\u205f\u2066\u2067\u2068\u2069\u206a\u3000\ud800\ufeff\ufff9\ufffa".toCharArray(), " \u00a0\u00ad\u0604\u061c\u06dd\u070f\u1680\u180e\u200f\u202f\u2064\u2066\u2067\u2068\u2069\u206f\u3000\uf8ff\ufeff\ufff9\ufffb".toCharArray());
        SINGLE_WIDTH = new RangesMatcher("CharMatcher.SINGLE_WIDTH", "\u0000\u05be\u05d0\u05f3\u0600\u0750\u0e00\u1e00\u2100\ufb50\ufe70\uff61".toCharArray(), "\u04f9\u05be\u05ea\u05f4\u06ff\u077f\u0e7f\u20af\u213a\ufdff\ufeff\uffdc".toCharArray());
        ANY = new FastMatcher("CharMatcher.ANY"){

            @Override
            public boolean matches(char c) {
                return true;
            }

            @Override
            public int indexIn(CharSequence sequence) {
                return sequence.length() == 0 ? -1 : 0;
            }

            @Override
            public int indexIn(CharSequence sequence, int start) {
                int length = sequence.length();
                Preconditions.checkPositionIndex(start, length);
                return start == length ? -1 : start;
            }

            @Override
            public int lastIndexIn(CharSequence sequence) {
                return sequence.length() - 1;
            }

            @Override
            public boolean matchesAllOf(CharSequence sequence) {
                Preconditions.checkNotNull(sequence);
                return true;
            }

            @Override
            public boolean matchesNoneOf(CharSequence sequence) {
                return sequence.length() == 0;
            }

            @Override
            public String removeFrom(CharSequence sequence) {
                Preconditions.checkNotNull(sequence);
                return "";
            }

            @Override
            public String replaceFrom(CharSequence sequence, char replacement) {
                char[] array = new char[sequence.length()];
                Arrays.fill(array, replacement);
                return new String(array);
            }

            @Override
            public String replaceFrom(CharSequence sequence, CharSequence replacement) {
                StringBuilder retval = new StringBuilder(sequence.length() * replacement.length());
                for (int i = 0; i < sequence.length(); ++i) {
                    retval.append(replacement);
                }
                return retval.toString();
            }

            @Override
            public String collapseFrom(CharSequence sequence, char replacement) {
                return sequence.length() == 0 ? "" : String.valueOf(replacement);
            }

            @Override
            public String trimFrom(CharSequence sequence) {
                Preconditions.checkNotNull(sequence);
                return "";
            }

            @Override
            public int countIn(CharSequence sequence) {
                return sequence.length();
            }

            @Override
            public CharMatcher and(CharMatcher other) {
                return Preconditions.checkNotNull(other);
            }

            @Override
            public CharMatcher or(CharMatcher other) {
                Preconditions.checkNotNull(other);
                return this;
            }

            @Override
            public CharMatcher negate() {
                return NONE;
            }
        };
        NONE = new FastMatcher("CharMatcher.NONE"){

            @Override
            public boolean matches(char c) {
                return false;
            }

            @Override
            public int indexIn(CharSequence sequence) {
                Preconditions.checkNotNull(sequence);
                return -1;
            }

            @Override
            public int indexIn(CharSequence sequence, int start) {
                int length = sequence.length();
                Preconditions.checkPositionIndex(start, length);
                return -1;
            }

            @Override
            public int lastIndexIn(CharSequence sequence) {
                Preconditions.checkNotNull(sequence);
                return -1;
            }

            @Override
            public boolean matchesAllOf(CharSequence sequence) {
                return sequence.length() == 0;
            }

            @Override
            public boolean matchesNoneOf(CharSequence sequence) {
                Preconditions.checkNotNull(sequence);
                return true;
            }

            @Override
            public String removeFrom(CharSequence sequence) {
                return sequence.toString();
            }

            @Override
            public String replaceFrom(CharSequence sequence, char replacement) {
                return sequence.toString();
            }

            @Override
            public String replaceFrom(CharSequence sequence, CharSequence replacement) {
                Preconditions.checkNotNull(replacement);
                return sequence.toString();
            }

            @Override
            public String collapseFrom(CharSequence sequence, char replacement) {
                return sequence.toString();
            }

            @Override
            public String trimFrom(CharSequence sequence) {
                return sequence.toString();
            }

            @Override
            public String trimLeadingFrom(CharSequence sequence) {
                return sequence.toString();
            }

            @Override
            public String trimTrailingFrom(CharSequence sequence) {
                return sequence.toString();
            }

            @Override
            public int countIn(CharSequence sequence) {
                Preconditions.checkNotNull(sequence);
                return 0;
            }

            @Override
            public CharMatcher and(CharMatcher other) {
                Preconditions.checkNotNull(other);
                return this;
            }

            @Override
            public CharMatcher or(CharMatcher other) {
                return Preconditions.checkNotNull(other);
            }

            @Override
            public CharMatcher negate() {
                return ANY;
            }
        };
        WHITESPACE_SHIFT = Integer.numberOfLeadingZeros(WHITESPACE_TABLE.length() - 1);
        WHITESPACE = new FastMatcher("WHITESPACE"){

            @Override
            public boolean matches(char c) {
                return CharMatcher.WHITESPACE_TABLE.charAt(1682554634 * c >>> WHITESPACE_SHIFT) == c;
            }

            @Override
            @GwtIncompatible(value="java.util.BitSet")
            void setBits(BitSet table) {
                for (int i = 0; i < CharMatcher.WHITESPACE_TABLE.length(); ++i) {
                    table.set(CharMatcher.WHITESPACE_TABLE.charAt(i));
                }
            }
        };
    }

    @GwtIncompatible(value="java.util.BitSet")
    private static class BitSetMatcher
    extends FastMatcher {
        private final BitSet table;

        private BitSetMatcher(BitSet table, String description) {
            super(description);
            if (table.length() + 64 < table.size()) {
                table = (BitSet)table.clone();
            }
            this.table = table;
        }

        @Override
        public boolean matches(char c) {
            return this.table.get(c);
        }

        @Override
        void setBits(BitSet bitSet) {
            bitSet.or(this.table);
        }
    }

    static final class NegatedFastMatcher
    extends NegatedMatcher {
        NegatedFastMatcher(CharMatcher original) {
            super(original);
        }

        NegatedFastMatcher(String toString, CharMatcher original) {
            super(toString, original);
        }

        @Override
        public final CharMatcher precomputed() {
            return this;
        }

        @Override
        CharMatcher withToString(String description) {
            return new NegatedFastMatcher(description, this.original);
        }
    }

    static abstract class FastMatcher
    extends CharMatcher {
        FastMatcher() {
        }

        FastMatcher(String description) {
            super(description);
        }

        @Override
        public final CharMatcher precomputed() {
            return this;
        }

        @Override
        public CharMatcher negate() {
            return new NegatedFastMatcher(this);
        }
    }

    private static class Or
    extends CharMatcher {
        final CharMatcher first;
        final CharMatcher second;

        Or(CharMatcher a, CharMatcher b, String description) {
            super(description);
            this.first = Preconditions.checkNotNull(a);
            this.second = Preconditions.checkNotNull(b);
        }

        Or(CharMatcher a, CharMatcher b) {
            String string = String.valueOf(String.valueOf(a));
            String string2 = String.valueOf(String.valueOf(b));
            this(a, b, new StringBuilder(18 + string.length() + string2.length()).append("CharMatcher.or(").append(string).append(", ").append(string2).append(")").toString());
        }

        @Override
        @GwtIncompatible(value="java.util.BitSet")
        void setBits(BitSet table) {
            this.first.setBits(table);
            this.second.setBits(table);
        }

        @Override
        public boolean matches(char c) {
            return this.first.matches(c) || this.second.matches(c);
        }

        @Override
        CharMatcher withToString(String description) {
            return new Or(this.first, this.second, description);
        }
    }

    private static class And
    extends CharMatcher {
        final CharMatcher first;
        final CharMatcher second;

        And(CharMatcher a, CharMatcher b) {
            String string = String.valueOf(String.valueOf(a));
            String string2 = String.valueOf(String.valueOf(b));
            this(a, b, new StringBuilder(19 + string.length() + string2.length()).append("CharMatcher.and(").append(string).append(", ").append(string2).append(")").toString());
        }

        And(CharMatcher a, CharMatcher b, String description) {
            super(description);
            this.first = Preconditions.checkNotNull(a);
            this.second = Preconditions.checkNotNull(b);
        }

        @Override
        public boolean matches(char c) {
            return this.first.matches(c) && this.second.matches(c);
        }

        @Override
        @GwtIncompatible(value="java.util.BitSet")
        void setBits(BitSet table) {
            BitSet tmp1 = new BitSet();
            this.first.setBits(tmp1);
            BitSet tmp2 = new BitSet();
            this.second.setBits(tmp2);
            tmp1.and(tmp2);
            table.or(tmp1);
        }

        @Override
        CharMatcher withToString(String description) {
            return new And(this.first, this.second, description);
        }
    }

    private static class NegatedMatcher
    extends CharMatcher {
        final CharMatcher original;

        NegatedMatcher(String toString, CharMatcher original) {
            super(toString);
            this.original = original;
        }

        NegatedMatcher(CharMatcher original) {
            String string = String.valueOf(String.valueOf(original));
            this(new StringBuilder(9 + string.length()).append(string).append(".negate()").toString(), original);
        }

        @Override
        public boolean matches(char c) {
            return !this.original.matches(c);
        }

        @Override
        public boolean matchesAllOf(CharSequence sequence) {
            return this.original.matchesNoneOf(sequence);
        }

        @Override
        public boolean matchesNoneOf(CharSequence sequence) {
            return this.original.matchesAllOf(sequence);
        }

        @Override
        public int countIn(CharSequence sequence) {
            return sequence.length() - this.original.countIn(sequence);
        }

        @Override
        @GwtIncompatible(value="java.util.BitSet")
        void setBits(BitSet table) {
            BitSet tmp = new BitSet();
            this.original.setBits(tmp);
            tmp.flip(0, 65536);
            table.or(tmp);
        }

        @Override
        public CharMatcher negate() {
            return this.original;
        }

        @Override
        CharMatcher withToString(String description) {
            return new NegatedMatcher(description, this.original);
        }
    }

    private static class RangesMatcher
    extends CharMatcher {
        private final char[] rangeStarts;
        private final char[] rangeEnds;

        RangesMatcher(String description, char[] rangeStarts, char[] rangeEnds) {
            super(description);
            this.rangeStarts = rangeStarts;
            this.rangeEnds = rangeEnds;
            Preconditions.checkArgument(rangeStarts.length == rangeEnds.length);
            for (int i = 0; i < rangeStarts.length; ++i) {
                Preconditions.checkArgument(rangeStarts[i] <= rangeEnds[i]);
                if (i + 1 >= rangeStarts.length) continue;
                Preconditions.checkArgument(rangeEnds[i] < rangeStarts[i + 1]);
            }
        }

        @Override
        public boolean matches(char c) {
            int index = Arrays.binarySearch(this.rangeStarts, c);
            if (index >= 0) {
                return true;
            }
            return (index = ~index - 1) >= 0 && c <= this.rangeEnds[index];
        }
    }
}
