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    }