View Javadoc
1   //******************************************************************************
2   //
3   // File:    LongRange.java
4   // Package: edu.rit.util
5   // Unit:    Class edu.rit.util.LongRange
6   //
7   // This Java source file is copyright (C) 2007 by Alan Kaminsky. All rights
8   // reserved. For further information, contact the author, Alan Kaminsky, at
9   // ark@cs.rit.edu.
10  //
11  // This Java source file is part of the Parallel Java Library ("PJ"). PJ is free
12  // software; you can redistribute it and/or modify it under the terms of the GNU
13  // General Public License as published by the Free Software Foundation; either
14  // version 3 of the License, or (at your option) any later version.
15  //
16  // PJ is distributed in the hope that it will be useful, but WITHOUT ANY
17  // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
18  // A PARTICULAR PURPOSE. See the GNU General Public License for more details.
19  //
20  // Linking this library statically or dynamically with other modules is making a
21  // combined work based on this library. Thus, the terms and conditions of the GNU
22  // General Public License cover the whole combination.
23  //
24  // As a special exception, the copyright holders of this library give you
25  // permission to link this library with independent modules to produce an
26  // executable, regardless of the license terms of these independent modules, and
27  // to copy and distribute the resulting executable under terms of your choice,
28  // provided that you also meet, for each linked independent module, the terms
29  // and conditions of the license of that module. An independent module is a module
30  // which is not derived from or based on this library. If you modify this library,
31  // you may extend this exception to your version of the library, but you are not
32  // obligated to do so. If you do not wish to do so, delete this exception
33  // statement from your version.
34  //
35  // A copy of the GNU General Public License is provided in the file gpl.txt. You
36  // may also obtain a copy of the GNU General Public License on the World Wide
37  // Web at http://www.gnu.org/licenses/gpl.html.
38  //
39  //******************************************************************************
40  package edu.rit.util;
41  
42  import java.io.Externalizable;
43  import java.io.IOException;
44  import java.io.ObjectInput;
45  import java.io.ObjectOutput;
46  import java.io.Serial;
47  
48  /**
49   * Class LongRange provides a range of type <code>long</code>. A range object has
50   * the following attributes: <B>lower bound</B> <I>L</I>, <B>upper bound</B>
51   * <I>U</I>, <B>stride</B> <I>S</I>, and <B>length</B> <I>N</I>. A range object
52   * represents the following set of integers: {<I>L</I>, <I>L</I>+<I>S</I>,
53   * <I>L</I>+2*<I>S</I>, . . . , <I>L</I>+(<I>N</I>-1)*<I>S</I>}, where <I>U</I>
54   * = <I>L</I>+(<I>N</I>-1)*<I>S</I>.
55   * <P>
56   * You construct a range object by specifying the lower bound, upper bound, and
57   * stride. If the stride is omitted, the default stride is 1. The length is
58   * determined automatically. If the lower bound is greater than the upper bound,
59   * the range's length is 0 (an empty range).
60   * <P>
61   * You can use a range object to control a for loop like this:
62   * <PRE>
63   *     LongRange range = new LongRange (0, N-1);
64   *     long lb = range.lb();
65   *     long ub = range.ub();
66   *     for (long i = lb; i &lt;= ub; ++ i)
67   *         . . .
68   * </PRE> Note that the range is from <code>lb()</code> to <code>ub()</code> inclusive,
69   * so the appropriate test in the for loop is <code>i &lt;= ub</code>. Also note
70   * that it usually reduces the running time to call <code>ub()</code> once, store
71   * the result in a local variable, and use the local variable in the for loop
72   * test, than to call <code>ub()</code> directly in the for loop test.
73   * <P>
74   * You can use a range object with a stride greater than 1 to control a for loop
75   * like this:
76   * <PRE>
77   *     LongRange range = new LongRange (0, N-1, 2);
78   *     long lb = range.lb();
79   *     long ub = range.ub();
80   *     long stride = range.stride();
81   *     for (long i = lb; i &lt;= ub; i += stride)
82   *         . . .
83   * </PRE>
84   *
85   * @author Alan Kaminsky
86   * @version 30-May-2007
87   */
88  public class LongRange
89          implements Externalizable {
90  
91  // Hidden data members.
92      @Serial
93      private static final long serialVersionUID = 9196521188817114486L;
94  
95      long lb;
96      long stride;
97      long length;
98      long ub;
99  
100 // Exported constructors.
101     /**
102      * Construct a new range object representing an empty range.
103      */
104     public LongRange() {
105         this.lb = 0;
106         this.stride = 1;
107         this.length = 0;
108         setUb();
109     }
110 
111     /**
112      * Construct a new range object with the given lower bound and upper bound.
113      * The stride is 1. The range object represents the following set of
114      * integers: {<I>L</I>, <I>L</I>+1, <I>L</I>+2, . . . , <I>U</I>}. The
115      * range's length <I>N</I> is <I>U</I>-<I>L</I>+1.
116      * <P>
117      * <I>Note:</I> <I>L</I> &gt; <I>U</I> is allowed and stands for an empty
118      * range.
119      *
120      * @param lb Lower bound <I>L</I>.
121      * @param ub Upper bound <I>U</I>.
122      */
123     public LongRange(long lb,
124             long ub) {
125         this.lb = lb;
126         this.stride = 1;
127         this.length = Math.max(ub - lb + 1, 0);
128         setUb();
129     }
130 
131     /**
132      * Construct a new range object with the given lower bound, upper bound, and
133      * stride. The stride must be greater than or equal to 1. The range object
134      * represents the following set of integers: {<I>L</I>, <I>L</I>+<I>S</I>,
135      * <I>L</I>+2*<I>S</I>, . . . , <I>L</I>+(<I>N</I>-1)*<I>S</I>}, where the
136      * range's length <I>N</I> is such that <I>L</I>+(<I>N</I>-1)*<I>S</I> is
137      * the largest integer less than or equal to <I>U</I>. Note that the actual
138      * upper bound of the range, <I>L</I>+(<I>N</I>-1)*<I>S</I>, may not be the
139      * same as <I>U</I>.
140      * <P>
141      * <I>Note:</I> <I>L</I> &gt; <I>U</I> is allowed and stands for an empty
142      * range.
143      *
144      * @param lb Lower bound <I>L</I>.
145      * @param ub Upper bound <I>U</I>.
146      * @param stride Stride <I>S</I> &gt;= 1.
147      * @exception IllegalArgumentException (unchecked exception) Thrown if
148      * <I>S</I> &lt; 1.
149      */
150     public LongRange(long lb,
151             long ub,
152             long stride) {
153         if (stride < 1) {
154             throw new IllegalArgumentException("LongRange(): stride = " + stride + " illegal");
155         }
156         this.lb = lb;
157         this.stride = stride;
158         this.length = Math.max((ub - lb + stride) / stride, 0);
159         setUb();
160     }
161 
162     /**
163      * Construct a new range object that is a copy of the given range object.
164      *
165      * @param range Range object to copy.
166      */
167     public LongRange(LongRange range) {
168         this.lb = range.lb;
169         this.stride = range.stride;
170         this.length = range.length;
171         this.ub = range.ub;
172     }
173 
174 // Exported operations.
175     /**
176      * Returns this range's lower bound.
177      *
178      * @return Lower bound.
179      */
180     public long lb() {
181         return lb;
182     }
183 
184     /**
185      * Returns this range's upper bound.
186      *
187      * @return Upper bound.
188      */
189     public long ub() {
190         return ub;
191     }
192 
193     /**
194      * Returns this range's stride.
195      *
196      * @return Stride.
197      */
198     public long stride() {
199         return stride;
200     }
201 
202     /**
203      * Returns this range's length.
204      *
205      * @return Length.
206      */
207     public long length() {
208         return length;
209     }
210 
211     /**
212      * Determine if this range contains the given value. This range contains the
213      * given value if <code>this.lb()</code> &lt;= <code>val</code> &lt;=
214      * <code>this.ub()</code>. (The stride does not affect the outcome.)
215      *
216      * @param value Value to test.
217      * @return True if this range contains the given <code>value</code>, false
218      * otherwise.
219      */
220     public boolean contains(long value) {
221         return this.lb <= value && value <= this.ub;
222     }
223 
224     /**
225      * Determine if this range contains the given range. This range contains the
226      * given range if <code>this.lb()</code> &lt;= <code>range.lb()</code> and
227      * <code>range.ub()</code> &lt;= <code>this.ub()</code>. (The strides do not affect
228      * the outcome.)
229      *
230      * @param range Range to test.
231      * @return True if this range contains the given <code>range</code>, false
232      * otherwise.
233      */
234     public boolean contains(LongRange range) {
235         return this.lb <= range.lb && range.ub <= this.ub;
236     }
237 
238     /**
239      * Partition this range and return one subrange. This range is split up into
240      * subranges; the <code>size</code> argument specifies the number of subranges.
241      * This range is divided as equally as possible among the subranges; the
242      * lengths of the subranges differ by at most 1. The subranges are numbered
243      * 0, 1, . . . <code>size-1</code>. This method returns the subrange whose
244      * number is <code>rank</code>.
245      * <P>
246      * Note that if <code>size</code> is greater than the length of this range, the
247      * returned subrange may be empty.
248      *
249      * @param size Number of subranges, <code>size</code> &gt;= 1.
250      * @param rank Rank of the desired subrange, 0 &lt;= <code>rank</code> &lt;
251      * <code>size</code>.
252      * @return Subrange.
253      * @exception IllegalArgumentException (unchecked exception) Thrown if
254      * <code>size</code> or <code>rank</code> is out of bounds.
255      */
256     public LongRange subrange(int size,
257             int rank) {
258         // Verify preconditions.
259         if (size < 1) {
260             throw new IllegalArgumentException("LongRange.subrange(): size = " + size + " illegal");
261         }
262         if (0 > rank || rank >= size) {
263             throw new IllegalArgumentException("LongRange.subrange(): rank = " + rank + " illegal");
264         }
265 
266         // Split this range.
267         LongRange result = new LongRange();
268         long sublen = this.length / size;
269         int subrem = (int) (this.length % size);
270         if (rank < subrem) {
271             ++sublen;
272             result.lb = this.lb + (rank * sublen) * this.stride;
273         } else {
274             result.lb = this.lb + (subrem + rank * sublen) * this.stride;
275         }
276         result.stride = this.stride;
277         result.length = sublen;
278         result.setUb();
279         return result;
280     }
281 
282     /**
283      * Partition this range and return all the subranges. This range is split up
284      * into subranges; the <code>size</code> argument specifies the number of
285      * subranges. This range is divided as equally as possible among the
286      * subranges; the lengths of the subranges differ by at most 1. The
287      * subranges are returned in an array with indexes 0, 1, . . .
288      * <code>size-1</code>.
289      * <P>
290      * Note that if <code>size</code> is greater than the length of this range, some
291      * of the returned subranges may be empty.
292      *
293      * @param size Number of subranges, size &gt;= 1.
294      * @return Array of subranges.
295      * @exception IllegalArgumentException (unchecked exception) Thrown if
296      * <code>size</code> is out of bounds.
297      */
298     public LongRange[] subranges(int size) {
299         // Verify preconditions.
300         if (size < 1) {
301             throw new IllegalArgumentException("LongRange.subranges(): size = " + size + " illegal");
302         }
303 
304         // Allocate storage for subranges.
305         LongRange[] result = new LongRange[size];
306 
307         // Compute subranges.
308         long sublen = this.length / size;
309         int subrem = (int) (this.length % size);
310         long x = this.lb;
311         ++sublen;
312         for (int i = 0; i < subrem; ++i) {
313             LongRange result_i = new LongRange();
314             result_i.lb = x;
315             x += sublen * this.stride;
316             result_i.stride = this.stride;
317             result_i.length = sublen;
318             result_i.setUb();
319             result[i] = result_i;
320         }
321         --sublen;
322         for (int i = subrem; i < size; ++i) {
323             LongRange result_i = new LongRange();
324             result_i.lb = x;
325             x += sublen * this.stride;
326             result_i.stride = this.stride;
327             result_i.length = sublen;
328             result_i.setUb();
329             result[i] = result_i;
330         }
331 
332         return result;
333     }
334 
335     /**
336      * Slice off a chunk of this range and return the chunk. Considering this
337      * range as a set of integers from the lower bound to the upper bound, the
338      * first <code>N1</code> integers are sliced off and discarded, then the next
339      * <code>N2</code> integers are sliced off to form a chunk, and the chunk is
340      * returned. If after removing the first <code>N1</code> integers there are
341      * fewer than <code>N2</code> integers left, a chunk consisting of all the
342      * remaining integers is returned; this may be an empty chunk.
343      *
344      * @param N1 Number of integers to discard (must be &gt;= 0).
345      * @param N2 Number of integers to include in the chunk (must be &gt;= 0).
346      * @return Chunk.
347      * @exception IllegalArgumentException (unchecked exception) Thrown if
348      * <code>N1</code> or <code>N2</code> is out of bounds.
349      */
350     public LongRange chunk(long N1,
351             long N2) {
352         // Verify preconditions.
353         if (N1 < 0) {
354             throw new IllegalArgumentException("LongRange.chunk(): N1 = " + N1 + " illegal");
355         }
356         if (N2 < 0) {
357             throw new IllegalArgumentException("LongRange.chunk(): N2 = " + N2 + " illegal");
358         }
359 
360         LongRange result = new LongRange();
361         result.lb = this.lb + N1 * this.stride;
362         result.stride = this.stride;
363         result.length = Math.min(N2, Math.max(0, this.length - N1));
364         result.setUb();
365         return result;
366     }
367 
368     /**
369      * {@inheritDoc}
370      *
371      * Determine if this range is equal to the given object. Two ranges are
372      * equal if they both have the same lower bound, stride, and length.
373      */
374     public boolean equals(Object obj) {
375         return obj instanceof LongRange
376                 && this.lb == ((LongRange) obj).lb
377                 && this.stride == ((LongRange) obj).stride
378                 && this.length == ((LongRange) obj).length;
379     }
380 
381     /**
382      * Returns a hash code for this range.
383      *
384      * @return a int.
385      */
386     public int hashCode() {
387         return (int) ((((this.lb << 10) + this.stride) << 10) + this.length);
388     }
389 
390     /**
391      * Returns a string version of this range. If the stride is 1, the format is
392      * <code>"<I>L</I>..<I>U</I>"</code>, where <I>L</I> is the lower bound and
393      * <I>U</I> is the upper bound. If the stride is greater than 1, the format
394      * is <code>"<I>L</I>..<I>U</I>;<I>S</I>"</code>, where <I>L</I> is the lower
395      * bound, <I>U</I> is the upper bound, and <I>S</I> is the stride.
396      *
397      * @return a {@link java.lang.String} object.
398      */
399     public String toString() {
400         StringBuilder b = new StringBuilder();
401         b.append(lb);
402         b.append("..");
403         b.append(ub);
404         if (stride > 1) {
405             b.append(';');
406             b.append(stride);
407         }
408         return b.toString();
409     }
410 
411     /**
412      * {@inheritDoc}
413      *
414      * Write this range to the given object output stream.
415      * @exception IOException Thrown if an I/O error occurred.
416      */
417     public void writeExternal(ObjectOutput out)
418             throws IOException {
419         out.writeLong(lb);
420         out.writeLong(stride);
421         out.writeLong(length);
422     }
423 
424     /**
425      * {@inheritDoc}
426      *
427      * Read this range from the given object input stream.
428      * @exception IOException Thrown if an I/O error occurred.
429      */
430     public void readExternal(ObjectInput in)
431             throws IOException {
432         lb = in.readLong();
433         stride = in.readLong();
434         length = in.readLong();
435         setUb();
436     }
437 
438 // Hidden operations.
439     /**
440      * Set the upper bound of this range based on the lower bound, stride, and
441      * length.
442      */
443     private void setUb() {
444         ub = lb + (length - 1) * stride;
445     }
446 
447 // Unit test main program.
448 //	/**
449 //	 * Unit test main program.
450 //	 */
451 //	public static void main
452 //		(String[] args)
453 //		{
454 //		if (args.length != 4)
455 //			{
456 //			System.err.println ("Usage: edu.rit.util.LongRange <lb> <ub> <stride> <size>");
457 //			System.exit (1);
458 //			}
459 //		long lb = Long.parseLong (args[0]);
460 //		long ub = Long.parseLong (args[1]);
461 //		long stride = Long.parseLong (args[2]);
462 //		int size = Integer.parseInt (args[3]);
463 //		LongRange range = new LongRange (lb, ub, stride);
464 //		System.out.println
465 //			("LongRange = " + range + ", length = " + range.length());
466 //		for (int rank = 0; rank < size; ++ rank)
467 //			{
468 //			LongRange subrange = range.subrange (size, rank);
469 //			System.out.println
470 //				("Subrange rank " + rank + " = " + subrange +
471 //				 ", length = " + subrange.length());
472 //			}
473 //		LongRange[] subranges = range.subranges (size);
474 //		for (int rank = 0; rank < size; ++ rank)
475 //			{
476 //			System.out.println
477 //				("Subranges[" + rank + "] = " + subranges[rank] +
478 //				 ", length = " + subranges[rank].length());
479 //			}
480 //		}
481 }