001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      https://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.commons.lang3.util;
018
019import java.io.Serializable;
020import java.util.BitSet;
021import java.util.Objects;
022import java.util.stream.IntStream;
023
024/**
025 * A fluent {@link BitSet} with additional operations.
026 * <p>
027 * Originally from Apache Commons VFS with more added to act as a fluent replacement for {@link java.util.BitSet}.
028 * </p>
029 *
030 * @since 3.13.0
031 */
032public final class FluentBitSet implements Cloneable, Serializable {
033
034    private static final long serialVersionUID = 1L;
035
036    /**
037     * Working BitSet.
038     */
039    private final BitSet bitSet;
040
041    /**
042     * Creates a new bit set. All bits are initially {@code false}.
043     */
044    public FluentBitSet() {
045        this(new BitSet());
046    }
047
048    /**
049     * Creates a new instance for the given bit set.
050     *
051     * @param set The bit set to wrap.
052     */
053    public FluentBitSet(final BitSet set) {
054        this.bitSet = Objects.requireNonNull(set, "set");
055    }
056
057    /**
058     * Creates a bit set whose initial size is large enough to explicitly represent bits with indices in the range {@code 0}
059     * through {@code nbits-1}. All bits are initially {@code false}.
060     *
061     * @param nbits the initial size of the bit set.
062     * @throws NegativeArraySizeException if the specified initial size is negative.
063     */
064    public FluentBitSet(final int nbits) {
065        this(new BitSet(nbits));
066    }
067
068    /**
069     * Performs a logical <strong>AND</strong> of this target bit set with the argument bit set. This bit set is modified so that each
070     * bit in it has the value {@code true} if and only if it both initially had the value {@code true} and the
071     * corresponding bit in the bit set argument also had the value {@code true}.
072     *
073     * @param set a bit set.
074     * @return {@code this} instance.
075     */
076    public FluentBitSet and(final BitSet set) {
077        bitSet.and(set);
078        return this;
079    }
080
081    /**
082     * Performs a logical <strong>AND</strong> of this target bit set with the argument bit set. This bit set is modified so that each
083     * bit in it has the value {@code true} if and only if it both initially had the value {@code true} and the
084     * corresponding bit in the bit set argument also had the value {@code true}.
085     *
086     * @param set a bit set.
087     * @return {@code this} instance.
088     */
089    public FluentBitSet and(final FluentBitSet set) {
090        bitSet.and(set.bitSet);
091        return this;
092    }
093
094    /**
095     * Clears all of the bits in this {@link BitSet} whose corresponding bit is set in the specified {@link BitSet}.
096     *
097     * @param set the {@link BitSet} with which to mask this {@link BitSet}.
098     * @return {@code this} instance.
099     */
100    public FluentBitSet andNot(final BitSet set) {
101        bitSet.andNot(set);
102        return this;
103    }
104
105    /**
106     * Clears all of the bits in this {@link BitSet} whose corresponding bit is set in the specified {@link BitSet}.
107     *
108     * @param set the {@link BitSet} with which to mask this {@link BitSet}.
109     * @return {@code this} instance.
110     */
111    public FluentBitSet andNot(final FluentBitSet set) {
112        this.bitSet.andNot(set.bitSet);
113        return this;
114    }
115
116    /**
117     * Gets the wrapped bit set.
118     *
119     * @return the wrapped bit set.
120     */
121    public BitSet bitSet() {
122        return bitSet;
123    }
124
125    /**
126     * Returns the number of bits set to {@code true} in this {@link BitSet}.
127     *
128     * @return the number of bits set to {@code true} in this {@link BitSet}.
129     */
130    public int cardinality() {
131        return bitSet.cardinality();
132    }
133
134    /**
135     * Sets all of the bits in this BitSet to {@code false}.
136     *
137     * @return {@code this} instance.
138     */
139    public FluentBitSet clear() {
140        bitSet.clear();
141        return this;
142    }
143
144    /**
145     * Sets the bits specified by the indexes to {@code false}.
146     *
147     * @param bitIndexArray the index of the bit to be cleared.
148     * @throws IndexOutOfBoundsException if the specified index is negative.
149     * @return {@code this} instance.
150     */
151    public FluentBitSet clear(final int... bitIndexArray) {
152        for (final int e : bitIndexArray) {
153            this.bitSet.clear(e);
154        }
155        return this;
156    }
157
158    /**
159     * Sets the bit specified by the index to {@code false}.
160     *
161     * @param bitIndex the index of the bit to be cleared.
162     * @throws IndexOutOfBoundsException if the specified index is negative.
163     * @return {@code this} instance.
164     */
165    public FluentBitSet clear(final int bitIndex) {
166        bitSet.clear(bitIndex);
167        return this;
168    }
169
170    /**
171     * Sets the bits from the specified {@code fromIndex} (inclusive) to the specified {@code toIndex} (exclusive) to
172     * {@code false}.
173     *
174     * @param fromIndex index of the first bit to be cleared.
175     * @param toIndex index after the last bit to be cleared.
176     * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or
177     *         {@code fromIndex} is larger than {@code toIndex}.
178     * @return {@code this} instance.
179     */
180    public FluentBitSet clear(final int fromIndex, final int toIndex) {
181        bitSet.clear(fromIndex, toIndex);
182        return this;
183    }
184
185    /**
186     * Cloning this {@link BitSet} produces a new {@link BitSet} that is equal to it. The clone of the bit set is another
187     * bit set that has exactly the same bits set to {@code true} as this bit set.
188     *
189     * @return a clone of this bit set
190     * @see #size()
191     */
192    @Override
193    public Object clone() {
194        return new FluentBitSet((BitSet) bitSet.clone());
195    }
196
197    @Override
198    public boolean equals(final Object obj) {
199        if (this == obj) {
200            return true;
201        }
202        if (!(obj instanceof FluentBitSet)) {
203            return false;
204        }
205        final FluentBitSet other = (FluentBitSet) obj;
206        return Objects.equals(bitSet, other.bitSet);
207    }
208
209    /**
210     * Sets the bit at the specified index to the complement of its current value.
211     *
212     * @param bitIndex the index of the bit to flip.
213     * @throws IndexOutOfBoundsException if the specified index is negative.
214     * @return {@code this} instance.
215     */
216    public FluentBitSet flip(final int bitIndex) {
217        bitSet.flip(bitIndex);
218        return this;
219    }
220
221    /**
222     * Sets each bit from the specified {@code fromIndex} (inclusive) to the specified {@code toIndex} (exclusive) to the
223     * complement of its current value.
224     *
225     * @param fromIndex index of the first bit to flip.
226     * @param toIndex index after the last bit to flip.
227     * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or
228     *         {@code fromIndex} is larger than {@code toIndex}.
229     * @return {@code this} instance.
230     */
231    public FluentBitSet flip(final int fromIndex, final int toIndex) {
232        bitSet.flip(fromIndex, toIndex);
233        return this;
234    }
235
236    /**
237     * Gets the value of the bit with the specified index. The value is {@code true} if the bit with the index
238     * {@code bitIndex} is currently set in this {@link BitSet}; otherwise, the result is {@code false}.
239     *
240     * @param bitIndex the bit index.
241     * @return the value of the bit with the specified index.
242     * @throws IndexOutOfBoundsException if the specified index is negative.
243     */
244    public boolean get(final int bitIndex) {
245        return bitSet.get(bitIndex);
246    }
247
248    /**
249     * Gets a new {@link BitSet} composed of bits from this {@link BitSet} from {@code fromIndex} (inclusive) to
250     * {@code toIndex} (exclusive).
251     *
252     * @param fromIndex index of the first bit to include.
253     * @param toIndex index after the last bit to include.
254     * @return a new {@link BitSet} from a range of this {@link BitSet}.
255     * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or
256     *         {@code fromIndex} is larger than {@code toIndex}.
257     */
258    public FluentBitSet get(final int fromIndex, final int toIndex) {
259        return new FluentBitSet(bitSet.get(fromIndex, toIndex));
260    }
261
262    @Override
263    public int hashCode() {
264        return bitSet.hashCode();
265    }
266
267    /**
268     * Returns true if the specified {@link BitSet} has any bits set to {@code true} that are also set to {@code true} in
269     * this {@link BitSet}.
270     *
271     * @param set {@link BitSet} to intersect with.
272     * @return boolean indicating whether this {@link BitSet} intersects the specified {@link BitSet}.
273     */
274    public boolean intersects(final BitSet set) {
275        return bitSet.intersects(set);
276    }
277
278    /**
279     * Returns true if the specified {@link BitSet} has any bits set to {@code true} that are also set to {@code true} in
280     * this {@link BitSet}.
281     *
282     * @param set {@link BitSet} to intersect with.
283     * @return boolean indicating whether this {@link BitSet} intersects the specified {@link BitSet}.
284     */
285    public boolean intersects(final FluentBitSet set) {
286        return bitSet.intersects(set.bitSet);
287    }
288
289    /**
290     * Tests whether if this {@link BitSet} contains no bits that are set to {@code true}.
291     *
292     * @return boolean indicating whether this {@link BitSet} is empty.
293     */
294    public boolean isEmpty() {
295        return bitSet.isEmpty();
296    }
297
298    /**
299     * Returns the "logical size" of this {@link BitSet}: the index of the highest set bit in the {@link BitSet} plus one.
300     * Returns zero if the {@link BitSet} contains no set bits.
301     *
302     * @return the logical size of this {@link BitSet}.
303     */
304    public int length() {
305        return bitSet.length();
306    }
307
308    /**
309     * Returns the index of the first bit that is set to {@code false} that occurs on or after the specified starting index.
310     *
311     * @param fromIndex the index to start checking from (inclusive).
312     * @return the index of the next clear bit.
313     * @throws IndexOutOfBoundsException if the specified index is negative.
314     */
315    public int nextClearBit(final int fromIndex) {
316        return bitSet.nextClearBit(fromIndex);
317    }
318
319    /**
320     * Returns the index of the first bit that is set to {@code true} that occurs on or after the specified starting index.
321     * If no such bit exists then {@code -1} is returned.
322     * <p>
323     * To iterate over the {@code true} bits in a {@link BitSet}, use the following loop:
324     * </p>
325     *
326     * <pre>
327     * {@code
328     * for (int i = bs.nextSetBit(0); i >= 0; i = bs.nextSetBit(i+1)) {
329     *     // operate on index i here
330     *     if (i == Integer.MAX_VALUE) {
331     *         break; // or (i+1) would overflow
332     *     }
333     * }}
334     * </pre>
335     *
336     * @param fromIndex the index to start checking from (inclusive).
337     * @return the index of the next set bit, or {@code -1} if there is no such bit.
338     * @throws IndexOutOfBoundsException if the specified index is negative.
339     */
340    public int nextSetBit(final int fromIndex) {
341        return bitSet.nextSetBit(fromIndex);
342    }
343
344    /**
345     * Performs a logical <strong>OR</strong> of this bit set with the bit set argument. This bit set is modified so that a bit in it
346     * has the value {@code true} if and only if it either already had the value {@code true} or the corresponding bit in
347     * the bit set argument has the value {@code true}.
348     *
349     * @param set a bit set.
350     * @return {@code this} instance.
351     */
352    public FluentBitSet or(final BitSet set) {
353        bitSet.or(set);
354        return this;
355    }
356
357    /**
358     * Performs a logical <strong>OR</strong> of this bit set with the bit set arguments. This bit set is modified so that a bit in it
359     * has the value {@code true} if and only if it either already had the value {@code true} or the corresponding bit in
360     * the bit set argument has the value {@code true}.
361     *
362     * @param set a bit set.
363     * @return {@code this} instance.
364     */
365    public FluentBitSet or(final FluentBitSet... set) {
366        for (final FluentBitSet e : set) {
367            this.bitSet.or(e.bitSet);
368        }
369        return this;
370    }
371
372    /**
373     * Performs a logical <strong>OR</strong> of this bit set with the bit set argument. This bit set is modified so that a bit in it
374     * has the value {@code true} if and only if it either already had the value {@code true} or the corresponding bit in
375     * the bit set argument has the value {@code true}.
376     *
377     * @param set a bit set.
378     * @return {@code this} instance.
379     */
380    public FluentBitSet or(final FluentBitSet set) {
381        this.bitSet.or(set.bitSet);
382        return this;
383    }
384
385    /**
386     * Returns the index of the nearest bit that is set to {@code false} that occurs on or before the specified starting
387     * index. If no such bit exists, or if {@code -1} is given as the starting index, then {@code -1} is returned.
388     *
389     * @param fromIndex the index to start checking from (inclusive).
390     * @return the index of the previous clear bit, or {@code -1} if there is no such bit.
391     * @throws IndexOutOfBoundsException if the specified index is less than {@code -1}.
392     */
393    public int previousClearBit(final int fromIndex) {
394        return bitSet.previousClearBit(fromIndex);
395    }
396
397    /**
398     * Returns the index of the nearest bit that is set to {@code true} that occurs on or before the specified starting
399     * index. If no such bit exists, or if {@code -1} is given as the starting index, then {@code -1} is returned.
400     *
401     * <p>
402     * To iterate over the {@code true} bits in a {@link BitSet}, use the following loop:
403     *
404     * <pre>
405     *  {@code
406     * for (int i = bs.length(); (i = bs.previousSetBit(i-1)) >= 0; ) {
407     *     // operate on index i here
408     * }}
409     * </pre>
410     *
411     * @param fromIndex the index to start checking from (inclusive)
412     * @return the index of the previous set bit, or {@code -1} if there is no such bit
413     * @throws IndexOutOfBoundsException if the specified index is less than {@code -1}
414     */
415    public int previousSetBit(final int fromIndex) {
416        return bitSet.previousSetBit(fromIndex);
417    }
418
419    /**
420     * Sets the bit at the specified indexes to {@code true}.
421     *
422     * @param bitIndexArray a bit index array.
423     * @throws IndexOutOfBoundsException if the specified index is negative.
424     * @return {@code this} instance.
425     */
426    public FluentBitSet set(final int... bitIndexArray) {
427        for (final int e : bitIndexArray) {
428            bitSet.set(e);
429        }
430        return this;
431    }
432
433    /**
434     * Sets the bit at the specified index to {@code true}.
435     *
436     * @param bitIndex a bit index
437     * @throws IndexOutOfBoundsException if the specified index is negative
438     * @return {@code this} instance.
439     */
440    public FluentBitSet set(final int bitIndex) {
441        bitSet.set(bitIndex);
442        return this;
443    }
444
445    /**
446     * Sets the bit at the specified index to the specified value.
447     *
448     * @param bitIndex a bit index.
449     * @param value a boolean value to set.
450     * @throws IndexOutOfBoundsException if the specified index is negative.
451     * @return {@code this} instance.
452     */
453    public FluentBitSet set(final int bitIndex, final boolean value) {
454        bitSet.set(bitIndex, value);
455        return this;
456    }
457
458    /**
459     * Sets the bits from the specified {@code fromIndex} (inclusive) to the specified {@code toIndex} (exclusive) to
460     * {@code true}.
461     *
462     * @param fromIndex index of the first bit to be set.
463     * @param toIndex index after the last bit to be set.
464     * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or
465     *         {@code fromIndex} is larger than {@code toIndex}.
466     * @return {@code this} instance.
467     */
468    public FluentBitSet set(final int fromIndex, final int toIndex) {
469        bitSet.set(fromIndex, toIndex);
470        return this;
471    }
472
473    /**
474     * Sets the bits from the specified {@code fromIndex} (inclusive) to the specified {@code toIndex} (exclusive) to the
475     * specified value.
476     *
477     * @param fromIndex index of the first bit to be set.
478     * @param toIndex index after the last bit to be set.
479     * @param value value to set the selected bits to.
480     * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or
481     *         {@code fromIndex} is larger than {@code toIndex}.
482     * @return {@code this} instance.
483     */
484    public FluentBitSet set(final int fromIndex, final int toIndex, final boolean value) {
485        bitSet.set(fromIndex, toIndex, value);
486        return this;
487    }
488
489    /**
490     * Sets the bits from the specified {@code fromIndex} (inclusive) to the specified {@code toIndex} (inclusive) to
491     * {@code true}.
492     *
493     * @param fromIndex index of the first bit to be set
494     * @param toIndex index of the last bit to be set
495     * @throws IndexOutOfBoundsException if {@code fromIndex} is negative, or {@code toIndex} is negative, or
496     *         {@code fromIndex} is larger than {@code toIndex}
497     * @return {@code this} instance.
498     */
499    public FluentBitSet setInclusive(final int fromIndex, final int toIndex) {
500        bitSet.set(fromIndex, toIndex + 1);
501        return this;
502    }
503
504    /**
505     * Returns the number of bits of space actually in use by this {@link BitSet} to represent bit values. The maximum
506     * element in the set is the size - 1st element.
507     *
508     * @return the number of bits currently in this bit set.
509     */
510    public int size() {
511        return bitSet.size();
512    }
513
514    /**
515     * Returns a stream of indices for which this {@link BitSet} contains a bit in the set state. The indices are returned
516     * in order, from lowest to highest. The size of the stream is the number of bits in the set state, equal to the value
517     * returned by the {@link #cardinality()} method.
518     *
519     * <p>
520     * The bit set must remain constant during the execution of the terminal stream operation. Otherwise, the result of the
521     * terminal stream operation is undefined.
522     * </p>
523     *
524     * @return a stream of integers representing set indices.
525     * @since 1.8
526     */
527    public IntStream stream() {
528        return bitSet.stream();
529    }
530
531    /**
532     * Returns a new byte array containing all the bits in this bit set.
533     *
534     * <p>
535     * More precisely, if:
536     * </p>
537     * <ol>
538     * <li>{@code byte[] bytes = s.toByteArray();}</li>
539     * <li>then {@code bytes.length == (s.length()+7)/8} and</li>
540     * <li>{@code s.get(n) == ((bytes[n/8] & (1<<(n%8))) != 0)}</li>
541     * <li>for all {@code n < 8 * bytes.length}.</li>
542     * </ol>
543     *
544     * @return a byte array containing a little-endian representation of all the bits in this bit set
545     */
546    public byte[] toByteArray() {
547        return bitSet.toByteArray();
548    }
549
550    /**
551     * Returns a new byte array containing all the bits in this bit set.
552     *
553     * <p>
554     * More precisely, if:
555     * </p>
556     * <ol>
557     * <li>{@code long[] longs = s.toLongArray();}</li>
558     * <li>then {@code longs.length == (s.length()+63)/64} and</li>
559     * <li>{@code s.get(n) == ((longs[n/64] & (1L<<(n%64))) != 0)}</li>
560     * <li>for all {@code n < 64 * longs.length}.</li>
561     * </ol>
562     *
563     * @return a byte array containing a little-endian representation of all the bits in this bit set
564     */
565    public long[] toLongArray() {
566        return bitSet.toLongArray();
567    }
568
569    @Override
570    public String toString() {
571        return bitSet.toString();
572    }
573
574    /**
575     * Performs a logical <strong>XOR</strong> of this bit set with the bit set argument. This bit set is modified so that a bit in it
576     * has the value {@code true} if and only if one of the following statements holds:
577     * <ul>
578     * <li>The bit initially has the value {@code true}, and the corresponding bit in the argument has the value
579     * {@code false}.</li>
580     * <li>The bit initially has the value {@code false}, and the corresponding bit in the argument has the value
581     * {@code true}.</li>
582     * </ul>
583     *
584     * @param set a bit set
585     * @return {@code this} instance.
586     */
587    public FluentBitSet xor(final BitSet set) {
588        bitSet.xor(set);
589        return this;
590    }
591
592    /**
593     * Performs a logical <strong>XOR</strong> of this bit set with the bit set argument. This bit set is modified so that a bit in it
594     * has the value {@code true} if and only if one of the following statements holds:
595     * <ul>
596     * <li>The bit initially has the value {@code true}, and the corresponding bit in the argument has the value
597     * {@code false}.</li>
598     * <li>The bit initially has the value {@code false}, and the corresponding bit in the argument has the value
599     * {@code true}.</li>
600     * </ul>
601     *
602     * @param set a bit set
603     * @return {@code this} instance.
604     */
605    public FluentBitSet xor(final FluentBitSet set) {
606        bitSet.xor(set.bitSet);
607        return this;
608    }
609
610}