Constructor of BitVector now is fail-fast for bits being null
[Mograsim.git] / net.mograsim.logic.core / src / net / mograsim / logic / core / types / BitVector.java
1 package net.mograsim.logic.core.types;
2
3 import static java.lang.String.format;
4
5 import java.util.Arrays;
6 import java.util.Iterator;
7 import java.util.NoSuchElementException;
8 import java.util.Objects;
9 import java.util.RandomAccess;
10 import java.util.function.BinaryOperator;
11 import java.util.function.UnaryOperator;
12
13 /**
14  * Immutable class representing a {@link Bit}Vector
15  *
16  *
17  * @author Christian Femers
18  *
19  */
20 public final class BitVector implements StrictLogicType<BitVector>, Iterable<Bit>, RandomAccess
21 {
22         private final Bit[] bits;
23
24         private BitVector(Bit[] bits)
25         {
26                 for (Bit bit : bits)
27                         if (bit == null)
28                                 throw new NullPointerException();
29                 this.bits = Objects.requireNonNull(bits);
30         }
31
32         public static BitVector of(Bit... bits)
33         {
34                 return new BitVector(bits.clone());
35         }
36
37         public static BitVector of(Bit bit, int length)
38         {
39                 return new BitVector(bit.makeArray(length));
40         }
41
42         public BitVectorMutator mutator()
43         {
44                 return BitVectorMutator.of(this);
45         }
46
47         public Bit getBit(int bitIndex)
48         {
49                 return bits[bitIndex];
50         }
51
52         public Bit[] getBits()
53         {
54                 return bits.clone();
55         }
56
57         @Override
58         public BitVector join(BitVector t)
59         {
60                 checkCompatibility(t);
61                 return new BitVector(binOp(bits.clone(), t.bits, Bit::join));
62         }
63
64         @Override
65         public BitVector and(BitVector t)
66         {
67                 checkCompatibility(t);
68                 return new BitVector(binOp(bits.clone(), t.bits, Bit::and));
69         }
70
71         @Override
72         public BitVector or(BitVector t)
73         {
74                 checkCompatibility(t);
75                 return new BitVector(binOp(bits.clone(), t.bits, Bit::or));
76         }
77
78         @Override
79         public BitVector xor(BitVector t)
80         {
81                 checkCompatibility(t);
82                 return new BitVector(binOp(bits.clone(), t.bits, Bit::xor));
83         }
84
85         @Override
86         public BitVector not()
87         {
88                 return new BitVector(unOp(bits.clone(), Bit::not));
89         }
90
91         public int length()
92         {
93                 return bits.length;
94         }
95
96         public BitVector concat(BitVector other)
97         {
98                 Bit[] newBits = Arrays.copyOf(bits, length() + other.length());
99                 System.arraycopy(other.bits, 0, newBits, length(), other.length());
100                 return new BitVector(newBits);
101         }
102
103         public BitVector subVector(int start)
104         {
105                 return new BitVector(Arrays.copyOfRange(bits, start, length()));
106         }
107
108         public BitVector subVector(int start, int end)
109         {
110                 return new BitVector(Arrays.copyOfRange(bits, start, end));
111         }
112
113         private void checkCompatibility(BitVector bv)
114         {
115                 if (length() != bv.length())
116                         throw new IllegalArgumentException(format("BitVector length does not match: %d and %d", length(), bv.length()));
117         }
118
119         static Bit[] binOp(Bit[] dest, Bit[] second, BinaryOperator<Bit> op)
120         {
121                 if (dest == null)
122                         return second.clone();
123                 for (int i = 0; i < dest.length; i++)
124                 {
125                         dest[i] = op.apply(dest[i], second[i]);
126                 }
127                 return dest;
128         }
129
130         static Bit[] unOp(Bit[] dest, UnaryOperator<Bit> op)
131         {
132                 if (dest == null)
133                         return null;
134                 for (int i = 0; i < dest.length; i++)
135                 {
136                         dest[i] = op.apply(dest[i]);
137                 }
138                 return dest;
139         }
140
141         /**
142          * Class for comfortable and efficient manipulation of {@link BitVector}s, similar to {@link StringBuilder}
143          *
144          * @author Christian Femers
145          */
146         public static final class BitVectorMutator implements LogicType<BitVectorMutator, BitVector>
147         {
148                 private Bit[] bits;
149
150                 private BitVectorMutator(Bit[] bits)
151                 {
152                         this.bits = bits;
153                 }
154
155                 static BitVectorMutator of(BitVector bv)
156                 {
157                         return new BitVectorMutator(bv.getBits());
158                 }
159
160                 /**
161                  * Returns a new mutator of the specified length, <b>with all bits set to <code>null</code></b>. Use with care!
162                  */
163                 public static BitVectorMutator ofLength(int length)
164                 {
165                         return new BitVectorMutator(new Bit[length]);
166                 }
167
168                 /**
169                  * Returns an empty mutator which has no bits set and will simply copy the values from the first binary operation performed.
170                  */
171                 public static BitVectorMutator empty()
172                 {
173                         return new BitVectorMutator(null);
174                 }
175
176                 /**
177                  * Produces the resulting, immutable {@link BitVector}<br>
178                  * 
179                  * @throws IllegalStateException if the mutator is (still) empty
180                  */
181                 public BitVector toBitVector()
182                 {
183                         if (bits == null)
184                                 throw new IllegalStateException("cannot create a BitVector from an empty mutator");
185                         return new BitVector(bits);
186                 }
187
188                 @Override
189                 public BitVectorMutator join(BitVector t)
190                 {
191                         checkCompatibility(t);
192                         bits = binOp(bits, t.bits, Bit::join);
193                         return this;
194                 }
195
196                 @Override
197                 public BitVectorMutator and(BitVector t)
198                 {
199                         checkCompatibility(t);
200                         bits = binOp(bits, t.bits, Bit::and);
201                         return this;
202                 }
203
204                 @Override
205                 public BitVectorMutator or(BitVector t)
206                 {
207                         checkCompatibility(t);
208                         bits = binOp(bits, t.bits, Bit::or);
209                         return this;
210                 }
211
212                 @Override
213                 public BitVectorMutator xor(BitVector t)
214                 {
215                         checkCompatibility(t);
216                         bits = binOp(bits, t.bits, Bit::xor);
217                         return this;
218                 }
219
220                 @Override
221                 public BitVectorMutator not()
222                 {
223                         unOp(bits, Bit::not);
224                         return this;
225                 }
226
227                 public void setBit(int bitIndex, Bit bit)
228                 {
229                         bits[bitIndex] = bit;
230                 }
231
232                 public Bit getBit(int bitIndex)
233                 {
234                         return bits[bitIndex];
235                 }
236
237                 public int length()
238                 {
239                         return bits.length;
240                 }
241
242                 private void checkCompatibility(BitVector bv)
243                 {
244                         if (bits != null && bits.length != bv.length())
245                                 throw new IllegalArgumentException(format("BitVector length does not match: %d and %d", bits.length, bv.length()));
246                 }
247         }
248
249         /**
250          * @see Arrays#hashCode(Object[])
251          */
252         @Override
253         public int hashCode()
254         {
255                 return Arrays.hashCode(bits);
256         }
257
258         /**
259          * Does test for equality of values/content
260          * 
261          * @see Object#equals(Object)
262          */
263         @Override
264         public boolean equals(Object obj)
265         {
266                 if (this == obj)
267                         return true;
268                 if (!(obj instanceof BitVector))
269                         return false;
270                 BitVector other = (BitVector) obj;
271                 return Arrays.equals(bits, other.bits);
272         }
273
274         /**
275          * Does test for equality of values/content, shifting the other BitVector by <code>offset</code> to the right.<br>
276          * Therefore <code>offset + other.length() <= this.length()</code> needs to be true.
277          * 
278          * @throws ArrayIndexOutOfBoundsException if <code>offset + other.length() > this.length()</code>
279          * 
280          * @see Object#equals(Object)
281          */
282         public boolean equalsWithOffset(BitVector other, int offset)
283         {
284                 if (other == null)
285                         return false;
286                 return Arrays.equals(bits, offset, offset + other.length(), other.bits, 0, other.length());
287         }
288
289         /**
290          * All {@link Bit}s symbols concatenated together
291          * 
292          * @see #parse(String)
293          */
294         @Override
295         public String toString()
296         {
297                 StringBuilder sb = new StringBuilder(bits.length);
298                 for (Bit bit : bits)
299                         sb.append(bit);
300                 return sb.toString();
301         }
302
303         /**
304          * Parses a String containing solely {@link Bit} symbols
305          * 
306          * @see #toString()
307          */
308         public static BitVector parse(String s)
309         {
310                 Bit[] values = new Bit[s.length()];
311                 for (int i = 0; i < s.length(); i++)
312                 {
313                         values[i] = Bit.parse(s, i);
314                 }
315                 return new BitVector(values);
316         }
317
318         @Override
319         public Iterator<Bit> iterator()
320         {
321                 return new Iterator<>()
322                 {
323                         private int pos = 0;
324
325                         @Override
326                         public Bit next()
327                         {
328                                 if (!hasNext())
329                                         throw new NoSuchElementException();
330                                 return getBit(pos++);
331                         }
332
333                         @Override
334                         public boolean hasNext()
335                         {
336                                 return pos != length();
337                         }
338                 };
339         }
340 }