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}