001/*
002 * Java Genetic Algorithm Library (jenetics-7.0.0).
003 * Copyright (c) 2007-2022 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         * Return the signum of the number, represented by this bit-array (-1 for
180         * negative, 0 for zero, 1 for positive).
181         *
182         * <pre>{@code
183         * final BitArray bits = ...;
184         * final BigInteger i = bits.toBigInteger();
185         * assert bits.signum() == i.signum();
186         * }</pre>
187         *
188         * @return the signum of the number, represented by this bit-array (-1 for
189         *             negative, 0 for zero, 1 for positive)
190         */
191        public int signum() {
192                if (get(length() - 1)) {
193                        return -1;
194                } else {
195                        return bitCount() == 0 ? 0 : 1;
196                }
197        }
198
199        /**
200         * Return the value of this bit-array as {@link BigInteger} value. This
201         * bit-array can be recreated by the returned {@code BigInteger} value. But
202         * only with the same {@link #length()} of {@code this} bit-array.
203         *
204         * <pre>{@code
205         * final var bits = BitArray.of("1111111010100110010110110010011110110101");
206         * final var bint = bits.toBigInteger();
207         * assert BitArray.of(bint, bits.length()).equals(bits);
208         * }</pre>
209         *
210         * @see #of(BigInteger, int)
211         *
212         * @return a new {@code BigInteger} object, which represents the integer
213         *         value of this bit-array
214         */
215        public BigInteger toBigInteger() {
216                return new BigInteger(toTowsComplementByteArray());
217        }
218
219        /**
220         * Returns the two's complement binary representation of this
221         * big integer. The output array is in <i>big-endian</i>
222         * byte-order: the most significant byte is at the offset position.
223         *
224         * <p>Note: This representation is consistent with {@code java.lang.BigInteger}
225         * byte array representation and can be used for conversion between the two
226         * classes.</p>
227         *
228         * @return the two's complement byte array representation
229         * @throws IndexOutOfBoundsException
230         *         if {@code bytes.length < (int)Math.ceil(length()/8.0)}
231         * @throws NullPointerException it the give array is {@code null}.
232         */
233        private byte[] toTowsComplementByteArray() {
234                final byte[] array = toByteArray();
235                if (get(length() - 1)) {
236                        for (int i = length(), n = array.length*Byte.SIZE; i < n; ++i) {
237                                Bits.set(array, i);
238                        }
239                }
240                Bits.reverse(array);
241                return array;
242        }
243
244        /**
245         * Return the {@code byte[]} array, which represents the state of the state
246         * of {@code this} bit-array.
247         *
248         * <pre>{@code
249         * final BitArray bits = ...;
250         * final byte[] bytes = bits.toByteArray();
251         * assert bits.equals(BitArray.of(bytes, bits.length()));
252         * }</pre>
253         *
254         * @return the bit-array data as {@code byte[]} array
255         */
256        public byte[] toByteArray() {
257                return Bits.copy(_data, _start, _end);
258        }
259
260        /**
261         * Create a new copy of {@code this} bit-array.
262         *
263         * @return a new copy of {@code this} bit-array
264         */
265        @Override
266        public BitArray copy() {
267                return new BitArray(toByteArray(), 0, length());
268        }
269
270        @Override
271        public int hashCode() {
272                return toBigInteger().hashCode();
273        }
274
275        @Override
276        public boolean equals(final Object obj) {
277                return obj instanceof BitArray other && equals(other);
278        }
279
280        private boolean equals(final BitArray array) {
281                if (array.length() != length()) {
282                        return false;
283                }
284                return Arrays.equals(toByteArray(), array.toByteArray());
285        }
286
287        @Override
288        public String toString() {
289                final char[] chars = new char[length()];
290                for (int i = 0; i < chars.length; ++i) {
291                        chars[chars.length - 1 - i] = get(i) ? '1' : '0';
292                }
293
294                return new String(chars);
295        }
296
297        /**
298         * Convert a binary representation of {@code this} bit-array to a string. The
299         * string has the following format:
300         * <pre>
301         *  Byte:       3        2        1        0
302         *              |        |        |        |
303         *  Array: "11110011|10011101|01000000|00101010"
304         *          |                 |        |      |
305         *  Bit:    23                15       7      0
306         * </pre>
307         *
308         * @return the binary representation of {@code this} bit array.
309         */
310        public String toByteString() {
311                return Bits.toByteString(toByteArray());
312        }
313
314        /* *************************************************************************
315         * Static factory methods.
316         * ************************************************************************/
317
318        /**
319         * Creates a new bit-array from the given {@code value} and the given
320         * {@code length}. It is guaranteed, that the created bit-array will
321         * represent the given {@link BigInteger}, as long as the {@code length}
322         * is big enough to store the whole value. If the length is shorter then
323         * required, the higher order bits will be truncated.
324         *
325         * <pre>{@code
326         * final var length = 2048;
327         * final var bint = BigInteger.probablePrime(length, new Random());
328         * final var bits = BitArray.of(bint, length + 1);
329         * assert bits3.toBigInteger().equals(bint);
330         * }</pre>
331         *
332         * @see #toBigInteger()
333         *
334         * @param value the integer value
335         * @param length the length of the created bit-array
336         * @return a newly created bit-array which represent the given {@code value}
337         * @throws NullPointerException if the given {@code value} is {@code null}
338         * @throws NegativeArraySizeException if the {@code length} is negative
339         */
340        public static BitArray of(final BigInteger value, final int length) {
341                final byte[] array = Bits.newArray(length);
342                final byte[] data = value.toByteArray();
343
344                Bits.reverse(data);
345                if (value.signum() < 0) {
346                        array[array.length - 1] = (byte)-1;
347                }
348                System.arraycopy(data, 0, array, 0, data.length);
349
350                return new BitArray(array, 0, length);
351        }
352
353        /**
354         * Create a new bit-array from the given {@link BigInteger} value.
355         *
356         * @see #of(BigInteger, int)
357         *
358         * @param value the integer value
359         * @return a newly created bit-array which represent the given {@code value}
360         * @throws NullPointerException if the given {@code value} is {@code null}
361         */
362        public static BitArray of(final BigInteger value) {
363                final byte[] data = value.toByteArray();
364                Bits.reverse(data);
365                return new BitArray(data, 0, data.length*Byte.SIZE);
366        }
367
368        /**
369         * Creates a new bit-array from the given string {@code value}. The given
370         * {@code length} might be bigger and smaller than the length of the given
371         * {@code value} string. The higher order bits of the created bit-array are
372         * trimmed or filled with zero if the {@code length} is smaller or bigger
373         * than the given string.
374         *
375         * @see #of(CharSequence)
376         * @see #toString()
377         *
378         * @param value the given input string, consisting only of '0's and '1's
379         * @param length the length of the created bit-array
380         * @return a new bit-array from the given input {@code value}
381         * @throws IllegalArgumentException if the given input {@code value} is
382         *         empty
383         */
384        public static BitArray of(final CharSequence value, final int length) {
385                final byte[] data = toByteArray(value, length);
386                return new BitArray(data, 0, length);
387        }
388
389        private static byte[] toByteArray(final CharSequence chars, final int length) {
390                final byte[] array = Bits.newArray(length);
391                for (int i = 0, j = length - 1; i < array.length; i++, j -= Byte.SIZE) {
392                        for (int bits = 0; bits < BITS.length && (j - bits) >= 0; ++bits) {
393                                if (get(chars, j - bits, length) == '1') {
394                                        array[i] |= BITS[bits];
395                                }
396                        }
397                }
398                return array;
399        }
400
401        private static char get(final CharSequence chars, final int i, final int length) {
402                if (chars.length() < length) {
403                        final int d = length - i;
404                        return d <= chars.length() ? chars.charAt(chars.length() - d) : '0';
405                } else if (chars.length() > length) {
406                        return chars.charAt(chars.length() - length  + i);
407                } else {
408                        return chars.charAt(i);
409                }
410        }
411
412        /**
413         * Creates a new bit-array from the given string {@code value}. The string,
414         * created by the {@link #toString()} method, will be equals to the given
415         * input {@code value}.
416         *
417         * <pre>{@code
418         * final var string = "11111110101001100101101100100111101101011101";
419         * final var bits = BitArray.of(string);
420         * assert bits.toString().equals(string);
421         * }</pre>
422         *
423         * @see #toString()
424         *
425         * @param value the given input string, consisting only of '0's and '1's
426         * @return a new bit-array from the given input {@code value}
427         * @throws IllegalArgumentException if the given input {@code value} is
428         *         empty
429         */
430        public static BitArray of(final CharSequence value) {
431                return of(value, value.length());
432        }
433
434        /**
435         * Create a new bit-array with the given {@code data} values and
436         * {@code begin} and {@code end} <em>bit</em> indexes. The given
437         * {@code data} is copied.
438         *
439         * @param data the {@code byte[]} array which contains the bit data
440         * @param begin the start bit index (inclusively)
441         * @param end the end bit index (exclusively)
442         * @return a newly created bit-array
443         * @throws NullPointerException if the given {@code data} array is
444         *         {@code null}
445         * @throws IllegalArgumentException if the {@code begin} and {@code end}
446         *         indexes are not within the valid range
447         */
448        public static BitArray of(final byte[] data, final int begin, final int end) {
449                final var bytes = Bits.copy(data, begin, end);
450                return new BitArray(bytes, 0, end - begin);
451        }
452
453        /**
454         * Create a new bit-array with the given {@code data} values and
455         * {@code length}. The given {@code data} is copied.
456         *
457         * @param data the {@code byte[]} array which contains the bit data
458         * @param length the bit length
459         * @return a newly created bit-array
460         * @throws NullPointerException if the given {@code data} array is
461         *         {@code null}
462         * @throws IllegalArgumentException if the {@code length} is greater than
463         *         {@code data.length*Byte.SIZE}
464         */
465        public static BitArray of(final byte[] data, final int length) {
466                return of(data, 0, length);
467        }
468
469        /**
470         * Create a new bit-array with the given {@code data} values and
471         * {@code length}. The given {@code data} is copied.
472         *
473         * @param data the {@code byte[]} array which contains the bit data
474         * @return a newly created bit-array
475         * @throws NullPointerException if the given {@code data} array is
476         *         {@code null}
477         */
478        public static BitArray of(final byte[] data) {
479                return of(data, 0, data.length*Byte.SIZE);
480        }
481
482        /**
483         * Crate a new bit-array with the given length. All bits are set to '0'.
484         *
485         * @param length the length of the bit-array
486         * @return a newly created bit-array with the given {@code length}
487         * @throws IllegalArgumentException if the given {@code length} is smaller
488         *         then one
489         */
490        public static BitArray ofLength(final int length) {
491                return new BitArray(Bits.newArray(length), 0, length);
492        }
493
494        /**
495         * Create a new bit-array which can store at least the number
496         * of bits as defined by the given {@code length} parameter. The returned
497         * byte array is initialized with ones according to the given ones
498         * probability {@code p}.
499         *
500         * @param length the number of bits, the returned bit-array can store.
501         * @param p the ones probability of the returned byte array.
502         * @return the new byte array.s
503         * @throws IllegalArgumentException if {@code p} is not a valid probability.
504         */
505        public static BitArray ofLength(final int length, final double p) {
506                return new BitArray(Bits.newArray(length, p), 0, length);
507        }
508
509}