001/**
002 * Copyright (c) 2011, The University of Southampton and the individual contributors.
003 * All rights reserved.
004 *
005 * Redistribution and use in source and binary forms, with or without modification,
006 * are permitted provided that the following conditions are met:
007 *
008 *   *  Redistributions of source code must retain the above copyright notice,
009 *      this list of conditions and the following disclaimer.
010 *
011 *   *  Redistributions in binary form must reproduce the above copyright notice,
012 *      this list of conditions and the following disclaimer in the documentation
013 *      and/or other materials provided with the distribution.
014 *
015 *   *  Neither the name of the University of Southampton nor the names of its
016 *      contributors may be used to endorse or promote products derived from this
017 *      software without specific prior written permission.
018 *
019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029 */
030package org.openimaj.ml.timeseries.series;
031
032import java.io.IOException;
033import java.io.PrintWriter;
034import java.util.Arrays;
035import java.util.Collection;
036import java.util.Iterator;
037import java.util.Scanner;
038
039import org.openimaj.io.ReadWriteableASCII;
040import org.openimaj.ml.timeseries.TimeSeries;
041import org.openimaj.ml.timeseries.TimeSeriesArithmaticOperator;
042import org.openimaj.ml.timeseries.collection.TimeSeriesCollectionAssignable;
043import org.openimaj.util.pair.IndependentPair;
044
045/**
046 * @author Sina Samangooei (ss@ecs.soton.ac.uk)
047 *
048 */
049public class DoubleTimeSeries extends TimeSeries<double[],Double,DoubleTimeSeries>
050        implements 
051                TimeSeriesArithmaticOperator<Double, DoubleTimeSeries>,
052                ReadWriteableASCII,
053                TimeSeriesCollectionAssignable<Double, DoubleTimeSeries>,
054                DoubleTimeSeriesProvider
055{
056
057        private long[] times;
058        private double[] data;
059        int size = 0;
060        
061        /**
062         * Convenience constructor, makes a time series with empty data of a given size
063         * @param i
064         */
065        public DoubleTimeSeries(int i) {
066                this.times = new long[i];
067                this.data = new double[i];
068                size = i;
069        }
070        /**
071         * Sets the times and data arrays backing this class 0 length
072         */
073        public DoubleTimeSeries() {
074                this.times = new long[0];
075                this.data = new double[0];
076                size = 0;
077        }
078        /**
079         * 
080         * @param times
081         * @param data
082         */
083        public DoubleTimeSeries(long[] times, double[] data) {
084                this.times = times;
085                this.data = data;
086                size = this.data.length;
087        }
088        private int[] findStartEnd(long time, int nbefore, int nafter){
089                int index = Arrays.binarySearch(times, time);
090                int fixed = index < 0 ? -1 * (index + 1) : index;
091                int start = 0;
092                int end = times.length - 1;
093                
094                start = fixed- nbefore;
095                // couldn't find it
096                if(index < 0){
097                        end = fixed + nafter;
098                }
099                // could
100                else{
101                        end = fixed + nafter + 1;
102                }
103                if(start < 0) start= 0;
104                if(end > times.length) end = times.length;
105                return new int[]{start,end};
106        }
107        @Override
108        public DoubleTimeSeries get(long time, int nbefore, int nafter) {
109                if(nbefore < 0 || nafter < 0)
110                {
111                        return new DoubleTimeSeries();
112                }
113                int[] startend = findStartEnd(time, nbefore, nafter);
114                double[] dataoutput = new double[startend[1] - startend[0]];
115                System.arraycopy(this.data, startend[0], dataoutput, 0, dataoutput.length);
116                long[] timeoutput = new long[startend[1] - startend[0]];
117                System.arraycopy(this.times, startend[0], timeoutput, 0, timeoutput.length);
118                DoubleTimeSeries output = newInstance(timeoutput,dataoutput);
119                return output;
120        }
121        
122        @Override
123        public DoubleTimeSeries get(long time, int nbefore, int nafter, DoubleTimeSeries output) {
124                int[] startend = findStartEnd(time, nbefore, nafter);
125                System.arraycopy(this.data, startend[0], output.data, 0, startend[1]-startend[0]);
126                System.arraycopy(this.times, startend[0], output.times, 0, startend[1]-startend[0]);
127                output.size = startend[1]-startend[0];
128                return output;
129        }
130
131        @Override
132        public DoubleTimeSeries get(long time, long threshbefore, long threshafter) {
133                if(threshafter < 0 || threshbefore < 0){
134                        return new DoubleTimeSeries();
135                }
136                int[] startend = findStartEnd(time, 0, 0);
137                int start = startend[0];
138                int end = start;
139                // Find the index range
140                while(start > 0 && times[start-1] >= time - threshbefore) start--;
141                while(end < times.length && times[end] <= time + threshafter) end++;
142                
143                double[] dataoutput = new double[end - start];
144                System.arraycopy(this.data, start, dataoutput, 0, dataoutput.length);
145                long[] timeoutput = new long[end - start];
146                System.arraycopy(this.times, start, timeoutput, 0, timeoutput.length);
147                DoubleTimeSeries output = newInstance(timeoutput,dataoutput);
148                return output;
149        }
150        
151        @Override
152        public DoubleTimeSeries get(long start, long end) {
153                return get(start,0,end-start);
154        }       
155
156        private DoubleTimeSeries newInstance(long[] timeoutput, double[] dataoutput) {
157                DoubleTimeSeries output = newInstance();
158                output.set(timeoutput, dataoutput);
159                return output;
160        }
161        @Override
162        public void set(long[] times, double[] data) {
163                this.times = times;
164                this.data = data;
165                this.size = data.length;
166        }
167        @Override
168        public long[] getTimes() {
169                return this.times;
170        }
171        @Override
172        public double[] getData() {
173                return this.data;
174        }
175        @Override
176        public DoubleTimeSeries newInstance() {
177                return new DoubleTimeSeries();
178        }
179        
180        @Override
181        public int size() {
182                return size;
183        }
184        @Override
185        public void internalAssign(DoubleTimeSeries interpolate) {
186                this.data = Arrays.copyOf(interpolate.data, interpolate.data.length);
187                this.times = Arrays.copyOf(interpolate.times, interpolate.times.length);
188                this.size = interpolate.size();
189        }
190        @Override
191        public String toString() {
192                StringBuffer sb = new StringBuffer();
193                String lf = "%d => %.5f\n";
194                for (int i = 0; i < this.size(); i++) {
195                        long time = this.times[i];
196                        double data = this.data[i];
197                        sb.append(String.format(lf, time,data));
198                }
199                return sb.toString();
200        }
201        @Override
202        public Iterator<IndependentPair<Long, Double>> iterator() {
203                return new Iterator<IndependentPair<Long,Double>>() {
204                        int index = 0;
205                        @Override
206                        public boolean hasNext() {
207                                return index < DoubleTimeSeries.this.size;
208                        }
209
210                        @Override
211                        public IndependentPair<Long, Double> next() {
212                                IndependentPair<Long, Double> toret = IndependentPair.pair(DoubleTimeSeries.this.times[index],DoubleTimeSeries.this.data[index]);
213                                index++;
214                                return toret;
215                        }
216
217                        @Override
218                        public void remove() {
219                        }
220                };
221        }
222        @Override
223        public void readASCII(Scanner in) throws IOException {
224                this.size = in.nextInt();
225                this.data = new double[size];
226                this.times = new long[size];
227                for (int i = 0; i < size; i++) {
228                        times[i] = in.nextLong();
229                        data[i] = in.nextDouble();
230                }
231                
232        }
233        @Override
234        public String asciiHeader() {
235                return "";
236        }
237        @Override
238        public void writeASCII(PrintWriter out) throws IOException {
239                out.print(this.size() + " ");
240                for (IndependentPair<Long, Double> timedouble: this) {
241                        out.print(timedouble.firstObject() + " " + timedouble.secondObject() + " ");
242                }
243        }
244        @Override
245        public DoubleTimeSeries newInstance(Collection<Long> time,Collection<Double> data) {
246                DoubleTimeSeries d = new DoubleTimeSeries();
247                d.internalAssign(time, data);
248                return d;
249        }
250        @Override
251        public void internalAssign(Collection<Long> time, Collection<Double> data) {
252                this.times = new long[time.size()];
253                this.data = new double[time.size()];
254                this.size = data.size();
255                int i = 0;
256                for (Long l : time)  this.times[i++] = l;
257                i = 0;
258                for (Double d : data)  this.data[i++] = d;
259        }
260        @Override
261        public void internalAssign(long[] times, double[] data) {
262                this.times = times;
263                this.data = data;
264                this.size = times.length;
265                
266        }
267        @Override
268        public Double zero() {
269                return 0d;
270        }
271        @Override
272        public Double sum() {
273                double s = 0;
274                for (int i = 0; i < this.data.length; i++) {
275                        s += this.data[i];
276                }
277                return s;
278        }
279        @Override
280        public DoubleTimeSeries doubleTimeSeries() {
281                return this;
282        }       
283}