package com.googlecode.javaewah;

/*
 * Copyright 2009-2016, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz, Owen Kaser, Gregory Ssi-Yan-Kai, Rory Graves
 * Licensed under the Apache License, Version 2.0.
 */

/**
 * Mostly for internal use. Similar to BufferedRunningLengthWord, but
 * automatically advances to the next BufferedRunningLengthWord as words are
 * discarded.
 *
 * @author David McIntosh
 * @since 0.4.0
 */
public final class IteratingBufferedRunningLengthWord implements IteratingRLW,
        Cloneable {
    /**
     * Instantiates a new iterating buffered running length word.
     *
     * @param iterator iterator
     */
    public IteratingBufferedRunningLengthWord(final EWAHIterator iterator) {
        this.iterator = iterator;
        this.brlw = new BufferedRunningLengthWord(this.iterator.next());
        this.literalWordStartPosition = this.iterator.literalWords()
                + this.brlw.literalWordOffset;
        this.buffer = this.iterator.buffer();
    }

    /**
     * Instantiates a new iterating buffered running length word.
     *
     * @param bitmap over which we want to iterate
     */
    public IteratingBufferedRunningLengthWord(
            final EWAHCompressedBitmap bitmap) {
        this(EWAHIterator.getEWAHIterator(bitmap));
    }

    /**
     * Discard first words, iterating to the next running length word if
     * needed.
     *
     * @param x the number of words to be discarded
     */
    @Override
    public void discardFirstWords(long x) {
        while (x > 0) {
            if (this.brlw.runningLength > x) {
                this.brlw.runningLength -= x;
                return;
            }
            x -= this.brlw.runningLength;
            this.brlw.runningLength = 0;
            long toDiscard = x > this.brlw.numberOfLiteralWords ? this.brlw.numberOfLiteralWords
                    : x;

            this.literalWordStartPosition += toDiscard;
            this.brlw.numberOfLiteralWords -= toDiscard;
            x -= toDiscard;
            if ((x > 0) || (this.brlw.size() == 0)) {
                if (!this.iterator.hasNext()) {
                    break;
                }
                this.brlw.reset(this.iterator.next());
                this.literalWordStartPosition = this.iterator
                        .literalWords();
            }
        }
    }
	@Override
	public void discardLiteralWords(long x) {
		this.literalWordStartPosition += x;
		this.brlw.numberOfLiteralWords -= x;
		if (this.brlw.numberOfLiteralWords == 0) {
			if (!this.iterator.hasNext()) {
				return;
			}
			this.brlw.reset(this.iterator.next());
			this.literalWordStartPosition = this.iterator.literalWords();
		}
	}
    @Override
    public void discardRunningWords() {
        this.brlw.runningLength = 0;
        if (this.brlw.getNumberOfLiteralWords() == 0)
            this.next();
    }

    /**
     * Move to the next RunningLengthWord
     *
     * @return whether the move was possible
     */
    @Override
    public boolean next() {
        if (!this.iterator.hasNext()) {
            this.brlw.numberOfLiteralWords = 0;
            this.brlw.runningLength = 0;
            return false;
        }
        this.brlw.reset(this.iterator.next());
        this.literalWordStartPosition = this.iterator.literalWords(); // +
        // this.brlw.literalWordOffset
        // ==0
        return true;
    }

    /**
     * Write out up to max words, returns how many were written
     *
     * @param container target for writes
     * @param max       maximal number of writes
     * @return how many written
     */
    public long discharge(BitmapStorage container, long max) {
    	long index = 0;
    	while (true) {
    		if (index + getRunningLength() > max) {
    			final int offset = (int) (max - index);
    			container.addStreamOfEmptyWords(getRunningBit(), offset);
    			this.brlw.runningLength -= offset;
    			return max;
    		}
    		container.addStreamOfEmptyWords(getRunningBit(), getRunningLength());
    		index += getRunningLength();
    		if (getNumberOfLiteralWords() + index > max) {
    			final int offset =(int) (max - index);
    			writeLiteralWords(offset, container);
    			this.brlw.runningLength = 0;
    			this.brlw.numberOfLiteralWords -= offset;
    			this.literalWordStartPosition += offset;
    			return max;
    		}
    		writeLiteralWords(getNumberOfLiteralWords(), container);
    		index += getNumberOfLiteralWords();
    		if(!next()) break;
    	}
    	return index;
    }

    /**
     * Write out up to max words (negated), returns how many were written
     *
     * @param container target for writes
     * @param max       maximal number of writes
     * @return how many written
     */
    public long dischargeNegated(BitmapStorage container, long max) {
        long index = 0;
        while ((index < max) && (size() > 0)) {
            // first run
            long pl = getRunningLength();
            if (index + pl > max) {
                pl = max - index;
            }
            container.addStreamOfEmptyWords(!getRunningBit(), pl);
            index += pl;
            int pd = getNumberOfLiteralWords();
            if (pd + index > max) {
                pd = (int) (max - index);
            }
            writeNegatedLiteralWords(pd, container);
            discardFirstWords(pl + pd);
            index += pd;
        }
        return index;
    }

    /**
     * Write out the remain words, transforming them to zeroes.
     *
     * @param container target for writes
     */
    public void dischargeAsEmpty(BitmapStorage container) {
        while (size() > 0) {
            container.addStreamOfEmptyWords(false, size());
            discardFirstWords(size());
        }
    }

    /**
     * Write out the remaining words
     *
     * @param container target for writes
     */
    public void discharge(BitmapStorage container) {
        this.brlw.literalWordOffset = this.literalWordStartPosition - this.iterator.literalWords();
        discharge(this.brlw, this.iterator, container);
    }

    /**
     * Get the nth literal word for the current running length word
     *
     * @param index zero based index
     * @return the literal word
     */
    @Override
    public long getLiteralWordAt(int index) {
        return this.buffer.getWord(this.literalWordStartPosition + index);
    }

    /**
     * Gets the number of literal words for the current running length word.
     *
     * @return the number of literal words
     */
    @Override
    public int getNumberOfLiteralWords() {
        return this.brlw.numberOfLiteralWords;
    }

    /**
     * Gets the running bit.
     *
     * @return the running bit
     */
    @Override
    public boolean getRunningBit() {
        return this.brlw.runningBit;
    }

    /**
     * Gets the running length.
     *
     * @return the running length
     */
    @Override
    public long getRunningLength() {
        return this.brlw.runningLength;
    }

    /**
     * Size in uncompressed words of the current running length word.
     *
     * @return the long
     */
    @Override
    public long size() {
        return this.brlw.size();
    }

    /**
     * write the first N literal words to the target bitmap. Does not
     * discard the words or perform iteration.
     *
     * @param numWords  number of words to be written
     * @param container where we write
     */
    public void writeLiteralWords(int numWords, BitmapStorage container) {
        container.addStreamOfLiteralWords(this.buffer, this.literalWordStartPosition, numWords);
    }

    /**
     * write the first N literal words (negated) to the target bitmap. Does
     * not discard the words or perform iteration.
     *
     * @param numWords  number of words to be written
     * @param container where we write
     */
    public void writeNegatedLiteralWords(int numWords, BitmapStorage container) {
        container.addStreamOfNegatedLiteralWords(this.buffer, this.literalWordStartPosition, numWords);
    }

    /**
     * For internal use. (One could use the non-static discharge method
     * instead, but we expect them to be slower.)
     *
     * @param initialWord the initial word
     * @param iterator    the iterator
     * @param container   the container
     */
    private static void discharge(final BufferedRunningLengthWord initialWord,
            final EWAHIterator iterator, final BitmapStorage container) {
        BufferedRunningLengthWord runningLengthWord = initialWord;
        for (; ; ) {
            final long runningLength = runningLengthWord.getRunningLength();
            container.addStreamOfEmptyWords(runningLengthWord.getRunningBit(), runningLength);
            container.addStreamOfLiteralWords(iterator.buffer(),
                    iterator.literalWords() + runningLengthWord.literalWordOffset,
                    runningLengthWord.getNumberOfLiteralWords()
            );
            if (!iterator.hasNext())
                break;
            runningLengthWord = new BufferedRunningLengthWord(iterator.next());
        }
    }

    @Override
    public IteratingBufferedRunningLengthWord clone() throws CloneNotSupportedException {
        IteratingBufferedRunningLengthWord answer = (IteratingBufferedRunningLengthWord) super .clone();
        answer.brlw = this.brlw.clone();
        answer.iterator = this.iterator.clone();
        return answer;
    }

    private BufferedRunningLengthWord brlw;
    private final Buffer buffer;
    private int literalWordStartPosition;
    private EWAHIterator iterator;

}


package com.googlecode.javaewah32;

import com.googlecode.javaewah.CloneableIterator;

import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;

/*
 * Copyright 2009-2016, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz, Owen Kaser, Gregory Ssi-Yan-Kai, Rory Graves
 * Licensed under the Apache License, Version 2.0.
 */

/**
 * Set of helper functions to aggregate bitmaps.
 */
public final class IteratorAggregation32 {

    /** Private constructor to prevent instantiation */
    private IteratorAggregation32() {}

    /**
     * @param x iterator to negate
     * @return negated version of the iterator
     */
    public static IteratingRLW32 not(final IteratingRLW32 x) {
        return new IteratingRLW32() {

            @Override
            public boolean next() {
                return x.next();
            }

            @Override
            public int getLiteralWordAt(int index) {
                return ~x.getLiteralWordAt(index);
            }

            @Override
            public int getNumberOfLiteralWords() {
                return x.getNumberOfLiteralWords();
            }

            @Override
            public boolean getRunningBit() {
                return !x.getRunningBit();
            }

            @Override
            public int size() {
                return x.size();
            }

            @Override
            public int getRunningLength() {
                return x.getRunningLength();
            }

            @Override
            public void discardFirstWords(int y) {
                x.discardFirstWords(y);
            }

            @Override
            public void discardRunningWords() {
                x.discardRunningWords();
            }

            @Override
            public IteratingRLW32 clone()
                    throws CloneNotSupportedException {
                throw new CloneNotSupportedException();
            }

			@Override
			public void discardLiteralWords(int y) {
				x.discardLiteralWords(y);
			}
        };
    }

    /**
     * Aggregate the iterators using a bitmap buffer.
     *
     * @param al iterators to aggregate
     * @return and aggregate
     */
    public static IteratingRLW32 bufferedand(final IteratingRLW32... al) {
        return bufferedand(DEFAULT_MAX_BUF_SIZE, al);
    }

    /**
     * Aggregate the iterators using a bitmap buffer.
     *
     * @param al      iterators to aggregate
     * @param bufSize size of the internal buffer used by the iterator in
     *                64-bit words
     * @return and aggregate
     */
    public static IteratingRLW32 bufferedand(final int bufSize,
                                             final IteratingRLW32... al) {
        if (al.length == 0)
            throw new IllegalArgumentException(
                    "Need at least one iterator");
        if (al.length == 1)
            return al[0];
        final LinkedList<IteratingRLW32> basell = new LinkedList<IteratingRLW32>();
        Collections.addAll(basell, al);
        return new BufferedIterator32(new AndIt(basell, bufSize));
    }

    /**
     * Aggregate the iterators using a bitmap buffer.
     *
     * @param al iterators to aggregate
     * @return or aggregate
     */
    public static IteratingRLW32 bufferedor(final IteratingRLW32... al) {
        return bufferedor(DEFAULT_MAX_BUF_SIZE, al);
    }

    /**
     * Aggregate the iterators using a bitmap buffer.
     *
     * @param al      iterators to aggregate
     * @param bufSize size of the internal buffer used by the iterator in
     *                64-bit words
     * @return or aggregate
     */
    public static IteratingRLW32 bufferedor(final int bufSize,
                                            final IteratingRLW32... al) {
        if (al.length == 0)
            throw new IllegalArgumentException(
                    "Need at least one iterator");
        if (al.length == 1)
            return al[0];

        final LinkedList<IteratingRLW32> basell = new LinkedList<IteratingRLW32>();
        Collections.addAll(basell, al);
        return new BufferedIterator32(new ORIt(basell, bufSize));
    }

    /**
     * Aggregate the iterators using a bitmap buffer.
     *
     * @param al iterators to aggregate
     * @return xor aggregate
     */
    public static IteratingRLW32 bufferedxor(final IteratingRLW32... al) {
        return bufferedxor(DEFAULT_MAX_BUF_SIZE, al);
    }

    /**
     * Aggregate the iterators using a bitmap buffer.
     *
     * @param al      iterators to aggregate
     * @param bufSize size of the internal buffer used by the iterator in
     *                64-bit words
     * @return xor aggregate
     */
    public static IteratingRLW32 bufferedxor(final int bufSize,
                                             final IteratingRLW32... al) {
        if (al.length == 0)
            throw new IllegalArgumentException(
                    "Need at least one iterator");
        if (al.length == 1)
            return al[0];

        final LinkedList<IteratingRLW32> basell = new LinkedList<IteratingRLW32>();
        Collections.addAll(basell, al);
        return new BufferedIterator32(new XORIt(basell, bufSize));
    }

    /**
     * Write out the content of the iterator, but as if it were all zeros.
     *
     * @param container where we write
     * @param i         the iterator
     */
    protected static void dischargeAsEmpty(final BitmapStorage32 container,
                                           final IteratingRLW32 i) {
        while (i.size() > 0) {
            container.addStreamOfEmptyWords(false, i.size());
            i.next();

        }
    }

    /**
     * Write out up to max words, returns how many were written
     *
     * @param container target for writes
     * @param i         source of data
     * @param max       maximal number of writes
     * @return how many written
     */
    protected static int discharge(final BitmapStorage32 container,
                                   IteratingRLW32 i, int max) {
        int counter = 0;
        while (i.size() > 0 && counter < max) {
            int l1 = i.getRunningLength();
            if (l1 > 0) {
                if (l1 + counter > max)
                    l1 = max - counter;
                container.addStreamOfEmptyWords(
                        i.getRunningBit(), l1);
                counter += l1;
            }
            int l = i.getNumberOfLiteralWords();
            if (l + counter > max)
                l = max - counter;
            for (int k = 0; k < l; ++k) {
                container.addWord(i.getLiteralWordAt(k));
            }
            counter += l;
            i.discardFirstWords(l + l1);
        }
        return counter;
    }

    /**
     * Write out up to max negated words, returns how many were written
     *
     * @param container target for writes
     * @param i         source of data
     * @param max       maximal number of writes
     * @return how many written
     */
    protected static int dischargeNegated(final BitmapStorage32 container,
                                          IteratingRLW32 i, int max) {
        int counter = 0;
        while (i.size() > 0 && counter < max) {
            int l1 = i.getRunningLength();
            if (l1 > 0) {
                if (l1 + counter > max)
                    l1 = max - counter;
                container.addStreamOfEmptyWords(i.getRunningBit(), l1);
                counter += l1;
            }
            int l = i.getNumberOfLiteralWords();
            if (l + counter > max)
                l = max - counter;
            for (int k = 0; k < l; ++k) {
                container.addWord(i.getLiteralWordAt(k));
            }
            counter += l;
            i.discardFirstWords(l + l1);
        }
        return counter;
    }

    static void andToContainer(final BitmapStorage32 container,
                               int desiredrlwcount, final IteratingRLW32 rlwi,
                               IteratingRLW32 rlwj) {
        while ((rlwi.size() > 0) && (rlwj.size() > 0)
                && (desiredrlwcount-- > 0)) {
            while ((rlwi.getRunningLength() > 0)
                    || (rlwj.getRunningLength() > 0)) {
                final boolean i_is_prey = rlwi
                        .getRunningLength() < rlwj
                        .getRunningLength();
                final IteratingRLW32 prey = i_is_prey ? rlwi
                        : rlwj;
                final IteratingRLW32 predator = i_is_prey ? rlwj
                        : rlwi;
                if (!predator.getRunningBit()) {
                    container.addStreamOfEmptyWords(false,
                            predator.getRunningLength());
                    prey.discardFirstWords(predator
                            .getRunningLength());
                    predator.discardFirstWords(predator
                            .getRunningLength());
                } else {
                    final int index = discharge(container,
                            prey,
                            predator.getRunningLength());
                    container.addStreamOfEmptyWords(false,
                            predator.getRunningLength()
                                    - index
                    );
                    predator.discardFirstWords(predator
                            .getRunningLength());
                }
            }
            final int nbre_literal = Math.min(
                    rlwi.getNumberOfLiteralWords(),
                    rlwj.getNumberOfLiteralWords());
            if (nbre_literal > 0) {
                desiredrlwcount -= nbre_literal;
                for (int k = 0; k < nbre_literal; ++k)
                    container.addWord(rlwi.getLiteralWordAt(k)
                            & rlwj.getLiteralWordAt(k));
                rlwi.discardFirstWords(nbre_literal);
                rlwj.discardFirstWords(nbre_literal);
            }
        }
    }

    static void andToContainer(final BitmapStorage32 container,
                               final IteratingRLW32 rlwi, IteratingRLW32 rlwj) {
        while ((rlwi.size() > 0) && (rlwj.size() > 0)) {
            while ((rlwi.getRunningLength() > 0)
                    || (rlwj.getRunningLength() > 0)) {
                final boolean i_is_prey = rlwi
                        .getRunningLength() < rlwj
                        .getRunningLength();
                final IteratingRLW32 prey = i_is_prey ? rlwi
                        : rlwj;
                final IteratingRLW32 predator = i_is_prey ? rlwj
                        : rlwi;
                if (!predator.getRunningBit()) {
                    container.addStreamOfEmptyWords(false,
                            predator.getRunningLength());
                    prey.discardFirstWords(predator
                            .getRunningLength());
                    predator.discardFirstWords(predator
                            .getRunningLength());
                } else {
                    final int index = discharge(container,
                            prey,
                            predator.getRunningLength());
                    container.addStreamOfEmptyWords(false,
                            predator.getRunningLength()
                                    - index
                    );
                    predator.discardFirstWords(predator
                            .getRunningLength());
                }
            }
            final int nbre_literal = Math.min(
                    rlwi.getNumberOfLiteralWords(),
                    rlwj.getNumberOfLiteralWords());
            if (nbre_literal > 0) {
                for (int k = 0; k < nbre_literal; ++k)
                    container.addWord(rlwi.getLiteralWordAt(k)
                            & rlwj.getLiteralWordAt(k));
                rlwi.discardFirstWords(nbre_literal);
                rlwj.discardFirstWords(nbre_literal);
            }
        }
    }

    /**
     * Compute the first few words of the XOR aggregate between two
     * iterators.
     *
     * @param container       where to write
     * @param desiredrlwcount number of words to be written (max)
     * @param rlwi            first iterator to aggregate
     * @param rlwj            second iterator to aggregate
     */
    public static void xorToContainer(final BitmapStorage32 container,
                                      int desiredrlwcount, final IteratingRLW32 rlwi,
                                      IteratingRLW32 rlwj) {
        while ((rlwi.size() > 0) && (rlwj.size() > 0)
                && (desiredrlwcount-- > 0)) {
            while ((rlwi.getRunningLength() > 0)
                    || (rlwj.getRunningLength() > 0)) {
                final boolean i_is_prey = rlwi
                        .getRunningLength() < rlwj
                        .getRunningLength();
                final IteratingRLW32 prey = i_is_prey ? rlwi
                        : rlwj;
                final IteratingRLW32 predator = i_is_prey ? rlwj
                        : rlwi;
                if (!predator.getRunningBit()) {
                    int index = discharge(container, prey,
                            predator.getRunningLength());
                    container.addStreamOfEmptyWords(false,
                            predator.getRunningLength()
                                    - index
                    );
                    predator.discardFirstWords(predator
                            .getRunningLength());
                } else {
                    int index = dischargeNegated(container,
                            prey,
                            predator.getRunningLength());
                    container.addStreamOfEmptyWords(true,
                            predator.getRunningLength()
                                    - index
                    );
                    predator.discardFirstWords(predator
                            .getRunningLength());
                }
            }
            final int nbre_literal = Math.min(
                    rlwi.getNumberOfLiteralWords(),
                    rlwj.getNumberOfLiteralWords());
            if (nbre_literal > 0) {
                desiredrlwcount -= nbre_literal;
                for (int k = 0; k < nbre_literal; ++k)
                    container.addWord(rlwi.getLiteralWordAt(k)
                            ^ rlwj.getLiteralWordAt(k));
                rlwi.discardFirstWords(nbre_literal);
                rlwj.discardFirstWords(nbre_literal);
            }
        }
    }

    protected static int inplaceor(int[] bitmap, IteratingRLW32 i) {
        int pos = 0;
        int s;
        while ((s = i.size()) > 0) {
            if (pos + s < bitmap.length) {
                final int L = i.getRunningLength();
                if (i.getRunningBit())
                    java.util.Arrays.fill(bitmap, pos, pos
                            + L, ~0);
                pos += L;
                final int LR = i.getNumberOfLiteralWords();
                for (int k = 0; k < LR; ++k)
                    bitmap[pos++] |= i.getLiteralWordAt(k);
                if (!i.next()) {
                    return pos;
                }
            } else {
                int howmany = bitmap.length - pos;
                int l = i.getRunningLength();
                if (pos + l > bitmap.length) {
                    if (i.getRunningBit()) {
                        java.util.Arrays.fill(bitmap, pos, bitmap.length, ~0);
                    }
                    i.discardFirstWords(howmany);
                    return bitmap.length;
                }
                if (i.getRunningBit())
                    java.util.Arrays.fill(bitmap, pos, pos + l, ~0);
                pos += l;
                for (int k = 0; pos < bitmap.length; ++k)
                    bitmap[pos++] |= i.getLiteralWordAt(k);
                i.discardFirstWords(howmany);
                return pos;
            }
        }
        return pos;
    }

    protected static int inplacexor(int[] bitmap, IteratingRLW32 i) {
        int pos = 0;
        int s;
        while ((s = i.size()) > 0) {
            if (pos + s < bitmap.length) {
                final int L = i.getRunningLength();
                if (i.getRunningBit()) {
                    for (int k = pos; k < pos + L; ++k)
                        bitmap[k] = ~bitmap[k];
                }
                pos += L;
                final int LR = i.getNumberOfLiteralWords();
                for (int k = 0; k < LR; ++k)
                    bitmap[pos++] ^= i.getLiteralWordAt(k);
                if (!i.next()) {
                    return pos;
                }
            } else {
                int howMany = bitmap.length - pos;
                int l = i.getRunningLength();
                if (pos + l > bitmap.length) {
                    if (i.getRunningBit()) {
                        for (int k = pos; k < bitmap.length; ++k)
                            bitmap[k] = ~bitmap[k];
                    }
                    i.discardFirstWords(howMany);
                    return bitmap.length;
                }
                if (i.getRunningBit())
                    for (int k = pos; k < pos + l; ++k)
                        bitmap[k] = ~bitmap[k];
                pos += l;
                for (int k = 0; pos < bitmap.length; ++k)
                    bitmap[pos++] ^= i.getLiteralWordAt(k);
                i.discardFirstWords(howMany);
                return pos;
            }
        }
        return pos;
    }

    protected static int inplaceand(int[] bitmap, IteratingRLW32 i) {
        int pos = 0;
        int s;
        while ((s = i.size()) > 0) {
            if (pos + s < bitmap.length) {
                final int L = i.getRunningLength();
                if (!i.getRunningBit()) {
                    for (int k = pos; k < pos + L; ++k)
                        bitmap[k] = 0;
                }
                pos += L;
                final int LR = i.getNumberOfLiteralWords();
                for (int k = 0; k < LR; ++k)
                    bitmap[pos++] &= i.getLiteralWordAt(k);
                if (!i.next()) {
                    return pos;
                }
            } else {
                int howMany = bitmap.length - pos;
                int l = i.getRunningLength();
                if (pos + l > bitmap.length) {
                    if (!i.getRunningBit()) {
                        for (int k = pos; k < bitmap.length; ++k)
                            bitmap[k] = 0;
                    }
                    i.discardFirstWords(howMany);
                    return bitmap.length;
                }
                if (!i.getRunningBit())
                    for (int k = pos; k < pos + l; ++k)
                        bitmap[k] = 0;
                pos += l;
                for (int k = 0; pos < bitmap.length; ++k)
                    bitmap[pos++] &= i.getLiteralWordAt(k);
                i.discardFirstWords(howMany);
                return pos;
            }
        }
        return pos;
    }

    /**
     * An optimization option. Larger values may improve speed, but at the
     * expense of memory.
     */
    public static final int DEFAULT_MAX_BUF_SIZE = 65536;

}

class ORIt implements CloneableIterator<EWAHIterator32> {
    final EWAHCompressedBitmap32 buffer = new EWAHCompressedBitmap32();
    final int[] hardBitmap;
    final LinkedList<IteratingRLW32> ll;

    ORIt(LinkedList<IteratingRLW32> basell, final int bufSize) {
        this.ll = basell;
        this.hardBitmap = new int[bufSize];
    }

    @Override
    public XORIt clone() throws CloneNotSupportedException {
        XORIt answer = (XORIt) super.clone();
        answer.buffer = this.buffer.clone();
        answer.hardbitmap = this.hardBitmap.clone();
        answer.ll = (LinkedList<IteratingRLW32>) this.ll.clone();
        return answer;
    }

    @Override
    public boolean hasNext() {
        return !this.ll.isEmpty();
    }

    @Override
    public EWAHIterator32 next() {
        this.buffer.clear();
        int effective = 0;
        Iterator<IteratingRLW32> i = this.ll.iterator();
        while (i.hasNext()) {
            IteratingRLW32 rlw = i.next();
            if (rlw.size() > 0) {
                int eff = IteratorAggregation32.inplaceor(
                        this.hardBitmap, rlw);
                if (eff > effective)
                    effective = eff;
            } else
                i.remove();
        }
        for (int k = 0; k < effective; ++k)
            this.buffer.addWord(this.hardBitmap[k]);
        Arrays.fill(this.hardBitmap, 0);
        return this.buffer.getEWAHIterator();
    }
}

class XORIt implements CloneableIterator<EWAHIterator32> {
    EWAHCompressedBitmap32 buffer = new EWAHCompressedBitmap32();
    int[] hardbitmap;
    LinkedList<IteratingRLW32> ll;

    XORIt(LinkedList<IteratingRLW32> basell, final int bufSize) {
        this.ll = basell;
        this.hardbitmap = new int[bufSize];

    }

    @Override
    public XORIt clone() throws CloneNotSupportedException {
        XORIt answer = (XORIt) super.clone();
        answer.buffer = this.buffer.clone();
        answer.hardbitmap = this.hardbitmap.clone();
        answer.ll = (LinkedList<IteratingRLW32>) this.ll.clone();
        return answer;
    }

    @Override
    public boolean hasNext() {
        return !this.ll.isEmpty();
    }

    @Override
    public EWAHIterator32 next() {
        this.buffer.clear();
        int effective = 0;
        Iterator<IteratingRLW32> i = this.ll.iterator();
        while (i.hasNext()) {
            IteratingRLW32 rlw = i.next();
            if (rlw.size() > 0) {
                int eff = IteratorAggregation32.inplacexor(
                        this.hardbitmap, rlw);
                if (eff > effective)
                    effective = eff;
            } else
                i.remove();
        }
        for (int k = 0; k < effective; ++k)
            this.buffer.addWord(this.hardbitmap[k]);
        Arrays.fill(this.hardbitmap, 0);
        return this.buffer.getEWAHIterator();
    }
}

class AndIt implements CloneableIterator<EWAHIterator32> {
    EWAHCompressedBitmap32 buffer = new EWAHCompressedBitmap32();
    LinkedList<IteratingRLW32> ll;
    final int bufferSize;

    public AndIt(LinkedList<IteratingRLW32> basell, final int bufSize) {
        this.ll = basell;
        this.bufferSize = bufSize;
    }

    @Override
    public boolean hasNext() {
        return !this.ll.isEmpty();
    }

    @Override
    public AndIt clone() throws CloneNotSupportedException {
        AndIt answer = (AndIt) super.clone();
        answer.buffer = this.buffer.clone();
        answer.ll = (LinkedList<IteratingRLW32>) this.ll.clone();
        return answer;
    }

    @Override
    public EWAHIterator32 next() {
        this.buffer.clear();
        IteratorAggregation32.andToContainer(this.buffer,
                this.bufferSize * this.ll.size(), this.ll.get(0),
                this.ll.get(1));
        if (this.ll.size() > 2) {
            Iterator<IteratingRLW32> i = this.ll.iterator();
            i.next();
            i.next();
            EWAHCompressedBitmap32 tmpbuffer = new EWAHCompressedBitmap32();
            while (i.hasNext() && this.buffer.sizeInBytes() > 0) {
                IteratorAggregation32
                        .andToContainer(tmpbuffer,
                                this.buffer.getIteratingRLW(),
                                i.next());
                this.buffer.swap(tmpbuffer);
                tmpbuffer.clear();
            }
        }
        for (IteratingRLW32 aLl : this.ll) {
            if (aLl.size() == 0) {
                this.ll.clear();
                break;
            }
        }
        return this.buffer.getEWAHIterator();
    }

}


package com.googlecode.javaewah;

/*
 * Copyright 2009-2016, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz, Owen Kaser, Gregory Ssi-Yan-Kai, Rory Graves
 * Licensed under the Apache License, Version 2.0.
 */

/**
 * Mostly for internal use. Similar to RunningLengthWord, but can be modified
 * without access to the array, and has faster access.
 *
 * @author Daniel Lemire
 * @since 0.1.0
 */
public final class BufferedRunningLengthWord implements Cloneable {

    /**
     * Instantiates a new buffered running length word.
     *
     * @param a the word
     */
    public BufferedRunningLengthWord(final long a) {
        this.numberOfLiteralWords = (int) (a >>> (1 + RunningLengthWord.RUNNING_LENGTH_BITS));
        this.runningBit = (a & 1) != 0;
        this.runningLength = (int) ((a >>> 1) & RunningLengthWord.LARGEST_RUNNING_LENGTH_COUNT);
    }

    /**
     * Instantiates a new buffered running length word.
     *
     * @param rlw the rlw
     */
    public BufferedRunningLengthWord(final RunningLengthWord rlw) {
        this(rlw.buffer.getWord(rlw.position));
    }

    /**
     * Discard first words.
     *
     * @param x the x
     */
    public void discardFirstWords(long x) {
        if (this.runningLength >= x) {
            this.runningLength -= x;
            return;
        }
        x -= this.runningLength;
        this.runningLength = 0;
        this.literalWordOffset += x;
        this.numberOfLiteralWords -= x;
    }

    /**
     * Gets the number of literal words.
     *
     * @return the number of literal words
     */
    public int getNumberOfLiteralWords() {
        return this.numberOfLiteralWords;
    }

    /**
     * Gets the running bit.
     *
     * @return the running bit
     */
    public boolean getRunningBit() {
        return this.runningBit;
    }

    /**
     * Gets the running length.
     *
     * @return the running length
     */
    public long getRunningLength() {
        return this.runningLength;
    }

    /**
     * Reset the values using the provided word.
     *
     * @param a the word
     */
    public void reset(final long a) {
        this.numberOfLiteralWords = (int) (a >>> (1 + RunningLengthWord.RUNNING_LENGTH_BITS));
        this.runningBit = (a & 1) != 0;
        this.runningLength = (int) ((a >>> 1) & RunningLengthWord.LARGEST_RUNNING_LENGTH_COUNT);
        this.literalWordOffset = 0;
    }

    /**
     * Reset the values of this running length word so that it has the same
     * values as the other running length word.
     *
     * @param rlw the other running length word
     */
    public void reset(final RunningLengthWord rlw) {
        reset(rlw.buffer.getWord(rlw.position));
    }

    /**
     * Sets the number of literal words.
     *
     * @param number the new number of literal words
     */
    public void setNumberOfLiteralWords(final int number) {
        this.numberOfLiteralWords = number;
    }

    /**
     * Sets the running bit.
     *
     * @param b the new running bit
     */
    public void setRunningBit(final boolean b) {
        this.runningBit = b;
    }

    /**
     * Sets the running length.
     *
     * @param number the new running length
     */
    public void setRunningLength(final long number) {
        this.runningLength = number;
    }

    /**
     * Size in uncompressed words.
     *
     * @return the long
     */
    public long size() {
        return this.runningLength + this.numberOfLiteralWords;
    }

    /*
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "running bit = " + getRunningBit() +
               " running length = " + getRunningLength() +
               " number of lit. words " + getNumberOfLiteralWords();
    }

    @Override
    public BufferedRunningLengthWord clone() throws CloneNotSupportedException {
        BufferedRunningLengthWord answer = (BufferedRunningLengthWord) super.clone();
        answer.literalWordOffset = this.literalWordOffset;
        answer.numberOfLiteralWords = this.numberOfLiteralWords;
        answer.runningBit = this.runningBit;
        answer.runningLength = this.runningLength;
        return answer;
    }

    /**
     * how many literal words have we read so far?
     */
    protected int literalWordOffset = 0;

    /**
     * The Number of literal words.
     */
    protected int numberOfLiteralWords;

    /**
     * The Running bit.
     */
    protected boolean runningBit;

    /**
     * The Running length.
     */
    protected long runningLength;
}


package com.googlecode.javaewah.datastructure;

import java.nio.LongBuffer;
import java.util.Iterator;

import com.googlecode.javaewah.IntIterator;

/**
 *
 * This is an immutable version of the BitSet class in this same package.
 * It is meant to be used with memory-file mapping.
 *
 *
final FileOutputStream fos = new FileOutputStream(tmpfile);
 *	BitSet Bitmap = BitSet.bitmapOf(0, 2, 55, 64, 512);
 *	Bitmap.serialize(new DataOutputStream(fos));
 *		RandomAccessFile memoryMappedFile = new RandomAccessFile(tmpfile, "r");
 *		ByteBuffer bb = memoryMappedFile.getChannel().map(
 *				FileChannel.MapMode.READ_ONLY, 0, totalcount);
 *		ImmutableBitSet mapped = new ImmutableBitSet(bb.asLongBuffer());

 *
 */
public class ImmutableBitSet  implements Cloneable, Iterable<Integer>,WordArray {
  /**
   * Construct a ImmutableBitSet from the content of the LongBuffer
   * which should have been initialized with BitSet.serialize (from the BitSet in this
   * same package).
   *
   * The input is not modified.
   *
   * @param bs the data source
   */
  public ImmutableBitSet(final LongBuffer bs) {
  	int length = (int) bs.get(0);
  	LongBuffer copy = bs.slice();
  	copy.position(1);
  	data = copy.slice();
  	data.limit(length);
  }

  /**
   * Get a copy of this ImmutableBitSet as a mutable BitSet.
   * @return a copy
   */
  public BitSet asBitSet() {
  	BitSet bs = new BitSet(this.size());
  	this.data.rewind();
  	this.data.get(bs.data, 0, bs.data.length);
		return bs;
  }

  /**
   * Compute the number of bits set to 1
   *
   * @return the number of bits
   */
  public int cardinality() {
      int sum = 0;
      int length = this.data.limit();
      for(int k = 0; k < length; ++k)
          sum += Long.bitCount(this.data.get(k));
      return sum;
  }

  @Override
  public ImmutableBitSet clone()  {
  	  ImmutableBitSet b;
      try {
          b = (ImmutableBitSet) super.clone();
          b.data = this.data.duplicate();
          return b;
      } catch (CloneNotSupportedException e) {
          return null;
      }
  }

	@Override
  public boolean equals(Object o) {
		if (o instanceof WordArray) {
			WordArray bs = (WordArray) o;
			for (int k = 0; k < Math.min(this.getNumberOfWords(),
					bs.getNumberOfWords()); ++k) {
				if (this.getWord(k) != bs.getWord(k))
					return false;
			}
			WordArray longer = bs.getNumberOfWords() < this.getNumberOfWords() ? this
					: bs;
			for (int k = Math.min(this.getNumberOfWords(),
					bs.getNumberOfWords()); k < Math.max(this.getNumberOfWords(),
	  					bs.getNumberOfWords()); ++k) {
				if (longer.getWord(k) != 0) {
					return false;
				}
			}
			return true;
		}
		return false;
	}

  /**
   * Check whether a bitset contains a set bit.
   *
   * @return true if no set bit is found
   */
  public boolean empty() {
    int length = this.data.limit();
    for(int k = 0; k < length; ++k)
          if (this.data.get(k) != 0) return false;
      return true;
  }

  /**
   * get value of bit i
   *
   * @param i index
   * @return value of the bit
   */
  public boolean get(final int i) {
      return (this.data.get(i / 64) & (1l << (i % 64))) != 0;
  }

  @Override
  public int hashCode() {
      int b = 31;
      long hash = 0;
      int length = this.data.limit();
      for(int k = 0; k < length; ++k) {
      	  long aData = this.data.get(k);
          hash = hash * b + aData;
      }
      return (int) hash;
  }

  /**
   * Iterate over the set bits
   *
   * @return an iterator
   */
  public IntIterator intIterator() {
      return new IntIterator() {
          @Override
          public boolean hasNext() {
              return this.i >= 0;
          }

          @Override
          public int next() {
              this.j = this.i;
              this.i = ImmutableBitSet.this.nextSetBit(this.i + 1);
              return this.j;
          }

          private int i = ImmutableBitSet.this.nextSetBit(0);

          private int j;

      };
  }

  @Override
  public Iterator<Integer> iterator() {
      return new Iterator<Integer>() {
          @Override
          public boolean hasNext() {
              return this.i >= 0;
          }

          @Override
          public Integer next() {
              this.j = this.i;
              this.i = ImmutableBitSet.this.nextSetBit(this.i + 1);
              return this.j;
          }

          @Override
          public void remove() {
          	throw new RuntimeException("Object is immutable");
          }

          private int i = ImmutableBitSet.this.nextSetBit(0);

          private int j;
      };
  }

  /**
   * Checks whether two bitsets intersect.
   *
   * @param bs other bitset
   * @return true if they have a non-empty intersection (result of AND)
   */
  public boolean intersects(BitSet bs) {
      for (int k = 0; k < Math.min(this.data.limit(), bs.data.length); ++k) {
          if ((this.data.get(k) & bs.data[k]) != 0) return true;
      }
      return false;
  }

  /**
   * Usage: for(int i=bs.nextSetBit(0); i>=0; i=bs.nextSetBit(i+1)) {
   * operate on index i here }
   *
   * @param i current set bit
   * @return next set bit or -1
   */
  public int nextSetBit(final int i) {
      int x = i / 64;
      if (x >= this.data.limit())
          return -1;
      long w = this.data.get(x);
      w >>>= (i % 64);
      if (w != 0) {
          return i + Long.numberOfTrailingZeros(w);
      }
      ++x;
      for (; x < this.data.limit(); ++x) {
          if (this.data.get(x) != 0) {
              return x
                      * 64
                      + Long.numberOfTrailingZeros(this.data.get(x));
          }
      }
      return -1;
  }

  /**
   * Usage: for(int i=bs.nextUnsetBit(0); i>=0; i=bs.nextUnsetBit(i+1))
   * { operate on index i here }
   *
   * @param i current unset bit
   * @return next unset bit or -1
   */
  public int nextUnsetBit(final int i) {
      int x = i / 64;
      if (x >= this.data.limit())
          return -1;
      long w = ~this.data.get(x);
      w >>>= (i % 64);
      if (w != 0) {
          return i + Long.numberOfTrailingZeros(w);
      }
      ++x;
      for (; x < this.data.limit(); ++x) {
          if (this.data.get(x) != ~0) {
              return x
                      * 64
                      + Long.numberOfTrailingZeros(~this.data.get(x));
          }
      }
      return -1;
  }

  /**
   * Query the size
   *
   * @return the size in bits.
   */
  public int size() {
      return this.data.limit() * 64;
  }

  /**
   * Iterate over the unset bits
   *
   * @return an iterator
   */
  public IntIterator unsetIntIterator() {
      return new IntIterator() {
          @Override
          public boolean hasNext() {
              return this.i >= 0;
          }

          @Override
          public int next() {
              this.j = this.i;
              this.i = ImmutableBitSet.this.nextUnsetBit(this.i + 1);
              return this.j;
          }

          private int i = ImmutableBitSet.this.nextUnsetBit(0);

          private int j;
      };
  }

	@Override
	public int getNumberOfWords() {
		return data.limit();
	}

	@Override
	public long getWord(int index) {
		return data.get(index);
	}
  @Override
  public String toString() {
      StringBuilder answer = new StringBuilder();
      IntIterator i = this.intIterator();
      answer.append("{");
      if (i.hasNext())
          answer.append(i.next());
      while (i.hasNext()) {
          answer.append(",");
          answer.append(i.next());
      }
      answer.append("}");
      return answer.toString();
  }

  private LongBuffer data;

}


package com.googlecode.javaewah32;

/*
 * Copyright 2009-2016, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz, Owen Kaser, Gregory Ssi-Yan-Kai, Rory Graves
 * Licensed under the Apache License, Version 2.0.
 */

/**
 * Mostly for internal use. Similar to BufferedRunningLengthWord32, but
 * automatically advances to the next BufferedRunningLengthWord32 as words are
 * discarded.
 *
 * @author Daniel Lemire and David McIntosh
 * @since 0.5.0
 */
public final class IteratingBufferedRunningLengthWord32 implements
        IteratingRLW32, Cloneable {
    /**
     * Instantiates a new iterating buffered running length word.
     *
     * @param iterator iterator
     */
    public IteratingBufferedRunningLengthWord32(
            final EWAHIterator32 iterator) {
        this.iterator = iterator;
        this.brlw = new BufferedRunningLengthWord32(
                this.iterator.next());
        this.literalWordStartPosition = this.iterator.literalWords()
                + this.brlw.literalWordOffset;
        this.buffer = this.iterator.buffer();
    }

    /**
     * Instantiates a new iterating buffered running length word.
     *
     * @param bitmap over which we want to iterate
     */
    public IteratingBufferedRunningLengthWord32(
            final EWAHCompressedBitmap32 bitmap) {
        this(EWAHIterator32.getEWAHIterator(bitmap));
    }
    /**
     * Discard first words, iterating to the next running length word if
     * needed.
     *
     * @param x the x
     */
    @Override
    public void discardFirstWords(int x) {
        while (x > 0) {
            if (this.brlw.RunningLength > x) {
                this.brlw.RunningLength -= x;
                return;
            }
            x -= this.brlw.RunningLength;
            this.brlw.RunningLength = 0;
            int toDiscard = x > this.brlw.NumberOfLiteralWords ? this.brlw.NumberOfLiteralWords
                    : x;

            this.literalWordStartPosition += toDiscard;
            this.brlw.NumberOfLiteralWords -= toDiscard;
            x -= toDiscard;
            if ((x > 0) || (this.brlw.size() == 0)) {
                if (!this.iterator.hasNext()) {
                    break;
                }
                this.brlw.reset(this.iterator.next());
                this.literalWordStartPosition = this.iterator
                        .literalWords();
            }
        }
    }

    @Override
  public void discardLiteralWords(int x) {
      this.literalWordStartPosition += x;
    this.brlw.NumberOfLiteralWords -= x;
    if (this.brlw.NumberOfLiteralWords == 0) {
      if (!this.iterator.hasNext()) {
        return;
      }
      this.brlw.reset(this.iterator.next());
      this.literalWordStartPosition = this.iterator.literalWords();
    }
  }

    @Override
    public void discardRunningWords() {
        this.brlw.RunningLength = 0;
        if (this.brlw.getNumberOfLiteralWords() == 0)
            this.next();
    }

    /**
     * Write out up to max words, returns how many were written
     *
     * @param container target for writes
     * @param max       maximal number of writes
     * @return how many written
     */
    public int discharge(BitmapStorage32 container, int max) {
      int index = 0;
      while (true) {
        if (index + getRunningLength() > max) {
          final int offset = max - index;
          container.addStreamOfEmptyWords(getRunningBit(), offset);
          this.brlw.RunningLength -= offset;
          return max;
        }
        container.addStreamOfEmptyWords(getRunningBit(), getRunningLength());
        index += getRunningLength();
        if (getNumberOfLiteralWords() + index > max) {
          final int offset = max - index;
          writeLiteralWords(offset, container);
          this.brlw.RunningLength = 0;
          this.brlw.NumberOfLiteralWords -= offset;
          this.literalWordStartPosition += offset;
          return max;
        }
        writeLiteralWords(getNumberOfLiteralWords(), container);
        index += getNumberOfLiteralWords();
        if(!next()) break;
      }
      return index;
    }

    /**
     * Write out up to max words (negated), returns how many were written
     *
     * @param container target for writes
     * @param max       maximal number of writes
     * @return how many written
     */
    public int dischargeNegated(BitmapStorage32 container, int max) {
        int index = 0;
        while ((index < max) && (size() > 0)) {
            // first run
            int pl = getRunningLength();
            if (index + pl > max) {
                pl = max - index;
            }
            container.addStreamOfEmptyWords(!getRunningBit(), pl);
            index += pl;
            int pd = getNumberOfLiteralWords();
            if (pd + index > max) {
                pd = max - index;
            }
            writeNegatedLiteralWords(pd, container);
            discardFirstWords(pl + pd);
            index += pd;
        }
        return index;
    }

    /**
     * Move to the next RunningLengthWord
     *
     * @return whether the move was possible
     */
    @Override
    public boolean next() {
        if (!this.iterator.hasNext()) {
            this.brlw.NumberOfLiteralWords = 0;
            this.brlw.RunningLength = 0;
            return false;
        }
        this.brlw.reset(this.iterator.next());
        this.literalWordStartPosition = this.iterator.literalWords(); // +
        // this.brlw.literalWordOffset
        // ==0
        return true;
    }

    /**
     * Write out the remain words, transforming them to zeroes.
     *
     * @param container target for writes
     */
    public void dischargeAsEmpty(BitmapStorage32 container) {
        while (size() > 0) {
            container.addStreamOfEmptyWords(false, size());
            discardFirstWords(size());
        }
    }

    /**
     * Write out the remaining words
     *
     * @param container target for writes
     */
    public void discharge(BitmapStorage32 container) {
        // fix the offset
        this.brlw.literalWordOffset = this.literalWordStartPosition
                - this.iterator.literalWords();
        discharge(this.brlw, this.iterator, container);
    }

    /**
     * Get the nth literal word for the current running length word
     *
     * @param index zero based index
     * @return the literal word
     */
    @Override
    public int getLiteralWordAt(int index) {
        return this.buffer.getWord(this.literalWordStartPosition + index);
    }

    /**
     * Gets the number of literal words for the current running length word.
     *
     * @return the number of literal words
     */
    @Override
    public int getNumberOfLiteralWords() {
        return this.brlw.NumberOfLiteralWords;
    }

    /**
     * Gets the running bit.
     *
     * @return the running bit
     */
    @Override
    public boolean getRunningBit() {
        return this.brlw.RunningBit;
    }

    /**
     * Gets the running length.
     *
     * @return the running length
     */
    @Override
    public int getRunningLength() {
        return this.brlw.RunningLength;
    }

    /**
     * Size in uncompressed words of the current running length word.
     *
     * @return the int
     */
    @Override
    public int size() {
        return this.brlw.size();
    }

    /**
     * write the first N literal words to the target bitmap. Does not
     * discard the words or perform iteration.
     *
     * @param numWords  number of words to be written
     * @param container where we write the data
     */
    public void writeLiteralWords(int numWords, BitmapStorage32 container) {
        container.addStreamOfLiteralWords(this.buffer,
                this.literalWordStartPosition, numWords);
    }

    /**
     * write the first N literal words (negated) to the target bitmap. Does
     * not discard the words or perform iteration.
     *
     * @param numWords  number of words to be written
     * @param container where we write the data
     */
    public void writeNegatedLiteralWords(int numWords,
                                         BitmapStorage32 container) {
        container.addStreamOfNegatedLiteralWords(this.buffer,
                this.literalWordStartPosition, numWords);
    }

    /**
     * For internal use. (One could use the non-static discharge method
     * instead, but we expect them to be slower.)
     *
     * @param initialWord the initial word
     * @param iterator    the iterator
     * @param container   the container
     */
    protected static void discharge(
            final BufferedRunningLengthWord32 initialWord,
            final EWAHIterator32 iterator, final BitmapStorage32 container) {
        BufferedRunningLengthWord32 runningLengthWord = initialWord;
        for (; ; ) {
            final int runningLength = runningLengthWord
                    .getRunningLength();
            container.addStreamOfEmptyWords(
                    runningLengthWord.getRunningBit(),
                    runningLength);
            container.addStreamOfLiteralWords(iterator.buffer(),
                    iterator.literalWords()
                            + runningLengthWord.literalWordOffset,
                    runningLengthWord.getNumberOfLiteralWords()
            );
            if (!iterator.hasNext())
                break;
            runningLengthWord = new BufferedRunningLengthWord32(
                    iterator.next());
        }
    }

    @Override
    public IteratingBufferedRunningLengthWord32 clone()
            throws CloneNotSupportedException {
        IteratingBufferedRunningLengthWord32 answer = (IteratingBufferedRunningLengthWord32) super
                .clone();
        answer.brlw = this.brlw.clone();
        answer.iterator = this.iterator.clone();
        return answer;
    }

    private BufferedRunningLengthWord32 brlw;
    private final Buffer32 buffer;
    private int literalWordStartPosition;
    private EWAHIterator32 iterator;

}


package com.googlecode.javaewah32;

/*
 * Copyright 2009-2016, Daniel Lemire, Cliff Moon, David McIntosh, Robert Becho, Google Inc., Veronika Zenz, Owen Kaser, Gregory Ssi-Yan-Kai, Rory Graves
 * Licensed under the Apache License, Version 2.0.
 */

/**
 * Mostly for internal use. Similar to RunningLengthWord, but can be modified
 * without access to the array, and has faster access.
 *
 * @author Daniel Lemire
 * @since 0.5.0
 */
public final class BufferedRunningLengthWord32 implements Cloneable {

    /**
     * Instantiates a new buffered running length word.
     *
     * @param a the word
     */
    public BufferedRunningLengthWord32(final int a) {
        this.NumberOfLiteralWords = (a >>> (1 + RunningLengthWord32.RUNNING_LENGTH_BITS));
        this.RunningBit = (a & 1) != 0;
        this.RunningLength = ((a >>> 1) & RunningLengthWord32.LARGEST_RUNNING_LENGTH_COUNT);
    }

    /**
     * Instantiates a new buffered running length word.
     *
     * @param rlw the rlw
     */
    public BufferedRunningLengthWord32(final RunningLengthWord32 rlw) {
        this(rlw.buffer.getWord(rlw.position));
    }

    /**
     * Discard first words.
     *
     * @param x the number of words to be discarded
     */
    public void discardFirstWords(int x) {
        if (this.RunningLength >= x) {
            this.RunningLength -= x;
            return;
        }
        x -= this.RunningLength;
        this.RunningLength = 0;
        this.literalWordOffset += x;
        this.NumberOfLiteralWords -= x;
    }

    /**
     * Gets the number of literal words.
     *
     * @return the number of literal words
     */
    public int getNumberOfLiteralWords() {
        return this.NumberOfLiteralWords;
    }

    /**
     * Gets the running bit.
     *
     * @return the running bit
     */
    public boolean getRunningBit() {
        return this.RunningBit;
    }

    /**
     * Gets the running length.
     *
     * @return the running length
     */
    public int getRunningLength() {
        return this.RunningLength;
    }

    /**
     * Reset the values using the provided word.
     *
     * @param a the word
     */
    public void reset(final int a) {
        this.NumberOfLiteralWords = (a >>> (1 + RunningLengthWord32.RUNNING_LENGTH_BITS));
        this.RunningBit = (a & 1) != 0;
        this.RunningLength = ((a >>> 1) & RunningLengthWord32.LARGEST_RUNNING_LENGTH_COUNT);
        this.literalWordOffset = 0;
    }

    /**
     * Reset the values of this running length word so that it has the same
     * values as the other running length word.
     *
     * @param rlw the other running length word
     */
    public void reset(final RunningLengthWord32 rlw) {
        reset(rlw.buffer.getWord(rlw.position));
    }

    /**
     * Sets the number of literal words.
     *
     * @param number the new number of literal words
     */
    public void setNumberOfLiteralWords(final int number) {
        this.NumberOfLiteralWords = number;
    }

    /**
     * Sets the running bit.
     *
     * @param b the new running bit
     */
    public void setRunningBit(final boolean b) {
        this.RunningBit = b;
    }

    /**
     * Sets the running length.
     *
     * @param number the new running length
     */
    public void setRunningLength(final int number) {
        this.RunningLength = number;
    }

    /**
     * Size in uncompressed words.
     *
     * @return the int
     */
    public int size() {
        return this.RunningLength + this.NumberOfLiteralWords;
    }

    /*
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString() {
        return "running bit = " + getRunningBit()
                + " running length = " + getRunningLength()
                + " number of lit. words " + getNumberOfLiteralWords();
    }

    @Override
    public BufferedRunningLengthWord32 clone()
            throws CloneNotSupportedException {
        BufferedRunningLengthWord32 answer = (BufferedRunningLengthWord32) super
                .clone();
        answer.literalWordOffset = this.literalWordOffset;
        answer.NumberOfLiteralWords = this.NumberOfLiteralWords;
        answer.RunningBit = this.RunningBit;
        answer.RunningLength = this.RunningLength;
        return answer;
    }

    /**
     * how many literal words have we read so far?
     */
    public int literalWordOffset = 0;

    /**
     * The Number of literal words.
     */
    protected int NumberOfLiteralWords;

    /**
     * The Running bit.
     */
    public boolean RunningBit;

    /**
     * The Running length.
     */
    public int RunningLength;

}


package com.googlecode.javaewah32.symmetric;

import com.googlecode.javaewah32.IteratingBufferedRunningLengthWord32;

/**
 * Wrapper around an IteratingBufferedRunningLengthWord used by the
 * RunningBitmapMerge class.
 *
 * @author Daniel Lemire
 * @since 0.8.2
 */
public final class EWAHPointer32 implements Comparable<EWAHPointer32> {
    private int endrun;
    private final int pos;
    private boolean isLiteral;
    private boolean value;
    private boolean dead = false;

    /**
     * Underlying iterator
     */
    public final IteratingBufferedRunningLengthWord32 iterator;

    /**
     * Construct a pointer over an IteratingBufferedRunningLengthWord.
     *
     * @param previousEndRun word where the previous run ended
     * @param rw             the iterator
     * @param pos            current position (in word)
     */
    public EWAHPointer32(final int previousEndRun,
                         final IteratingBufferedRunningLengthWord32 rw, final int pos) {
        this.pos = pos;
        this.iterator = rw;
        if (this.iterator.getRunningLength() > 0) {
            this.endrun = previousEndRun
                    + this.iterator.getRunningLength();
            this.isLiteral = false;
            this.value = this.iterator.getRunningBit();
        } else if (this.iterator.getNumberOfLiteralWords() > 0) {
            this.isLiteral = true;
            this.endrun = previousEndRun
                    + this.iterator.getNumberOfLiteralWords();
        } else {
            this.endrun = previousEndRun;
            this.dead = true;
        }
    }

    /**
     * @return the end of the current run
     */
    public int endOfRun() {
        return this.endrun;
    }

    /**
     * @return the beginning of the current run
     */
    public int beginOfRun() {
        if (this.isLiteral)
            return this.endrun
                    - this.iterator.getNumberOfLiteralWords();
        return (this.endrun - this.iterator.getRunningLength());
    }

    /**
     * Process the next run
     */
    public void parseNextRun() {
        if ((this.isLiteral)
                || (this.iterator.getNumberOfLiteralWords() == 0)) {
            // no choice, must load next runs
            this.iterator.discardFirstWords(this.iterator.size());
            if (this.iterator.getRunningLength() > 0) {
                this.endrun += this.iterator
                        .getRunningLength();
                this.isLiteral = false;
                this.value = this.iterator.getRunningBit();
            } else if (this.iterator.getNumberOfLiteralWords() > 0) {
                this.isLiteral = true;
                this.endrun += this.iterator
                        .getNumberOfLiteralWords();
            } else {
                this.dead = true;
            }

        } else {
            this.isLiteral = true;
            this.endrun += this.iterator.getNumberOfLiteralWords();
        }

    }

    /**
     * @return true if there is no more data
     */
    public boolean hasNoData() {
        return this.dead;
    }

    /**
     * @param f call the function with the current information
     */
    public void callbackUpdate(final UpdateableBitmapFunction32 f) {
        if (this.dead)
            f.setZero(this.pos);
        else if (this.isLiteral)
            f.setLiteral(this.pos);
        else if (this.value)
            f.setOne(this.pos);
        else
            f.setZero(this.pos);
    }

    @Override
    public int compareTo(EWAHPointer32 other) {
        return this.endrun - other.endrun;
    }
}


package com.michaldrabik.tapbarmenulib;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.LinearLayout;
import com.wnafee.vector.compat.ResourcesCompat;

/**
 * TapBar Menu Layout.
 *
 * @author Michal Drabik (michal.drabik0@gmail.com) on 2015-11-13.
 */
public class TapBarMenu extends LinearLayout {

  public static final int BUTTON_POSITION_LEFT = 0;
  public static final int BUTTON_POSITION_CENTER = 1;
  public static final int BUTTON_POSITION_RIGHT = 2;
  public static final int MENU_ANCHOR_BOTTOM = 3;
  public static final int MENU_ANCHOR_TOP = 4;
  private static final DecelerateInterpolator DECELERATE_INTERPOLATOR = new DecelerateInterpolator(2.5f);

  private enum State {
    OPENED,
    CLOSED
  }

  private static final int LEFT = 0;
  private static final int RIGHT = 1;
  private static final int TOP = 2;
  private static final int BOTTOM = 3;
  private static final int RADIUS = 4;

  private AnimatorSet animatorSet = new AnimatorSet();
  private ValueAnimator[] animator = new ValueAnimator[5];
  private float [] button = new float[5];

  private Path path = new Path();
  private State state = State.CLOSED;
  private Paint paint;
  private int animationDuration;
  private float width;
  private float height;
  private float buttonLeftInitial;
  private float buttonRightInitial;
  private float yPosition;
  private Drawable iconOpenedDrawable;
  private Drawable iconClosedDrawable;
  private OnClickListener onClickListener;

  //Custom XML Attributes
  private int backgroundColor;
  private int buttonSize;
  private int buttonPosition;
  private int buttonMarginRight;
  private int buttonMarginLeft;
  private int menuAnchor;
  private boolean showMenuItems;

  public TapBarMenu(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(attrs);
  }

  public TapBarMenu(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(attrs);
  }

  private void init(AttributeSet attrs) {
    setWillNotDraw(false);
    setupAttributes(attrs);
    setGravity(Gravity.CENTER);
    setupAnimators();
    setupPaint();
  }

  private void setupAttributes(AttributeSet attrs) {
    TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.TapBarMenu, 0, 0);

    if (typedArray.hasValue(R.styleable.TapBarMenu_tbm_iconOpened)) {
      iconOpenedDrawable = typedArray.getDrawable(R.styleable.TapBarMenu_tbm_iconOpened);
    } else {
      iconOpenedDrawable = ResourcesCompat.getDrawable(getContext(), R.drawable.icon_animated);
    }

    if (typedArray.hasValue(R.styleable.TapBarMenu_tbm_iconClosed)) {
      iconClosedDrawable = typedArray.getDrawable(R.styleable.TapBarMenu_tbm_iconClosed);
    } else {
      iconClosedDrawable = ResourcesCompat.getDrawable(getContext(), R.drawable.icon_close_animated);
    }

    backgroundColor = typedArray.getColor(R.styleable.TapBarMenu_tbm_backgroundColor, ContextCompat.getColor(getContext(), R.color.red));
    buttonSize =
        typedArray.getDimensionPixelSize(R.styleable.TapBarMenu_tbm_buttonSize, getResources().getDimensionPixelSize(R.dimen.defaultButtonSize));
    buttonMarginRight = typedArray.getDimensionPixelSize(R.styleable.TapBarMenu_tbm_buttonMarginRight, 0);
    buttonMarginLeft = typedArray.getDimensionPixelSize(R.styleable.TapBarMenu_tbm_buttonMarginLeft, 0);
    buttonPosition = typedArray.getInt(R.styleable.TapBarMenu_tbm_buttonPosition, BUTTON_POSITION_CENTER);
    menuAnchor = typedArray.getInt(R.styleable.TapBarMenu_tbm_menuAnchor, MENU_ANCHOR_BOTTOM);
    showMenuItems = typedArray.getBoolean(R.styleable.TapBarMenu_tbm_showItems, false);
    typedArray.recycle();
  }

  private void setupAnimators() {
    for ( int i = 0 ; i < 5 ; i++ ) {
      animator[i] = new ValueAnimator();
    }

    animator[LEFT].addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator valueAnimator) {
        button[LEFT] = (float) valueAnimator.getAnimatedValue();
      }
    });
    animator[RIGHT].addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator valueAnimator) {
        button[RIGHT] = (float) valueAnimator.getAnimatedValue();
      }
    });
    animator[TOP].addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator valueAnimator) {
        button[TOP] = (float) valueAnimator.getAnimatedValue();
      }
    });
    animator[BOTTOM].addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator valueAnimator) {
        button[BOTTOM] = (float) valueAnimator.getAnimatedValue();
      }
    });
    animator[RADIUS].addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
      @Override
      public void onAnimationUpdate(ValueAnimator valueAnimator) {
        button[RADIUS] = (float) valueAnimator.getAnimatedValue();
        invalidate();
      }
    });
    animationDuration = getResources().getInteger(R.integer.animationDuration);
    animatorSet.setDuration(animationDuration);
    animatorSet.setInterpolator(DECELERATE_INTERPOLATOR);
    animatorSet.playTogether(animator);
  }

  private void setupMenuItems() {
    for (int i = 0; i < getChildCount(); i++) {
      getChildAt(i).setVisibility(showMenuItems ? VISIBLE : GONE);
    }
  }

  private void setupPaint() {
    paint = new Paint();
    paint.setColor(backgroundColor);
    paint.setAntiAlias(true);
  }

  @Override protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    setupMenuItems();
  }

  /**
   * Opens the menu if it's closed or close it if it's opened.
   */
  public void toggle() {
    if (state == State.OPENED)
      close();
    else
      open();
  }

  /**
   * Open the menu.
   */
  public void open() {
    state = State.OPENED;
    showIcons(true);

    animator[LEFT].setFloatValues(button[LEFT], 0);
    animator[RIGHT].setFloatValues(button[RIGHT], width);
    animator[RADIUS].setFloatValues(button[RADIUS], 0);
    animator[TOP].setFloatValues(button[TOP], 0);
    animator[BOTTOM].setFloatValues(button[BOTTOM], height);

    animatorSet.cancel();
    animatorSet.start();
    if (iconOpenedDrawable instanceof Animatable) {
      ((Animatable) iconOpenedDrawable).start();
    }
    ViewGroup parentView = (ViewGroup) TapBarMenu.this.getParent();
    this.animate()
        .y(menuAnchor == MENU_ANCHOR_BOTTOM ? parentView.getBottom() - height : 0)
            .setDuration(animationDuration)
            .setInterpolator(DECELERATE_INTERPOLATOR)
            .start();
  }

  /**
   * Close the menu.
   */
  public void close() {
    updateDimensions(width, height);
    state = State.CLOSED;
    showIcons(false);

    animator[LEFT].setFloatValues(0, button[LEFT]);
    animator[RIGHT].setFloatValues(width, button[RIGHT]);
    animator[RADIUS].setFloatValues(0, button[RADIUS]);
    animator[TOP].setFloatValues(0, button[TOP]);
    animator[BOTTOM].setFloatValues(height, button[BOTTOM]);

    animatorSet.cancel();
    animatorSet.start();
    if (iconClosedDrawable instanceof Animatable) {
      ((Animatable) iconClosedDrawable).start();
    }
    this.animate()
            .y(yPosition)
            .setDuration(animationDuration)
            .setInterpolator(DECELERATE_INTERPOLATOR)
            .start();
  }

  /**
   * @return True if menu is opened. False otherwise.
   */
  public boolean isOpened() {
    return state == State.OPENED;
  }

  /**
   * Sets TapBarMenu's background color from given resource.
   * @param colorResId Color resource id. For example: R.color.holo_blue_light
   */
  public void setMenuBackgroundColor(int colorResId) {
    backgroundColor = ContextCompat.getColor(getContext(), colorResId);
    paint.setColor(backgroundColor);
    invalidate();
  }

  /**
   * Set position of 'Open Menu' button.
   * @param position One of: {@link #BUTTON_POSITION_CENTER}, {@link #BUTTON_POSITION_LEFT}, {@link #BUTTON_POSITION_RIGHT}.
   */
  public void setButtonPosition(int position) {
    buttonPosition = position;
    invalidate();
  }

  /**
   * Sets diameter of 'Open Menu' button.
   * @param size Diameter in pixels.
   */
  public void setButtonSize(int size) {
    buttonSize = size;
    invalidate();
  }

  /**
   * Sets left margin for 'Open Menu' button.
   * @param margin Left margin in pixels
   */
  public void setButtonMarginLeft(int margin) {
    buttonMarginLeft = margin;
  }

  /**
   * Sets right margin for 'Open Menu' button.
   * @param margin Right margin in pixels
   */
  public void setButtonMarginRight(int margin) {
    buttonMarginRight = margin;
  }

  /**
   * Set anchor point of the menu. Can be either bottom or top.
   * @param anchor One of: {@link #MENU_ANCHOR_BOTTOM}, {@link #MENU_ANCHOR_TOP}.
   */
  public void setAnchor(int anchor) {
    menuAnchor = anchor;
  }

  /**
  * Sets the passed drawable as the drawable to be used in the open state.
  * @param openDrawable The open state drawable
  * */
  public void setIconOpenDrawable(Drawable openDrawable) {
    this.iconOpenedDrawable = openDrawable;
    invalidate();
  }

  /**
  * Sets the passed drawable as the drawable to be used in the closed state.
  * @param closeDrawable The closed state drawable
  * */
  public void setIconCloseDrawable(Drawable closeDrawable) {
    this.iconClosedDrawable = closeDrawable;
    invalidate();
  }

  /**
  * Sets the passed drawable as the drawable to be used in the open state.
  * @param openDrawable The open state drawable
  * */
  public void setIconOpenedDrawable(Drawable openDrawable) {
    this.iconOpenedDrawable = openDrawable;
    invalidate();
  }

  /**
  * Sets the passed drawable as the drawable to be used in the closed state.
  * @param closeDrawable The closed state drawable
  * */
  public void setIconClosedDrawable(Drawable closeDrawable) {
    this.iconClosedDrawable = closeDrawable;
    invalidate();
  }

  @Override public void setOnClickListener(OnClickListener listener) {
    onClickListener = listener;
  }

  @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    updateDimensions(w, h);
    yPosition = getY();
  }

  @Override protected void onDraw(Canvas canvas) {
    canvas.drawPath(createRoundedRectPath(button[LEFT], button[TOP], button[RIGHT], button[BOTTOM], button[RADIUS], button[RADIUS]), paint);
    if (state == State.CLOSED) {
      iconClosedDrawable.draw(canvas);
    } else {
      iconOpenedDrawable.draw(canvas);
    }
  }

  private void updateDimensions(float w, float h) {
    int ratio;
    width = w;
    height = h;

    button[RADIUS] = buttonSize;
    setButtonPosition(width);
    if (iconClosedDrawable instanceof Animatable) {
      ratio = 3;
    } else {
      ratio = 5;
    }
    float iconLeft = button[LEFT] + buttonSize / ratio;
    float iconTop = (height - buttonSize) / 2 + buttonSize / ratio;

    float iconRight = button[RIGHT] - buttonSize / ratio;
    float iconBottom = (height + buttonSize) / 2 - buttonSize / ratio;
    iconOpenedDrawable.setBounds((int) iconLeft, (int) iconTop, (int) iconRight, (int) iconBottom);
    iconClosedDrawable.setBounds((int) iconLeft, (int) iconTop, (int) iconRight, (int) iconBottom);
  }

  private void setButtonPosition(float width) {
    if (buttonPosition == BUTTON_POSITION_CENTER) {
      button[LEFT] = ((width / 2) - (buttonSize / 2));
    } else if (buttonPosition == BUTTON_POSITION_LEFT) {
      button[LEFT] = 0;
    } else {
      button[LEFT] = width - buttonSize;
    }
    int padding = buttonMarginLeft - buttonMarginRight;
    button[LEFT] += padding;
    button[RIGHT] = button[LEFT] + buttonSize;
    button[TOP] = (height - buttonSize) / 2;
    button[BOTTOM] = (height + buttonSize) / 2;
    buttonLeftInitial = button[LEFT];
    buttonRightInitial = button[RIGHT];
  }

  private void showIcons(final boolean show) {
    for (int i = 0; i < getChildCount(); i++) {
      final View view = getChildAt(i);
      int translation = menuAnchor == MENU_ANCHOR_BOTTOM ? view.getHeight() : -view.getHeight();
      view.setTranslationY(show ? translation : 0f);
      view.setScaleX(show ? 0f : 1f);
      view.setScaleY(show ? 0f : 1f);
      view.setVisibility(VISIBLE);
      view.setAlpha(show ? 0f : 1f);
      view.animate()
          .scaleX(show ? 1f : 0f)
          .scaleY(show ? 1f : 0f)
          .translationY(0f)
          .alpha(show ? 1f : 0f)
          .setInterpolator(DECELERATE_INTERPOLATOR)
          .setDuration(show ? animationDuration / 2 : animationDuration / 3)
          .setStartDelay(show ? animationDuration / 3 : 0)
          .setListener(new AnimatorListenerAdapter() {
            @Override public void onAnimationEnd(Animator animation) {
              super.onAnimationEnd(animation);
              view.setVisibility(show ? VISIBLE : GONE);
            }
          })
          .start();
    }
  }

  private Path createRoundedRectPath(float left, float top, float right, float bottom, float rx, float ry) {
    path.reset();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      return createRoundedRectPathApi21(path, left, top, right, bottom, rx, ry);
    } else {
      return createRoundedRectPathPreApi21(path, left, top, right, bottom, rx, ry);
    }
  }

  @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  private Path createRoundedRectPathApi21(Path path, float left, float top, float right, float bottom, float rx, float ry) {
    if (rx < 0) rx = 0;
    if (ry < 0) ry = 0;
    float width = right - left;
    float height = bottom - top;
    if (rx > width / 2) rx = width / 2;
    if (ry > height / 2) ry = height / 2;
    float widthMinusCorners = (width - (2 * rx));
    float heightMinusCorners = (height - (2 * ry));
    path.moveTo(right, top + ry);
    path.arcTo(right - 2 * rx, top, right, top + 2 * ry, 0, -90, false);
    path.rLineTo(-widthMinusCorners, 0);
    path.arcTo(left, top, left + 2 * rx, top + 2 * ry, 270, -90, false);
    path.rLineTo(0, heightMinusCorners);
    path.arcTo(left, bottom - 2 * ry, left + 2 * rx, bottom, 180, -90, false);
    path.rLineTo(widthMinusCorners, 0);
    path.arcTo(right - 2 * rx, bottom - 2 * ry, right, bottom, 90, -90, false);
    path.rLineTo(0, -heightMinusCorners);
    path.close();
    return path;
  }

  private Path createRoundedRectPathPreApi21(Path path, float left, float top, float right, float bottom, float rx, float ry) {
    if (rx < 0) rx = 0;
    if (ry < 0) ry = 0;
    float width = right - left;
    float height = bottom - top;
    if (rx > width / 2) rx = width / 2;
    if (ry > height / 2) ry = height / 2;
    float widthMinusCorners = (width - (2 * rx));
    float heightMinusCorners = (height - (2 * ry));
    path.moveTo(right, top + ry);
    path.rQuadTo(0, -ry, -rx, -ry);
    path.rLineTo(-widthMinusCorners, 0);
    path.rQuadTo(-rx, 0, -rx, ry);
    path.rLineTo(0, heightMinusCorners);
    path.rQuadTo(0, ry, rx, ry);
    path.rLineTo(widthMinusCorners, 0);
    path.rQuadTo(rx, 0, rx, -ry);
    path.rLineTo(0, -heightMinusCorners);
    path.close();
    return path;
  }

  @Override public boolean onInterceptTouchEvent(MotionEvent event) {
    return (event.getX() > buttonLeftInitial && event.getX() < buttonRightInitial);
  }

  @Override public boolean onTouchEvent(@NonNull MotionEvent event) {
    if ((event.getX() > buttonLeftInitial && event.getX() < buttonRightInitial) && (event.getAction() == MotionEvent.ACTION_UP)) {
      if (onClickListener != null) {
        onClickListener.onClick(this);
      }
    }
    return true;
  }

  @Override protected void onDetachedFromWindow() {
    onDestroy();
    super.onDetachedFromWindow();
  }

  private void onDestroy() {
    iconOpenedDrawable = null;
    iconClosedDrawable = null;
    for ( int i = 0 ; i < 5 ; i ++ ){
      animator[i] = null;
    }
    animator = null;
    button = null;
    onClickListener = null;
  }
}


package com.hankcs.lucene;

import org.apache.lucene.util.ArrayUtil;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

import static org.apache.lucene.util.RamUsageEstimator.NUM_BYTES_CHAR;

/**
 * 抄袭lucene的英文处理
 * Stemmer, implementing the Porter Stemming Algorithm
 * <p/>
 * The Stemmer class transforms a word into its root form. The input word can be
 * provided a character at time (by calling add()), or at once by calling one of
 * the various stem(something) methods.
 */

public class PorterStemmer
{
    private char[] b;
    private int i, /* offset into b */
            j, k, k0;
    private boolean dirty = false;
    private static final int INITIAL_SIZE = 50;

    public PorterStemmer()
    {
        b = new char[INITIAL_SIZE];
        i = 0;
    }

    /**
     * reset() resets the stemmer so it can stem another word. If you invoke the
     * stemmer by calling add(char) and then stem(), you must call reset()
     * before starting another word.
     */
    public void reset()
    {
        i = 0;
        dirty = false;
    }

    /**
     * Add a character to the word being stemmed. When you are finished adding
     * characters, you can call stem(void) to process the word.
     */
    public void add(char ch)
    {
        if (b.length <= i)
        {
            b = ArrayUtil.grow(b, i + 1);
        }
        b[i++] = ch;
    }

    /**
     * After a word has been stemmed, it can be retrieved by toString(), or a
     * reference to the internal buffer can be retrieved by getResultBuffer and
     * getResultLength (which is generally more efficient.)
     */
    @Override
    public String toString()
    {
        return new String(b, 0, i);
    }

    /**
     * Returns the length of the word resulting from the stemming process.
     */
    public int getResultLength()
    {
        return i;
    }

    /**
     * Returns a reference to a character buffer containing the results of the
     * stemming process. You also need to consult getResultLength() to determine
     * the length of the result.
     */
    public char[] getResultBuffer()
    {
        return b;
    }

    /* cons(i) is true <=> b[i] is a consonant. */

    private boolean cons(int i)
    {
        switch (b[i])
        {
            case 'a':
            case 'e':
            case 'i':
            case 'o':
            case 'u':
                return false;
            case 'y':
                return (i == k0) || !cons(i - 1);
            default:
                return true;
        }
    }

    /*
     * m() measures the number of consonant sequences between k0 and j. if c is
     * a consonant sequence and v a vowel sequence, and <..> indicates arbitrary
     * presence,
     *
     * <c><v> gives 0 <c>vc<v> gives 1 <c>vcvc<v> gives 2 <c>vcvcvc<v> gives 3
     * ....
     */

    private int m()
    {
        int n = 0;
        int i = k0;
        while (true)
        {
            if (i > j) return n;
            if (!cons(i)) break;
            i++;
        }
        i++;
        while (true)
        {
            while (true)
            {
                if (i > j) return n;
                if (cons(i)) break;
                i++;
            }
            i++;
            n++;
            while (true)
            {
                if (i > j) return n;
                if (!cons(i)) break;
                i++;
            }
            i++;
        }
    }

    /* vowelinstem() is true <=> k0,...j contains a vowel */

    private boolean vowelinstem()
    {
        int i;
        for (i = k0; i <= j; i++)
            if (!cons(i)) return true;
        return false;
    }

    /* doublec(j) is true <=> j,(j-1) contain a double consonant. */

    private boolean doublec(int j)
    {
        if (j < k0 + 1) return false;
        if (b[j] != b[j - 1]) return false;
        return cons(j);
    }

    /*
     * cvc(i) is true <=> i-2,i-1,i has the form consonant - vowel - consonant
     * and also if the second c is not w,x or y. this is used when trying to
     * restore an e at the end of a short word. e.g.
     *
     * cav(e), lov(e), hop(e), crim(e), but snow, box, tray.
     */

    private boolean cvc(int i)
    {
        if (i < k0 + 2 || !cons(i) || cons(i - 1) || !cons(i - 2)) return false;
        else
        {
            int ch = b[i];
            if (ch == 'w' || ch == 'x' || ch == 'y') return false;
        }
        return true;
    }

    private boolean ends(String s)
    {
        int l = s.length();
        int o = k - l + 1;
        if (o < k0) return false;
        for (int i = 0; i < l; i++)
            if (b[o + i] != s.charAt(i)) return false;
        j = k - l;
        return true;
    }

    /*
     * setto(s) sets (j+1),...k to the characters in the string s, readjusting
     * k.
     */

    void setto(String s)
    {
        int l = s.length();
        int o = j + 1;
        for (int i = 0; i < l; i++)
            b[o + i] = s.charAt(i);
        k = j + l;
        dirty = true;
    }

    /* r(s) is used further down. */

    void r(String s)
    {
        if (m() > 0) setto(s);
    }

    /*
     * step1() gets rid of plurals and -ed or -ing. e.g.
     *
     * caresses -> caress ponies -> poni ties -> ti caress -> caress cats -> cat
     *
     * feed -> feed agreed -> agree disabled -> disable
     *
     * matting -> mat mating -> mate meeting -> meet milling -> mill messing ->
     * mess
     *
     * meetings -> meet
     */

    private void step1()
    {
        if (b[k] == 's')
        {
            if (ends("sses")) k -= 2;
            else if (ends("ies")) setto("i");
            else if (b[k - 1] != 's') k--;
        }
        if (ends("eed"))
        {
            if (m() > 0) k--;
        }
        else if ((ends("ed") || ends("ing")) && vowelinstem())
        {
            k = j;
            if (ends("at")) setto("ate");
            else if (ends("bl")) setto("ble");
            else if (ends("iz")) setto("ize");
            else if (doublec(k))
            {
                int ch = b[k--];
                if (ch == 'l' || ch == 's' || ch == 'z') k++;
            }
            else if (m() == 1 && cvc(k)) setto("e");
        }
    }

    /* step2() turns terminal y to i when there is another vowel in the stem. */

    private void step2()
    {
        if (ends("y") && vowelinstem())
        {
            b[k] = 'i';
            dirty = true;
        }
    }

    /*
     * step3() maps double suffices to single ones. so -ization ( = -ize plus
     * -ation) maps to -ize etc. note that the string before the suffix must
     * give m() > 0.
     */

    private void step3()
    {
        if (k == k0) return; /* For Bug 1 */
        switch (b[k - 1])
        {
            case 'a':
                if (ends("ational"))
                {
                    r("ate");
                    break;
                }
                if (ends("tional"))
                {
                    r("tion");
                    break;
                }
                break;
            case 'c':
                if (ends("enci"))
                {
                    r("ence");
                    break;
                }
                if (ends("anci"))
                {
                    r("ance");
                    break;
                }
                break;
            case 'e':
                if (ends("izer"))
                {
                    r("ize");
                    break;
                }
                break;
            case 'l':
                if (ends("bli"))
                {
                    r("ble");
                    break;
                }
                if (ends("alli"))
                {
                    r("al");
                    break;
                }
                if (ends("entli"))
                {
                    r("ent");
                    break;
                }
                if (ends("eli"))
                {
                    r("e");
                    break;
                }
                if (ends("ousli"))
                {
                    r("ous");
                    break;
                }
                break;
            case 'o':
                if (ends("ization"))
                {
                    r("ize");
                    break;
                }
                if (ends("ation"))
                {
                    r("ate");
                    break;
                }
                if (ends("ator"))
                {
                    r("ate");
                    break;
                }
                break;
            case 's':
                if (ends("alism"))
                {
                    r("al");
                    break;
                }
                if (ends("iveness"))
                {
                    r("ive");
                    break;
                }
                if (ends("fulness"))
                {
                    r("ful");
                    break;
                }
                if (ends("ousness"))
                {
                    r("ous");
                    break;
                }
                break;
            case 't':
                if (ends("aliti"))
                {
                    r("al");
                    break;
                }
                if (ends("iviti"))
                {
                    r("ive");
                    break;
                }
                if (ends("biliti"))
                {
                    r("ble");
                    break;
                }
                break;
            case 'g':
                if (ends("logi"))
                {
                    r("log");
                    break;
                }
        }
    }

    /* step4() deals with -ic-, -full, -ness etc. similar strategy to step3. */

    private void step4()
    {
        switch (b[k])
        {
            case 'e':
                if (ends("icate"))
                {
                    r("ic");
                    break;
                }
                if (ends("ative"))
                {
                    r("");
                    break;
                }
                if (ends("alize"))
                {
                    r("al");
                    break;
                }
                break;
            case 'i':
                if (ends("iciti"))
                {
                    r("ic");
                    break;
                }
                break;
            case 'l':
                if (ends("ical"))
                {
                    r("ic");
                    break;
                }
                if (ends("ful"))
                {
                    r("");
                    break;
                }
                break;
            case 's':
                if (ends("ness"))
                {
                    r("");
                    break;
                }
                break;
        }
    }

    /* step5() takes off -ant, -ence etc., in context <c>vcvc<v>. */

    private void step5()
    {
        if (k == k0) return; /* for Bug 1 */
        switch (b[k - 1])
        {
            case 'a':
                if (ends("al")) break;
                return;
            case 'c':
                if (ends("ance")) break;
                if (ends("ence")) break;
                return;
            case 'e':
                if (ends("er")) break;
                return;
            case 'i':
                if (ends("ic")) break;
                return;
            case 'l':
                if (ends("able")) break;
                if (ends("ible")) break;
                return;
            case 'n':
                if (ends("ant")) break;
                if (ends("ement")) break;
                if (ends("ment")) break;
                /* element etc. not stripped before the m */
                if (ends("ent")) break;
                return;
            case 'o':
                if (ends("ion") && j >= 0 && (b[j] == 's' || b[j] == 't')) break;
                /* j >= 0 fixes Bug 2 */
                if (ends("ou")) break;
                return;
            /* takes care of -ous */
            case 's':
                if (ends("ism")) break;
                return;
            case 't':
                if (ends("ate")) break;
                if (ends("iti")) break;
                return;
            case 'u':
                if (ends("ous")) break;
                return;
            case 'v':
                if (ends("ive")) break;
                return;
            case 'z':
                if (ends("ize")) break;
                return;
            default:
                return;
        }
        if (m() > 1) k = j;
    }

    /* step6() removes a final -e if m() > 1. */

    private void step6()
    {
        j = k;
        if (b[k] == 'e')
        {
            int a = m();
            if (a > 1 || a == 1 && !cvc(k - 1)) k--;
        }
        if (b[k] == 'l' && doublec(k) && m() > 1) k--;
    }

    /**
     * Stem a word provided as a String. Returns the result as a String.
     */
    public String stem(String s)
    {
        if (stem(s.toCharArray(), s.length())) return toString();
        else return s;
    }

    /**
     * Stem a word contained in a char[]. Returns true if the stemming process
     * resulted in a word different from the input. You can retrieve the result
     * with getResultLength()/getResultBuffer() or toString().
     */
    public boolean stem(char[] word)
    {
        return stem(word, word.length);
    }

    /**
     * Stem a word contained in a portion of a char[] array. Returns true if the
     * stemming process resulted in a word different from the input. You can
     * retrieve the result with getResultLength()/getResultBuffer() or
     * toString().
     */
    public boolean stem(char[] wordBuffer, int offset, int wordLen)
    {
        reset();
        if (b.length < wordLen)
        {
            b = new char[ArrayUtil.oversize(wordLen, NUM_BYTES_CHAR)];
        }
        System.arraycopy(wordBuffer, offset, b, 0, wordLen);
        i = wordLen;
        return stem(0);
    }

    /**
     * Stem a word contained in a leading portion of a char[] array. Returns
     * true if the stemming process resulted in a word different from the input.
     * You can retrieve the result with getResultLength()/getResultBuffer() or
     * toString().
     */
    public boolean stem(char[] word, int wordLen)
    {
        return stem(word, 0, wordLen);
    }

    /**
     * Stem the word placed into the Stemmer buffer through calls to add().
     * Returns true if the stemming process resulted in a word different from
     * the input. You can retrieve the result with
     * getResultLength()/getResultBuffer() or toString().
     */
    public boolean stem()
    {
        return stem(0);
    }

    public boolean stem(int i0)
    {
        k = i - 1;
        k0 = i0;
        if (k > k0 + 1)
        {
            step1();
            step2();
            step3();
            step4();
            step5();
            step6();
        }
        // Also, a word is considered dirty if we lopped off letters
        // Thanks to Ifigenia Vairelles for pointing this out.
        if (i != k + 1) dirty = true;
        i = k + 1;
        return dirty;
    }

    /**
     * Test program for demonstrating the Stemmer. It reads a file and stems
     * each word, writing the result to standard out. Usage: Stemmer file-name
     */
    public static void main(String[] args)
    {
        PorterStemmer s = new PorterStemmer();

        for (String arg : args)
        {
            try (InputStream in = new FileInputStream(arg))
            {
                byte[] buffer = new byte[1024];
                int bufferLen, offset, ch;

                bufferLen = in.read(buffer);
                offset = 0;
                s.reset();

                while (true)
                {
                    if (offset < bufferLen) ch = buffer[offset++];
                    else
                    {
                        bufferLen = in.read(buffer);
                        offset = 0;
                        if (bufferLen < 0) ch = -1;
                        else ch = buffer[offset++];
                    }

                    if (Character.isLetter((char) ch))
                    {
                        s.add(Character.toLowerCase((char) ch));
                    }
                    else
                    {
                        s.stem();
                        System.out.print(s.toString());
                        s.reset();
                        if (ch < 0) break;
                        else
                        {
                            System.out.print((char) ch);
                        }
                    }
                }
            }
            catch (IOException e)
            {
                System.out.println("error reading " + arg);
            }
        }
    }

}


package com.hankcs.lucene;

import com.hankcs.hanlp.HanLP;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.Tokenizer;

import java.util.Set;

public class HanLPAnalyzer extends Analyzer
{
    private boolean enablePorterStemming;
    private Set<String> filter;

    /**
     * @param filter    停用词
     * @param enablePorterStemming 是否分析词干（仅限英文）
     */
    public HanLPAnalyzer(Set<String> filter, boolean enablePorterStemming)
    {
        this.filter = filter;
        this.enablePorterStemming = enablePorterStemming;
    }

    /**
     * @param enablePorterStemming 是否分析词干.进行单复数,时态的转换
     */
    public HanLPAnalyzer(boolean enablePorterStemming)
    {
        this.enablePorterStemming = enablePorterStemming;
    }

    public HanLPAnalyzer()
    {
        super();
    }

    /**
     * 重载Analyzer接口，构造分词组件
     */
    @Override
    protected TokenStreamComponents createComponents(String fieldName)
    {
        Tokenizer tokenizer = new HanLPTokenizer(HanLP.newSegment().enableOffset(true), filter, enablePorterStemming);
        return new TokenStreamComponents(tokenizer);
    }
}