001 /**
002 * Copyright (c) 2010, SIB. All rights reserved.
003 *
004 * SIB (Swiss Institute of Bioinformatics) - http://www.isb-sib.ch Host -
005 * https://sourceforge.net/projects/javaprotlib/
006 *
007 * Redistribution and use in source and binary forms, with or without
008 * modification, are permitted provided that the following conditions are met:
009 * Redistributions of source code must retain the above copyright notice, this
010 * list of conditions and the following disclaimer. Redistributions in binary
011 * form must reproduce the above copyright notice, this list of conditions and
012 * the following disclaimer in the documentation and/or other materials provided
013 * with the distribution. Neither the name of the SIB/GENEBIO nor the names of
014 * its contributors may be used to endorse or promote products derived from this
015 * software without specific prior written permission.
016 *
017 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
018 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
019 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
020 * ARE DISCLAIMED. IN NO EVENT SHALL SIB/GENEBIO BE LIABLE FOR ANY DIRECT,
021 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
022 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
023 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
024 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
026 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027 */
028 package org.expasy.jpl.commons.collection;
029
030
031 import org.expasy.jpl.commons.base.builder.BuilderException;
032 import org.expasy.jpl.commons.base.builder.InstanceBuilder;
033
034
035 /**
036 * A {@code IntegerSequence} in mathematics is a sequence (i.e., an ordered
037 * list) of integers. It is represented as an interval of numbers between a and
038 * b, including a and excluding b, it is often denoted [a,b[.
039 *
040 * @author nikitin
041 *
042 * @version 1.0
043 *
044 */
045 public final class IntegerSequence {
046
047 private final Interval interval;
048 private final int by;
049 private final int size;
050 private boolean isUpperBoundIncluded;
051
052 public static class Builder implements InstanceBuilder<IntegerSequence> {
053
054 private Interval interval;
055 private int from = -1;
056 private int to = -1;
057 private int by = 1;
058 private int size;
059 private boolean isIncludeUpperBound;
060 private boolean revert = false;
061
062 public Builder(final int from, final int to) {
063 this.from = from;
064 this.to = to;
065 }
066
067 /**
068 * Alternative builder
069 *
070 * Convert double interval to integer interval: a, b -> floor(1),
071 * ceil(b)
072 */
073 public Builder(final Interval interval) {
074 this.interval = interval.clone();
075 }
076
077 public Builder revert() {
078 this.revert = true;
079 return this;
080 }
081
082 public Builder by(final int by) {
083 this.by = by;
084 return this;
085 }
086
087 public IntegerSequence build() throws BuilderException {
088 if (by == 0) {
089 throw new BuilderException("by " + by + " cannot be " + "null");
090 }
091
092 if (interval == null) {
093 if (from > to) {
094 int tmp = to;
095 to = from;
096 from = tmp;
097 }
098
099 interval = new Interval.Builder(from, to).build();
100 }
101
102 double from = interval.getLowerBound();
103 double to = interval.getUpperBound();
104
105 if (revert) {
106 double tmp = to;
107 to = from;
108 from = tmp;
109 }
110
111 boolean isNewInterval = false;
112
113 if (Math.ceil(from) != from) {
114 // interval of integers only
115 from = Math.ceil(interval.getLowerBound());
116 isNewInterval = true;
117 }
118
119 if (Math.floor(to) != to) {
120 // interval of integers only
121 to = Math.floor(interval.getUpperBound());
122 isNewInterval = true;
123 isIncludeUpperBound = true;
124 }
125
126 if (isNewInterval) {
127 Interval.Builder builder = new Interval.Builder(from, to);
128
129 if (isIncludeUpperBound) {
130 builder.includeUpperBound();
131 }
132 interval = builder.build();
133 }
134
135 isIncludeUpperBound =
136 IntegerSequence.isUpperBoundIncluded(interval, by);
137
138 size =
139 IntegerSequence.computeSize(interval, by, isIncludeUpperBound);
140
141 return new IntegerSequence(this);
142 }
143 }
144
145 public IntegerSequence(final Builder builder) {
146 interval = builder.interval;
147 by = builder.by;
148 size = builder.size;
149 isUpperBoundIncluded = builder.isIncludeUpperBound;
150 }
151
152 private IntegerSequence() {
153 interval = Interval.emptyInstance();
154 by = 0;
155 size = 0;
156 }
157
158 public static IntegerSequence emptyInstance() {
159 return new IntegerSequence();
160 }
161
162 public final int getFrom() {
163 return (int) interval.getLowerBound();
164 }
165
166 public final int getTo() {
167 return (int) interval.getUpperBound();
168 }
169
170 public final int getBy() {
171 return by;
172 }
173
174 public final int size() {
175 return size;
176 }
177
178 public int[] toInts() {
179 final int[] ints = new int[size];
180
181 int uby = (by > 0) ? by : -by;
182
183 if (interval.isLowerBoundIncluded()) {
184 ints[0] = (int) interval.getLowerBound();
185 }
186
187 int j = 1;
188 for (int i = (int) interval.getLowerBound() + uby; j < size
189 && i < interval.getUpperBound(); i += uby) {
190 ints[j++] = i;
191 }
192
193 if (isUpperBoundIncluded) {
194 ints[j] = (int) interval.getUpperBound();
195 }
196
197 if (by < 0) {
198 PrimitiveArrayUtils.reverse(ints);
199 }
200 return ints;
201 }
202
203 public boolean isEmpty() {
204 if (interval.isEmpty()) {
205 return true;
206 }
207
208 int aby = (by > 0) ? by : -by;
209
210 // interval with integers
211 if (interval.getRange() <= aby) {
212 return !interval.isLowerBoundIncluded() && !isUpperBoundIncluded;
213 }
214
215 return false;
216 }
217
218 public static boolean isUpperBoundIncluded(Interval interval, int by) {
219
220 if (interval.isUpperBoundIncluded()) {
221 int aby = (by > 0) ? by : -by;
222
223 int modLower = ((int) interval.getLowerBound()) % aby;
224 int modUpper = ((int) interval.getUpperBound()) % aby;
225
226 if (modLower == modUpper) {
227 return true;
228 }
229 }
230 return false;
231 }
232
233 /**
234 * Compute the number of integers in the sequence form an interval.
235 *
236 * @param interval the sequence interval.
237 * @param by the step sequence.
238 * @return the size.
239 */
240 public static int computeSize(Interval interval, int by,
241 boolean upperBoundIncluded) {
242
243 double value;
244
245 int aby = (by > 0) ? by : -by;
246
247 int minus = 1;
248
249 // always
250 if (aby > 1) {
251 if (interval.getLowerBound() % 2 == 0) {
252 minus = 2;
253 }
254 }
255
256 // only internal ints
257 value = (interval.getRange() - minus) / (double) aby;
258
259 int size = (int) Math.round(value);
260
261 if (interval.isLowerBoundIncluded()) {
262 size++;
263 }
264
265 if (upperBoundIncluded) {
266 size++;
267 }
268
269 return size;
270 }
271
272 @Override
273 public String toString() {
274 return interval.toString();
275 }
276
277 }