View Javadoc

1   /*
2   
3       dsh-matrix  long-addressable bit and typed object matrix implementations.
4       Copyright (c) 2004-2013 held jointly by the individual authors.
5   
6       This library is free software; you can redistribute it and/or modify it
7       under the terms of the GNU Lesser General Public License as published
8       by the Free Software Foundation; either version 3 of the License, or (at
9       your option) any later version.
10  
11      This library is distributed in the hope that it will be useful, but WITHOUT
12      ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
13      FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14      License for more details.
15  
16      You should have received a copy of the GNU Lesser General Public License
17      along with this library;  if not, write to the Free Software Foundation,
18      Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.
19  
20      > http://www.fsf.org/licensing/licenses/lgpl.html
21      > http://www.opensource.org/licenses/lgpl-license.php
22  
23  */
24  package org.dishevelled.matrix;
25  
26  import org.dishevelled.bitset.MutableBitSet;
27  
28  import org.dishevelled.functor.UnaryProcedure;
29  
30  /**
31   * Fixed size bit matrix in one dimension, indexed by <code>long</code>s.
32   *
33   * @author  Michael Heuer
34   */
35  public final class BitMatrix1D
36  {
37      /** Bit set. */
38      private final MutableBitSet bitset;
39  
40      /** Size. */
41      private final long size;
42  
43  
44      /**
45       * Create a new 1D bit matrix of the specified size.
46       *
47       * @param size size, must be <code>&gt;= 0</code>
48       * @throws IllegalArgumentException if <code>size</code> is negative
49       */
50      public BitMatrix1D(final long size)
51      {
52          if (size < 0)
53          {
54              throw new IllegalArgumentException("size must be >= 0");
55          }
56          this.size = size;
57          this.bitset = new MutableBitSet(size);
58      }
59  
60  
61      /**
62       * Return the size of this 1D bit matrix.
63       *
64       * @return the size of this 1D bit matrix
65       */
66      public long size()
67      {
68          return size;
69      }
70  
71      /**
72       * Return the cardinality of this 1D bit matrix, the
73       * number of bits set to true.
74       *
75       * @return the cardinality of this 1D bit matrix
76       */
77      public long cardinality()
78      {
79          return bitset.cardinality();
80      }
81  
82      /**
83       * Return true if the cardinality of this 1D bit matrix is zero.
84       *
85       * @return true if the cardinality of this 1D bit matrix is zero
86       */
87      public boolean isEmpty()
88      {
89          return bitset.isEmpty();
90      }
91  
92      /**
93       * Clear all the values in this 1D bit matrix.
94       */
95      public void clear()
96      {
97          for (long i = bitset.nextSetBit(0); i >= 0; i = bitset.nextSetBit(i + 1))
98          {
99              bitset.clear(i);
100         }
101     }
102 
103     /**
104      * Return the bit value at the specified index.
105      *
106      * @param index index, must be <code>&gt;= 0</code> and <code>&lt; size()</code>
107      * @return the bit value at the specified index
108      * @throws IndexOutOfBoundsException if <code>index</code> is negative
109      *    or if <code>index</code> is greater than or equal to <code>size()</code>
110      */
111     public boolean get(final long index)
112     {
113         if (index < 0)
114         {
115             throw new IndexOutOfBoundsException(index + " < 0");
116         }
117         if (index >= size)
118         {
119             throw new IndexOutOfBoundsException(index + " >= " + size);
120         }
121         return getQuick(index);
122     }
123 
124     /**
125      * Return the bit value at the specified index without checking bounds.
126      *
127      * @param index index
128      * @return the bit value at the specified index
129      */
130     public boolean getQuick(final long index)
131     {
132         return bitset.getQuick(index);
133     }
134 
135     /**
136      * Set the bit value at the specified index to <code>value</code>.
137      *
138      * @param index index, must be <code>&gt;= 0</code> and <code>&lt; size()</code>
139      * @param value value
140      * @throws IndexOutOfBoundsException if <code>index</code> is negative
141      *    or if <code>index</code> is greater than or equal to <code>size()</code>
142      */
143     public void set(final long index, final boolean value)
144     {
145         if (index < 0)
146         {
147             throw new IndexOutOfBoundsException(index + " < 0");
148         }
149         if (index >= size)
150         {
151             throw new IndexOutOfBoundsException(index + " >= " + size);
152         }
153         setQuick(index, value);
154     }
155 
156     /**
157      * Set the bit value at the specified index to <code>value</code> without checking bounds.
158      *
159      * @param index index
160      * @param value value
161      */
162     public void setQuick(final long index, final boolean value)
163     {
164         if (value)
165         {
166             bitset.setQuick(index);
167         }
168         else
169         {
170             bitset.clearQuick(index);
171         }
172     }
173 
174     /**
175      * Set all of the bit values from <code>index0</code>, inclusive, to
176      * <code>index1</code>, exclusive, to the specified value.
177      *
178      * @param index0 first index, must be <code>&gt;= 0</code> and <code>&lt; size()</code>
179      * @param index1 second index, must be <code>&gt;= 0</code>, <code>&lt;= size()</code>
180      *    and <code>&gt;= index0</code>
181      * @param value value
182      * @throws IllegalArgumentException if <code>index1</code> is less than <code>index0</code>
183      * @throws IndexOutOfBoundsException if either <code>index0</code>
184      *    or <code>index1</code> are negative or if <code>index0</code> is greater than
185      *    or equal to <code>size()</code> or if <code>index1</code> is strictly greater
186      *    than <code>size()</code>
187      */
188     public void set(final long index0, final long index1, final boolean value)
189     {
190         if (index0 >= size)
191         {
192             throw new IndexOutOfBoundsException(index0 + " >= " + size);
193         }
194         if (index1 > size)
195         {
196             throw new IndexOutOfBoundsException(index1 + " > " + size);
197         }
198         if (value)
199         {
200             bitset.set(index0, index1);
201         }
202         else
203         {
204             bitset.clear(index0, index1);
205         }
206     }
207 
208     /**
209      * Set the bit value at the specified index to the complement
210      * of its current bit value.
211      *
212      * @param index index, must be <code>&gt;= 0</code> and <code>&lt; size()</code>
213      * @throws IndexOutOfBoundsException if <code>index</code> is negative
214      *    or if <code>index</code> is greater than or equal to <code>size()</code>
215      */
216     public void flip(final long index)
217     {
218         if (index < 0)
219         {
220             throw new IndexOutOfBoundsException(index + " < 0");
221         }
222         if (index >= size)
223         {
224             throw new IndexOutOfBoundsException(index + " >= " + size);
225         }
226         flipQuick(index);
227     }
228 
229     /**
230      * Set the bit value at the specified index to the complement
231      * of its current bit value without checking bounds.
232      *
233      * @param index index
234      */
235     public void flipQuick(final long index)
236     {
237         bitset.flipQuick(index);
238     }
239 
240     /**
241      * Set all of the bit values from <code>index0</code>, inclusive, to
242      * <code>index1</code>, exclusive, to the complement of their current
243      * bit values.
244      *
245      * @param index0 first index, must be <code>&gt;= 0</code> and <code>&lt; size()</code>
246      * @param index1 second index, must be <code>&gt;= 0</code>, <code>&lt;= size()</code>
247      *    and <code>&gt;= index0</code>
248      * @throws IllegalArgumentException if <code>index1</code> is less than <code>index0</code>
249      * @throws IndexOutOfBoundsException if either <code>index0</code>
250      *    or <code>index1</code> are negative or if <code>index0</code> is greater than
251      *    or equal to <code>size()</code> or if <code>index1</code> is strictly greater
252      *    than <code>size()</code>
253      */
254     public void flip(final long index0, final long index1)
255     {
256         if (index0 >= size)
257         {
258             throw new IndexOutOfBoundsException(index0 + " >= " + size);
259         }
260         if (index1 > size)
261         {
262             throw new IndexOutOfBoundsException(index1 + " > " + size);
263         }
264         bitset.flip(index0, index1);
265     }
266 
267     /**
268      * Assign all values in this 1D bit matrix to <code>value</code>.
269      *
270      * @param value value
271      * @return this 1D bit matrix, for convenience
272      */
273     public BitMatrix1D assign(final boolean value)
274     {
275         if (size > 0)
276         {
277             set(0, size, value);
278         }
279         return this;
280     }
281 
282     /**
283      * Return true if the specified 1D bit matrix has any bits set
284      * to true that are also set to true in this 1D bit matrix.
285      *
286      * @param other other 1D bit matrix, must not be null and must
287      *    be the same size as this 1D bit matrix
288      * @return true if the specified 1D bit matrix has any bits set
289      *    to true that are also set to true in this 1D bit matrix
290      * @throws IllegalArgumentException if <code>other</code> is null
291      *    or if <code>other</code> is not the same size as <code>this</code>
292      */
293     public boolean intersects(final BitMatrix1D other)
294     {
295         if (other == null)
296         {
297             throw new IllegalArgumentException("other must not be null");
298         }
299         if (size != other.size())
300         {
301             throw new IllegalArgumentException("this and other must be the same size");
302         }
303         return bitset.intersects(other.bitset);
304     }
305 
306     /**
307      * Perform a logical <b>AND</b> of this 1D bit matrix
308      * and the specified 1D bit matrix.
309      *
310      * @param other other 1D bit matrix, must not be null and must
311      *    be the same size as this 1D bit matrix
312      * @return this 1D bit matrix, for convenience
313      * @throws IllegalArgumentException if <code>other</code> is null
314      *    or if <code>other</code> is not the same size as <code>this</code>
315      */
316     public BitMatrix1D and(final BitMatrix1D other)
317     {
318         if (other == null)
319         {
320             throw new IllegalArgumentException("other must not be null");
321         }
322         if (!(size == other.size()))
323         {
324             throw new IllegalArgumentException("this and other must be the same size");
325         }
326         bitset.and(other.bitset);
327         return this;
328     }
329 
330     /**
331      * Clear all of the bits in this 1D bit matrix whose corresponding
332      * bit is set in the specified 1D bit matrix.
333      *
334      * @param other other 1D bit matrix, must not be null and must
335      *    be the same size as this 1D bit matrix
336      * @return this 1D bit matrix, for convenience
337      * @throws IllegalArgumentException if <code>other</code> is null
338      *    or if <code>other</code> is not the same size as <code>this</code>
339      */
340     public BitMatrix1D andNot(final BitMatrix1D other)
341     {
342         if (other == null)
343         {
344             throw new IllegalArgumentException("other must not be null");
345         }
346         if (size != other.size())
347         {
348             throw new IllegalArgumentException("this and other must be the same size");
349         }
350         bitset.andNot(other.bitset);
351         return this;
352     }
353 
354     /**
355      * Perform a logical <b>OR</b> of this 1D bit matrix
356      * and the specified 1D bit matrix.
357      *
358      * @param other other 1D bit matrix, must not be null and must
359      *    be the same size as this 1D bit matrix
360      * @return this 1D bit matrix, for convenience
361      * @throws IllegalArgumentException if <code>other</code> is null
362      *    or if <code>other</code> is not the same size as <code>this</code>
363      */
364     public BitMatrix1D or(final BitMatrix1D other)
365     {
366         if (other == null)
367         {
368             throw new IllegalArgumentException("other must not be null");
369         }
370         if (size != other.size())
371         {
372             throw new IllegalArgumentException("this and other must be the same size");
373         }
374         bitset.or(other.bitset);
375         return this;
376     }
377 
378     /**
379      * Perform a logical <b>XOR</b> of this 1D bit matrix
380      * and the specified 1D bit matrix.
381      *
382      * @param other other 1D bit matrix, must not be null and must
383      *    be the same size as this 1D bit matrix
384      * @return this 1D bit matrix, for convenience
385      * @throws IllegalArgumentException if <code>other</code> is null
386      *    or if <code>other</code> is not the same size as <code>this</code>
387      */
388     public BitMatrix1D xor(final BitMatrix1D other)
389     {
390         if (other == null)
391         {
392             throw new IllegalArgumentException("other must not be null");
393         }
394         if (size != other.size())
395         {
396             throw new IllegalArgumentException("this and other must be the same size");
397         }
398         bitset.xor(other.bitset);
399         return this;
400     }
401 
402     /**
403      * Apply the specified procedure to each index in this 1D bit
404      * matrix with a bit equal to the specified value.
405      *
406      * @param value value
407      * @param procedure procedure, must not be null
408      */
409     public void forEach(final boolean value, final UnaryProcedure<Long> procedure)
410     {
411         if (procedure == null)
412         {
413             throw new IllegalArgumentException("procedure must not be null");
414         }
415         if (value)
416         {
417             for (long i = bitset.nextSetBit(0); i >= 0; i = bitset.nextSetBit(i + 1))
418             {
419                 procedure.run(Long.valueOf(i));
420             }
421         }
422         else
423         {
424             // less efficient
425             for (long i = 0; i < size; i++)
426             {
427                 if (!bitset.getQuick(i))
428                 {
429                     procedure.run(Long.valueOf(i));
430                 }
431             }
432         }
433     }
434 
435     /** {@inheritDoc} */
436     public boolean equals(final Object o)
437     {
438         if (o == null)
439         {
440             return false;
441         }
442         if (!(o instanceof BitMatrix1D))
443         {
444             return false;
445         }
446 
447         BitMatrix1D bm = (BitMatrix1D) o;
448 
449         if (size != bm.size())
450         {
451             return false;
452         }
453         return bitset.equals(bm.bitset);
454     }
455 
456     /** {@inheritDoc} */
457     public int hashCode()
458     {
459         int hashCode = 37;
460         hashCode = 17 * hashCode + (int) (size ^ (size >>> 32));
461         hashCode = 17 * hashCode + bitset.hashCode();
462         return hashCode;
463     }
464 }