/******************************************************************************* * Mission Control Technologies, Copyright (c) 2009-2012, United States Government * as represented by the Administrator of the National Aeronautics and Space * Administration. All rights reserved. * * The MCT platform is licensed under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0. * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. * * MCT includes source code licensed under additional open source licenses. See * the MCT Open Source Licenses file included with this distribution or the About * MCT Licenses dialog available at runtime from the MCT Help menu for additional * information. *******************************************************************************/ package plotter; /** * Stores doubles in a double-ended circular buffer. * @author Adam Crume */ public class DoubleDataDouble extends DoubleData { /** Default initial capacity. */ private static final int DEFAULT_CAPACITY = 8; /** Contains the data. */ private double[] data; /** * Creates a buffer with the specified capacity. * @param capacity capacity of the buffer */ public DoubleDataDouble(int capacity) { data = new double[capacity]; } /** * Creates a buffer with default capacity. */ public DoubleDataDouble() { this(DEFAULT_CAPACITY); } /** * Adds an element to the buffer. * @param d element to add */ public void add(double d) { if(length == data.length) { // If we don't have enough space, allocate a larger array setCapacity(data.length * 2); } data[wrap(offset + length, data.length)] = d; length++; } /** * Adds elements to the buffer. * @param d data to add * @param off offset within <code>d</code> to start copying from * @param len number of elements to copy */ public void add(double[] d, int off, int len) { if(off < 0 || len < 0 || off + len > d.length) { throw new IndexOutOfBoundsException("d.length = " + d.length + ", off = " + off + ", len = " + len); } if(length + len > data.length) { // If we don't have enough space, allocate a larger array int newlen = data.length; while(newlen < length + len) { newlen *= 2; } setCapacity(newlen); } int start1 = wrap(offset + length, data.length); int end1 = Math.min(start1 + len, data.length); System.arraycopy(d, off, data, start1, end1 - start1); if(end1 - start1 < len) { System.arraycopy(d, off + end1 - start1, data, 0, len - end1 + start1); } length += len; } /** * Adds elements to the buffer. * @param d data to add * @param off offset within <code>d</code> to start copying from * @param len number of elements to copy */ public void add(DoubleData d, int off, int len) { if(off < 0 || len < 0 || off + len > d.length) { throw new IndexOutOfBoundsException("d.getLength() = " + d.length + ", off = " + off + ", len = " + len); } DoubleDataDouble d2 = (DoubleDataDouble) d; // TODO: What if it is a different type? int off2 = wrap(d.offset + off, d2.data.length); int available = d2.data.length - off2; if(available < len) { add(d2.data, off2, available); add(d2.data, 0, len - available); } else { add(d2.data, off2, len); } } /** * Copies data from the source object. * @param src object to copy data from * @param srcoff index within src to copy data from * @param dstoff index within this to copy data to * @param len number of elements to copy */ public void copyFrom(DoubleData src, int srcoff, int dstoff, int len) { if(srcoff < 0 || len < 0 || srcoff + len > src.length) { throw new IndexOutOfBoundsException("src.getLength() = " + src.length + ", srcoff = " + srcoff + ", len = " + len); } if(dstoff < 0 || dstoff + len > length) { throw new IndexOutOfBoundsException("dstoff = " + dstoff + ", len = " + len + ", getLength() = " + length); } DoubleDataDouble src2 = (DoubleDataDouble) src; // TODO: What if it is a different type? int off2 = wrap(src.offset + srcoff, src2.data.length); int available = src2.data.length - off2; if(available < len) { // Which order depends only if src == data. int dstoff2 = wrap(dstoff + offset, data.length); if(dstoff2 < off2 && dstoff2 > wrap(src.offset + srcoff + len, src2.data.length)) { copyFrom(src2.data, off2, dstoff, available); copyFrom(src2.data, 0, dstoff + available, len - available); } else { copyFrom(src2.data, 0, dstoff + available, len - available); copyFrom(src2.data, off2, dstoff, available); } } else { copyFrom(src2.data, off2, dstoff, len); } } /** * Copies data from the source object. * @param src object to copy data from * @param srcoff index within src to copy data from * @param dstoff index within this to copy data to * @param len number of elements to copy */ public void copyFrom(double[] src, int srcoff, int dstoff, int len) { if(srcoff < 0 || len < 0 || srcoff + len > src.length) { throw new IndexOutOfBoundsException("d.length = " + src.length + ", srcoff = " + srcoff + ", len = " + len); } if(dstoff < 0 || dstoff + len > length) { throw new IndexOutOfBoundsException("dstoff = " + dstoff + ", len = " + len + ", getLength() = " + length); } int off2 = wrap(offset + dstoff, data.length); int available = data.length - off2; if(available < len) { // Which order depends only if src == data. if(off2 < srcoff + len) { System.arraycopy(src, srcoff + available, data, 0, len - available); System.arraycopy(src, srcoff, data, off2, available); } else { System.arraycopy(src, srcoff, data, off2, available); System.arraycopy(src, srcoff + available, data, 0, len - available); } } else { System.arraycopy(src, srcoff, data, off2, len); } } /** * Inserts a value into the buffer. * @param index position for the new value * @param d value to add */ public void insert(int index, double d) { if(index < 0 || index > length) { throw new IndexOutOfBoundsException("Index out of bounds: " + index + ", length is " + length); } if(length == data.length) { setCapacity(data.length * 2); } // TODO: See if this mess can be simplified int split = data.length - offset; if(index < length / 2) { // Insert near head; shift early elements left if(index <= split) { if(offset == 0) { data[data.length - 1] = data[0]; System.arraycopy(data, offset + 1, data, offset, index); } else { System.arraycopy(data, offset, data, offset - 1, index); } } else { System.arraycopy(data, offset, data, offset - 1, split); data[data.length - 1] = data[0]; System.arraycopy(data, 1, data, 0, index - split - 1); } offset--; while(offset < 0) { offset += data.length; } } else { // Insert near tail; shift late elements right if(index < split) { if(split <= length) { System.arraycopy(data, 0, data, 1, length - split); data[0] = data[data.length - 1]; System.arraycopy(data, index + offset, data, index + offset + 1, split - index - 1); } else { System.arraycopy(data, index + offset, data, index + offset + 1, length - index); } } else { System.arraycopy(data, index - split, data, index - split + 1, length - index); } } length++; data[wrap(offset + index, data.length)] = d; } /** * Inserts elements into the buffer. * @param index position for the new value * @param d data to add * @param off offset within d to start copying data from * @param len number of elements to insert */ public void insert(int index, DoubleData d, int off, int len) { if(index < 0 || index > length) { throw new IndexOutOfBoundsException("Index out of bounds: " + index + ", length is " + length); } if(off < 0 || len < 0 || off + len > d.length) { throw new IndexOutOfBoundsException("Index out of bounds: off = " + off + ", len = " + len + ", d.length = " + d.length); } int newlen = length + len; int cap = data.length; while(newlen > cap) { cap *= 2; } if(cap != data.length) { setCapacity(cap); } if(index < length / 2) { // Insert near head; shift early elements left length += len; offset -= len; // implicitly shifts elements right while(offset < 0) { offset += data.length; } copyFrom(this, len, 0, index); } else { // Insert near tail; shift late elements right length += len; copyFrom(this, index, index + len, getLength() - index - len); } copyFrom(d, off, index, len); } /** * Adds elements to the beginning of the buffer. * @param d data to add * @param off offset within <code>d</code> to start copying from * @param len number of elements to add */ public void prepend(double[] d, int off, int len) { if(len < 0 || off < 0 || off + len > d.length) { throw new IndexOutOfBoundsException("d.length = " + d.length + ", off = " + off + ", len = " + len); } if(length + len > data.length) { int newlen = data.length; while(newlen < length + len) { newlen *= 2; } setCapacity(newlen); } int start1 = offset - len; while(start1 < 0) { start1 += data.length; } int end1 = Math.min(start1 + len, data.length); System.arraycopy(d, off, data, start1, end1 - start1); if(end1 - start1 < len) { System.arraycopy(d, off + end1 - start1, data, 0, len - end1 + start1); } length += len; offset = start1; } /** * Adds elements to the beginning of the buffer. * @param d data to add * @param off offset within <code>d</code> to start copying from * @param len number of elements to add */ public void prepend(DoubleData d, int off, int len) { if(len < 0 || off < 0 || off + len > d.length) { throw new IndexOutOfBoundsException("d.getLength() = " + d.length + ", off = " + off + ", len = " + len); } DoubleDataDouble d2 = (DoubleDataDouble) d; // TODO: What if it is a different type? int off2 = wrap(d.offset + off, d2.data.length); int available = d2.data.length - off2; if(available < len) { prepend(d2.data, 0, len - available); prepend(d2.data, off2, available); } else { prepend(d2.data, off2, len); } } /** * Returns the element at the given index. * @param index index of the element * @return value at that index */ public double get(int index) { if(index < 0 || index >= length) { throw new IndexOutOfBoundsException("Index out of bounds: " + index + ", length is " + length); } return data[wrap(offset + index, data.length)]; } /** * Sets the element at the given index * @param index index of the element * @param d value to set at that index */ public void set(int index, double d) { if(index < 0 || index >= length) { throw new IndexOutOfBoundsException("Index out of bounds: " + index + ", length is " + length); } data[wrap(offset + index, data.length)] = d; } /** * Returns the capacity, or the maximum length the buffer can grow to without resizing. * @return the capacity */ public int getCapacity() { return data.length; } /** * Sets the buffer's capacity. * The new capacity cannot be less than the amount of data currently in the buffer. * @param capacity new capacity * @throws IllegalArgumentException if the requested capacity is less than the length */ public void setCapacity(int capacity) { if(capacity < length) { throw new IllegalArgumentException("Cannot set capacity less than the current length. Remove elements first. length = " + length + ", new capacity = " + capacity); } if(capacity == data.length) { return; } double[] data2 = new double[capacity]; int available = data.length - offset; if(length <= available) { System.arraycopy(data, offset, data2, 0, length); } else { System.arraycopy(data, offset, data2, 0, available); System.arraycopy(data, 0, data2, available, length - available); } data = data2; offset = 0; } /** * Removes elements from the front of the buffer. * @param count number of elements to remove */ public void removeFirst(int count) { if(count < 0) { throw new IllegalArgumentException("Count cannot be negative: " + count); } if(count > length) { throw new IllegalArgumentException("Trying to remove " + count + " elements, but only contains " + length); } offset = wrap(offset + count, data.length); length -= count; } /** * Searches the data for an insertion point. * Assumes the data is sorted. * Assumes the data does not contain NaNs and that the argument is not NaN. * Runs in O(log(n)) time, where n is the length (as defined by {@link #getLength()}). * @param d value to search for * @return index of the search key, if it is contained in the array; otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>. * The <i>insertion point</i> is defined as the point at which the key would be inserted into the array: * the index of the first element greater than the key, or <tt>a.length</tt> if all elements in the array are less than the specified key. * Note that this guarantees that the return value will be >= 0 if and only if the key is found. */ public int binarySearch(double d) { if(length == 0) { return -1; } int min = 0; int max = length; while(max - min > 1) { int mid = (min + max) / 2; double x = data[wrap(offset + mid, data.length)]; if(x < d) { min = mid; } else if(x > d) { max = mid; } else { // assume x==d return mid; } } double x = data[wrap(offset + min, data.length)]; if(x == d) { return min; } else if(x < d) { return -min - 2; } else { return -min - 1; } } /** * Searches the data for an insertion point. * Assumes the data is sorted. * Assumes the data does not contain NaNs and that the argument is not NaN. * Runs on average in O(log(log(n))) time, where n is the length (as defined by {@link #getLength()}). * However, in the worst case (where the values are exponentially distributed), may run in O(n) time. * @param d value to search for * @return index of the search key, if it is contained in the array; otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>. * The <i>insertion point</i> is defined as the point at which the key would be inserted into the array: * the index of the first element greater than the key, or <tt>a.length</tt> if all elements in the array are less than the specified key. * Note that this guarantees that the return value will be >= 0 if and only if the key is found. */ public int dictionarySearch(double d) { if(length == 0) { return -1; } int min = 0; int max = length - 1; while(true) { double minval = get(min); if(minval > d) { return -min - 1; } double maxval = get(max); if(maxval < d) { return -max - 2; } int mid = min + (int) ((d - minval) * (max - min) / (maxval - minval)); double midval = get(mid); if(midval < d) { min = mid + 1; } else if(midval > d) { max = mid - 1; } else { return mid; } } } @Override public DoubleDataDouble clone() { DoubleDataDouble d = (DoubleDataDouble) super.clone(); d.data = data.clone(); return d; } }