001/*
002 * Java Genetic Algorithm Library (jenetics-8.0.0).
003 * Copyright (c) 2007-2024 Franz Wilhelmstötter
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 *      http://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 *
017 * Author:
018 *    Franz Wilhelmstötter (franz.wilhelmstoetter@gmail.com)
019 */
020package io.jenetics.internal.collection;
021
022import static java.lang.String.format;
023
024import java.math.BigInteger;
025import java.util.Arrays;
026import java.util.Objects;
027
028import io.jenetics.internal.util.Bits;
029import io.jenetics.util.Copyable;
030
031/**
032 * This class represents a fixed sized array of <em>bit</em> or <em>boolean</em>
033 * values, backed by a {@code byte[]} array. The order of the bit values is shown
034 * if the drawing.
035 * <pre> {@code
036 *  Byte:       3        2        1        0
037 *              |        |        |        |
038 *  Array: |11110011|10011101|01000000|00101010|
039 *          |                 |        |      |
040 *  Bit:    23                15       7      0
041 * } </pre>
042 *
043 * @author <a href="mailto:franz.wilhelmstoetter@gmail.com">Franz Wilhelmstötter</a>
044 * @since 7.0
045 * @version 7.0
046 */
047public final class BitArray implements Copyable<BitArray> {
048
049        private static final int[] BITS = {
050                1, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
051        };
052
053        private final byte[] _data;
054        private final int _start;
055        private final int _end;
056
057        /**
058         * Create a new bit-array with the given {@code data} values and
059         * {@code begin} and {@code end} <em>bit</em> indexes.
060         *
061         * @param data the {@code byte[]} array which contains the bit data
062         * @param start the start bit index (inclusively)
063         * @param end the end bit index (exclusively)
064         * @throws NullPointerException if the given {@code data} array is
065         *         {@code null}
066         * @throws IllegalArgumentException if the {@code begin} and {@code end}
067         *         indexes are not within the valid range
068         */
069        BitArray(final byte[] data, final int start, final int end) {
070                if (data.length == 0) {
071                        throw new IllegalArgumentException("Byte array must not be empty.");
072                }
073                if (start < 0) {
074                        throw new IllegalArgumentException(
075                                "Begin index is smaller then zero: " + start
076                        );
077                }
078                if (end < start || end > data.length*Byte.SIZE) {
079                        throw new IllegalArgumentException(format(
080                                "End index is not within the valid range of [%d, %d]: %d",
081                                start, data.length*Byte.SIZE, end
082                        ));
083                }
084
085                _data = data;
086                _start = start;
087                _end = end;
088        }
089
090        /**
091         * Create a new bit-array with the given {@code data} values.
092         *
093         * @param data the {@code byte[]} array which contains the bit data
094         * @throws NullPointerException if the given {@code data} array is
095         *         {@code null}
096         */
097        BitArray(final byte[] data) {
098                this(data, 0, data.length*Byte.SIZE);
099        }
100
101        /**
102         * Return the length of the bit-array.
103         *
104         * @return the length of the bit array
105         */
106        public int length() {
107                return _end - _start;
108        }
109
110        /**
111         * Return the number of set bits of this bit-array.
112         *
113         * @return the number of set bits
114         */
115        public int bitCount() {
116                return Bits.count(_data, _start,_end);
117        }
118
119        /**
120         * Sets the specified bit {@code value} at the given bit {@code index}.
121         *
122         * @param index the bit index
123         * @param value the bit value
124         * @throws IndexOutOfBoundsException if the index is not within the valid
125         *         range of {@code [0, length())}
126         */
127        public void set(final int index, final boolean value) {
128                Objects.checkIndex(index, length());
129                Bits.set(_data, _start + index, value);
130        }
131
132        /**
133         * Set the bit in the given byte array at the bit position (not the index
134         * within the byte array) to {@code true}.
135         *
136         * @param index the bit index
137         * @throws IndexOutOfBoundsException if the index is not within the valid
138         *         range of {@code [0, length())}
139         */
140        public void set(final int index) {
141                Objects.checkIndex(index, length());
142                Bits.set(_data, _start + index);
143        }
144
145        /**
146         * Set the bit in the given byte array at the bit position (not the index
147         * within the byte array) to {@code false}.
148         *
149         * @param index the bit index
150         * @throws IndexOutOfBoundsException if the index is not within the valid
151         *         range of {@code [0, length())}
152         */
153        public void unset(final int index) {
154                Objects.checkIndex(index, length());
155                Bits.unset(_data, _start + index);
156        }
157
158        /**
159         * Return the bit value at the given bit {@code index}.
160         *
161         * @param index the bit index
162         * @return the bit value
163         * @throws IndexOutOfBoundsException if the index is not within the valid
164         *         range of {@code [0, length())}
165         */
166        public boolean get(final int index) {
167                Objects.checkIndex(index, length());
168                return Bits.get(_data, _start + index);
169        }
170
171        /**
172         * Inverts {@code this} bit-array.
173         */
174        public void invert() {
175                Bits.invert(_data);
176        }
177
178        /**
179         * Shifting all bits in {@code this} bit array the given {@code n} bits to
180         * the left. The bits on the right side are filled with zeros.
181         *
182         * @since 7.1
183         *
184         * @param n the number of bits to shift.
185         */
186        public void shiftLeft(final int n) {
187                if (_start != 0) {
188                        throw new IllegalStateException(format(
189                                "BitArray start is != 0:%s ", _start
190                        ));
191                }
192                Bits.shiftLeft(_data, n);
193        }
194
195        /**
196         * Shifting all bits in {@code this} bit array the given {@code n} bits to
197         * the right. The bits on the right side are filled with zeros.
198         *
199         * @since 7.1
200         *
201         * @param n the number of bits to shift.
202         */
203        public void shiftRight(final int n) {
204                if (_start != 0) {
205                        throw new IllegalStateException(format(
206                                "BitArray start is != 0:%s ", _start
207                        ));
208                }
209                Bits.shiftRight(_data, n);
210        }
211
212        /**
213         * Return the signum of the number, represented by this bit-array (-1 for
214         * negative, 0 for zero, 1 for positive).
215         *
216         * {@snippet lang="java":
217         * final BitArray bits = null; // @replace substring='null' replacement="..."
218         * final BigInteger i = bits.toBigInteger();
219         * assert bits.signum() == i.signum();
220         * }
221         *
222         * @return the signum of the number, represented by this bit-array (-1 for
223         *             negative, 0 for zero, 1 for positive)
224         */
225        public int signum() {
226                if (get(length() - 1)) {
227                        return -1;
228                } else {
229                        return bitCount() == 0 ? 0 : 1;
230                }
231        }
232
233        /**
234         * Return the value of this bit-array as {@link BigInteger} value. This
235         * bit-array can be recreated by the returned {@code BigInteger} value. But
236         * only with the same {@link #length()} of {@code this} bit-array.
237         *
238         * {@snippet lang="java":
239         * final var bits = BitArray.of("1111111010100110010110110010011110110101");
240         * final var bint = bits.toBigInteger();
241         * assert BitArray.of(bint, bits.length()).equals(bits);
242         * }
243         *
244         * @see #of(BigInteger, int)
245         *
246         * @return a new {@code BigInteger} object, which represents the integer
247         *         value of this bit-array
248         */
249        public BigInteger toBigInteger() {
250                return new BigInteger(toTowsComplementByteArray());
251        }
252
253        /**
254         * Returns the two's complement binary representation of this
255         * big integer. The output array is in <i>big-endian</i>
256         * byte-order: the most significant byte is at the offset position.
257         *
258         * <p>Note: This representation is consistent with {@code java.lang.BigInteger}
259         * byte array representation and can be used for conversion between the two
260         * classes.</p>
261         *
262         * @return the two's complement byte array representation
263         * @throws IndexOutOfBoundsException
264         *         if {@code bytes.length < (int)Math.ceil(length()/8.0)}
265         * @throws NullPointerException it the give array is {@code null}.
266         */
267        private byte[] toTowsComplementByteArray() {
268                final byte[] array = toByteArray();
269                if (get(length() - 1)) {
270                        for (int i = length(), n = array.length*Byte.SIZE; i < n; ++i) {
271                                Bits.set(array, i);
272                        }
273                }
274                Bits.reverse(array);
275                return array;
276        }
277
278        /**
279         * Return the {@code byte[]} array, which represents the state of the state
280         * of {@code this} bit-array.
281         *
282         * {@snippet lang="java":
283         * final BitArray bits = null; // @replace substring='null' replacement="..."
284         * final byte[] bytes = bits.toByteArray();
285         * assert bits.equals(BitArray.of(bytes, bits.length()));
286         * }
287         *
288         * @return the bit-array data as {@code byte[]} array
289         */
290        public byte[] toByteArray() {
291                return Bits.copy(_data, _start, _end);
292        }
293
294        /**
295         * Create a new copy of {@code this} bit-array.
296         *
297         * @return a new copy of {@code this} bit-array
298         */
299        @Override
300        public BitArray copy() {
301                return new BitArray(toByteArray(), 0, length());
302        }
303
304        @Override
305        public int hashCode() {
306                return toBigInteger().hashCode();
307        }
308
309        @Override
310        public boolean equals(final Object obj) {
311                return obj instanceof BitArray other && equals(other);
312        }
313
314        private boolean equals(final BitArray array) {
315                if (array.length() != length()) {
316                        return false;
317                }
318                return Arrays.equals(toByteArray(), array.toByteArray());
319        }
320
321        @Override
322        public String toString() {
323                final char[] chars = new char[length()];
324                for (int i = 0; i < chars.length; ++i) {
325                        chars[chars.length - 1 - i] = get(i) ? '1' : '0';
326                }
327
328                return new String(chars);
329        }
330
331        /**
332         * Convert a binary representation of {@code this} bit-array to a string. The
333         * string has the following format:
334         * <pre>
335         *  Byte:       3        2        1        0
336         *              |        |        |        |
337         *  Array: "11110011|10011101|01000000|00101010"
338         *          |                 |        |      |
339         *  Bit:    23                15       7      0
340         * </pre>
341         *
342         * @return the binary representation of {@code this} bit array.
343         */
344        public String toByteString() {
345                return Bits.toByteString(toByteArray());
346        }
347
348        /* *************************************************************************
349         * Static factory methods.
350         * ************************************************************************/
351
352        /**
353         * Creates a new bit-array from the given {@code value} and the given
354         * {@code length}. It is guaranteed that the created bit-array will
355         * represent the given {@link BigInteger}, as long as the {@code length}
356         * is big enough to store the whole value. If the length is shorter than
357         * required, the higher order bits will be truncated.
358         *
359         * {@snippet lang="java":
360         * final var length = 2048;
361         * final var bint = BigInteger.probablePrime(length, new Random());
362         * final var bits = BitArray.of(bint, length + 1);
363         * assert bits3.toBigInteger().equals(bint);
364         * }
365         *
366         * @see #toBigInteger()
367         *
368         * @param value the integer value
369         * @param length the length of the created bit-array
370         * @return a newly created bit-array which represent the given {@code value}
371         * @throws NullPointerException if the given {@code value} is {@code null}
372         * @throws NegativeArraySizeException if the {@code length} is negative
373         */
374        public static BitArray of(final BigInteger value, final int length) {
375                final byte[] array = Bits.newArray(length);
376                final byte[] data = value.toByteArray();
377
378                Bits.reverse(data);
379                if (value.signum() < 0) {
380                        array[array.length - 1] = (byte)-1;
381                }
382                System.arraycopy(data, 0, array, 0, data.length);
383
384                return new BitArray(array, 0, length);
385        }
386
387        /**
388         * Create a new bit-array from the given {@link BigInteger} value.
389         *
390         * @see #of(BigInteger, int)
391         *
392         * @param value the integer value
393         * @return a newly created bit-array which represent the given {@code value}
394         * @throws NullPointerException if the given {@code value} is {@code null}
395         */
396        public static BitArray of(final BigInteger value) {
397                final byte[] data = value.toByteArray();
398                Bits.reverse(data);
399                return new BitArray(data, 0, data.length*Byte.SIZE);
400        }
401
402        /**
403         * Creates a new bit-array from the given string {@code value}. The given
404         * {@code length} might be bigger and smaller than the length of the given
405         * {@code value} string. The higher order bits of the created bit-array are
406         * trimmed or filled with zero if the {@code length} is smaller or bigger
407         * than the given string.
408         *
409         * @see #of(CharSequence)
410         * @see #toString()
411         *
412         * @param value the given input string, consisting only of '0's and '1's
413         * @param length the length of the created bit-array
414         * @return a new bit-array from the given input {@code value}
415         * @throws IllegalArgumentException if the given input {@code value} is
416         *         empty
417         */
418        public static BitArray of(final CharSequence value, final int length) {
419                final byte[] data = toByteArray(value, length);
420                return new BitArray(data, 0, length);
421        }
422
423        private static byte[] toByteArray(final CharSequence chars, final int length) {
424                final byte[] array = Bits.newArray(length);
425                for (int i = 0, j = length - 1; i < array.length; i++, j -= Byte.SIZE) {
426                        for (int bits = 0; bits < BITS.length && (j - bits) >= 0; ++bits) {
427                                if (get(chars, j - bits, length) == '1') {
428                                        array[i] |= (byte)BITS[bits];
429                                }
430                        }
431                }
432                return array;
433        }
434
435        private static char get(final CharSequence chars, final int i, final int length) {
436                if (chars.length() < length) {
437                        final int d = length - i;
438                        return d <= chars.length() ? chars.charAt(chars.length() - d) : '0';
439                } else if (chars.length() > length) {
440                        return chars.charAt(chars.length() - length  + i);
441                } else {
442                        return chars.charAt(i);
443                }
444        }
445
446        /**
447         * Creates a new bit-array from the given string {@code value}. The string,
448         * created by the {@link #toString()} method, will be equals to the given
449         * input {@code value}.
450         *
451         * {@snippet lang="java":
452         * final var string = "11111110101001100101101100100111101101011101";
453         * final var bits = BitArray.of(string);
454         * assert bits.toString().equals(string);
455         * }
456         *
457         * @see #toString()
458         *
459         * @param value the given input string, consisting only of '0's and '1's
460         * @return a new bit-array from the given input {@code value}
461         * @throws IllegalArgumentException if the given input {@code value} is
462         *         empty
463         */
464        public static BitArray of(final CharSequence value) {
465                return of(value, value.length());
466        }
467
468        /**
469         * Create a new bit-array with the given {@code data} values and
470         * {@code begin} and {@code end} <em>bit</em> indexes. The given
471         * {@code data} is copied.
472         *
473         * @param data the {@code byte[]} array which contains the bit data
474         * @param begin the start bit index (inclusively)
475         * @param end the end bit index (exclusively)
476         * @return a newly created bit-array
477         * @throws NullPointerException if the given {@code data} array is
478         *         {@code null}
479         * @throws IllegalArgumentException if the {@code begin} and {@code end}
480         *         indexes are not within the valid range
481         */
482        public static BitArray of(final byte[] data, final int begin, final int end) {
483                final var bytes = Bits.copy(data, begin, end);
484                return new BitArray(bytes, 0, end - begin);
485        }
486
487        /**
488         * Create a new bit-array with the given {@code data} values and
489         * {@code length}. The given {@code data} is copied.
490         *
491         * @param data the {@code byte[]} array which contains the bit data
492         * @param length the bit length
493         * @return a newly created bit-array
494         * @throws NullPointerException if the given {@code data} array is
495         *         {@code null}
496         * @throws IllegalArgumentException if the {@code length} is greater than
497         *         {@code data.length*Byte.SIZE}
498         */
499        public static BitArray of(final byte[] data, final int length) {
500                return of(data, 0, length);
501        }
502
503        /**
504         * Create a new bit-array with the given {@code data} values and
505         * {@code length}. The given {@code data} is copied.
506         *
507         * @param data the {@code byte[]} array which contains the bit data
508         * @return a newly created bit-array
509         * @throws NullPointerException if the given {@code data} array is
510         *         {@code null}
511         */
512        public static BitArray of(final byte[] data) {
513                return of(data, 0, data.length*Byte.SIZE);
514        }
515
516        /**
517         * Crate a new bit-array with the given length. All bits are set to '0'.
518         *
519         * @param length the length of the bit-array
520         * @return a newly created bit-array with the given {@code length}
521         * @throws IllegalArgumentException if the given {@code length} is smaller
522         *         then one
523         */
524        public static BitArray ofLength(final int length) {
525                return new BitArray(Bits.newArray(length), 0, length);
526        }
527
528        /**
529         * Create a new bit-array which can store at least the number
530         * of bits as defined by the given {@code length} parameter. The returned
531         * byte array is initialized with ones according to the given ones
532         * probability {@code p}.
533         *
534         * @param length the number of bits, the returned bit-array can store.
535         * @param p the ones probability of the returned byte array.
536         * @return the new byte array.s
537         * @throws IllegalArgumentException if {@code p} is not a valid probability.
538         */
539        public static BitArray ofLength(final int length, final double p) {
540                return new BitArray(Bits.newArray(length, p), 0, length);
541        }
542
543}