001/* 002 * Java Genetic Algorithm Library (jenetics-7.2.0). 003 * Copyright (c) 2007-2023 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 * <pre>{@code 217 * final BitArray bits = ...; 218 * final BigInteger i = bits.toBigInteger(); 219 * assert bits.signum() == i.signum(); 220 * }</pre> 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 * <pre>{@code 239 * final var bits = BitArray.of("1111111010100110010110110010011110110101"); 240 * final var bint = bits.toBigInteger(); 241 * assert BitArray.of(bint, bits.length()).equals(bits); 242 * }</pre> 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 * <pre>{@code 283 * final BitArray bits = ...; 284 * final byte[] bytes = bits.toByteArray(); 285 * assert bits.equals(BitArray.of(bytes, bits.length())); 286 * }</pre> 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 * <pre>{@code 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 * }</pre> 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 * <pre>{@code 452 * final var string = "11111110101001100101101100100111101101011101"; 453 * final var bits = BitArray.of(string); 454 * assert bits.toString().equals(string); 455 * }</pre> 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}