001/** 002 * ArrayParam -- An array of point values in a case. 003 * 004 * Copyright (C) 2003-2017, by Joseph A. Huwaldt. All rights reserved. 005 * 006 * This library is free software; you can redistribute it and/or modify it under the terms 007 * of the GNU Lesser General Public License as published by the Free Software Foundation; 008 * either version 2 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, but WITHOUT ANY 011 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 012 * PARTICULAR PURPOSE. See the GNU Library General Public License for more details. 013 * 014 * You should have received a copy of the GNU Lesser General Public License along with 015 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - 016 * Suite 330, Boston, MA 02111-1307, USA. Or visit: http://www.gnu.org/licenses/lgpl.html 017 */ 018package jahuwaldt.js.datareader; 019 020import jahuwaldt.js.param.Parameter; 021import java.text.MessageFormat; 022import java.util.Arrays; 023import java.util.List; 024import java.util.Objects; 025import static java.util.Objects.requireNonNull; 026import javax.measure.converter.ConversionException; 027import javax.measure.converter.UnitConverter; 028import javax.measure.quantity.Quantity; 029import javax.measure.unit.Unit; 030import javolution.context.ArrayFactory; 031import javolution.context.ObjectFactory; 032import javolution.util.FastTable; 033import org.jscience.mathematics.number.Float64; 034import org.jscience.mathematics.vector.Float64Vector; 035 036/** 037 * An array of parameter values in a {@link DataCase case} or run. 038 * 039 * <p> Modified by: Joseph A. Huwaldt </p> 040 * 041 * @author Joseph A. Huwaldt, Date: March 5, 2003 042 * @version March 18, 2017 043 * 044 * @param <Q> The Quantity or unit type of this array of values. 045 */ 046public final class ArrayParam<Q extends Quantity> extends UnitParameter<Q, ArrayParam> { 047 048 // The number of elements in the array (the size of the array). 049 private int _numElements = 0; 050 051 // The array of points in current units. 052 private double[] _values; 053 054 /** 055 * Resets the internal state of this object to its default values. 056 */ 057 @Override 058 protected void reset() { 059 super.reset(); 060 ArrayFactory.DOUBLES_FACTORY.recycle(_values); 061 _values = null; 062 _numElements = 0; 063 } 064 065 /** 066 * Recycles a parameter instance immediately (on the stack when executing in a 067 * StackContext). 068 * 069 * @param instance The instance to be recycled. 070 */ 071 public static void recycle(ArrayParam instance) { 072 FACTORY.recycle(instance); 073 } 074 075 /** 076 * Do not allow the default constructor to be used except by subclasses. 077 */ 078 private ArrayParam() { } 079 080 /** 081 * Construct an array with the specified name and units with the array values all set 082 * to zero. The name and the units may <em>not</em> be null. 083 * 084 * @param <Q> The quantity or unit type of this array of values. 085 * @param name The name of this array of parameters. 086 * @param unit The units to assume for each value in the array. 087 * @param size The size or number of elements in this array. 088 * @return An array with the specified name and units with the array values all set to 089 * zero. 090 */ 091 public static <Q extends Quantity> ArrayParam<Q> valueOf(CharSequence name, Unit<Q> unit, int size) { 092 ArrayParam<Q> param = ArrayParam.newInstance(name); 093 param._unit = requireNonNull(unit, MessageFormat.format(RESOURCES.getString("paramNullErr"),"unit")); 094 095 param._numElements = size; 096 param._values = ArrayFactory.DOUBLES_FACTORY.array(size); 097 Arrays.fill(param._values, 0, size, 0); 098 099 return param; 100 } 101 102 /** 103 * Construct an array with the specified name, array of values and units (name and 104 * units may <em>not</em> be null). 105 * 106 * @param <Q> The quantity or unit type of this array of values. 107 * @param name The name of this array of parameters. 108 * @param unit The units to assume for each value in the array. 109 * @param values The Java array of values to be stored in this ArrayParam object. 110 * @return An array with the specified name, array of values and units. 111 */ 112 public static <Q extends Quantity> ArrayParam<Q> valueOf(CharSequence name, Unit<Q> unit, double[] values) { 113 requireNonNull(unit, MessageFormat.format(RESOURCES.getString("paramNullErr"),"unit")); 114 requireNonNull(values, MessageFormat.format(RESOURCES.getString("paramNullErr"),"values")); 115 116 ArrayParam param = ArrayParam.newInstance(name); 117 param._unit = unit; 118 param._numElements = values.length; 119 120 // Defensively copy the supplied array. 121 param._values = ArrayFactory.DOUBLES_FACTORY.array(values.length); 122 System.arraycopy(values, 0, param._values, 0, values.length); 123 Arrays.fill(param._values, values.length, param._values.length, 0); 124 125 return param; 126 } 127 128 /** 129 * Construct an array with the specified name, list of values and units (name and 130 * units may <em>not</em> be null). 131 * 132 * @param <Q> The quantity or unit type of this array of values. 133 * @param name The name of this array of parameters. 134 * @param unit The units to assume for each value in the array. 135 * @param values The list of values to be stored in this array. 136 * @return An array with the specified name, list of values and units. 137 */ 138 public static <Q extends Quantity> ArrayParam<Q> valueOf(CharSequence name, Unit<Q> unit, List<Double> values) { 139 requireNonNull(unit, MessageFormat.format(RESOURCES.getString("paramNullErr"),"unit")); 140 requireNonNull(values, MessageFormat.format(RESOURCES.getString("paramNullErr"),"values")); 141 142 int numElements = values.size(); 143 ArrayParam param = ArrayParam.newInstance(name); 144 param._unit = unit; 145 param._numElements = numElements; 146 147 // Defensively copy the supplied array. 148 param._values = ArrayFactory.DOUBLES_FACTORY.array(numElements); 149 for (int i = 0; i < numElements; ++i) { 150 param._values[i] = values.get(i); 151 } 152 Arrays.fill(param._values, numElements, param._values.length, 0); 153 154 return param; 155 } 156 157 /** 158 * Return the number of elements in this array. 159 * 160 * @return The number of elements in this array. 161 */ 162 public int size() { 163 return _numElements; 164 } 165 166 /** 167 * Return the value of an element in the array in reference SI units. 168 * 169 * @param idx Index to the element in the array to be returned. 170 * @return The value of an element in the array in reference SI units. 171 * @throws IndexOutOfBoundsException if (index < 0) || (index > size()-1) 172 */ 173 public double getValueSI(int idx) { 174 if (idx >= _numElements) 175 throw new IndexOutOfBoundsException(MessageFormat.format( 176 RESOURCES.getString("idxOutOfBoundsErr"),idx,_numElements)); 177 178 return getUnit().toStandardUnit().convert(_values[idx]); 179 } 180 181 /** 182 * Return the value of an element in the array in the current units. 183 * 184 * @param idx Index to the point in the array to be returned. 185 * @return The value of an element in the array in the current units. 186 * @throws IndexOutOfBoundsException if (index < 0) || (index > size()-1) 187 */ 188 public double getValue(int idx) { 189 if (idx >= _numElements) 190 throw new IndexOutOfBoundsException(MessageFormat.format( 191 RESOURCES.getString("idxOutOfBoundsErr"),idx,_numElements)); 192 193 return _values[idx]; 194 } 195 196 /** 197 * Return the list of values as a Float64Vector, in reference SI units. 198 * 199 * @return The list of values as a Float64Vector, in reference SI units. 200 */ 201 public Float64Vector getValuesSI() { 202 203 FastTable<Float64> newValues = FastTable.newInstance(); 204 UnitConverter cvtr = getUnit().toStandardUnit(); 205 206 int length = _numElements; 207 for (int i = 0; i < length; ++i) 208 newValues.add(Float64.valueOf(cvtr.convert(_values[i]))); 209 210 Float64Vector vector = Float64Vector.valueOf(newValues); 211 FastTable.recycle(newValues); 212 213 return vector; 214 215 } 216 217 /** 218 * Return the list of values as a Float64Vector, in current units. 219 * 220 * @return The list of values as a Float64Vector, in current units. 221 */ 222 public Float64Vector getValues() { 223 FastTable<Float64> newValues = FastTable.newInstance(); 224 225 int length = _numElements; 226 for (int i = 0; i < length; ++i) 227 newValues.add(Float64.valueOf(_values[i])); 228 229 Float64Vector vector = Float64Vector.valueOf(newValues); 230 FastTable.recycle(newValues); 231 232 return vector; 233 } 234 235 /** 236 * Returns the equivalent to this parameter but stated in the specified unit. The 237 * values <em>are</em> converted to the new units. 238 * 239 * @param unit the unit of the parameter to be returned. 240 * @return an equivalent to this parameter but stated in the specified unit. 241 * @throws ConversionException if the current model does not allows for conversion to 242 * the specified unit. 243 */ 244 @Override 245 public ArrayParam<Q> to(Unit<Q> unit) throws ConversionException { 246 if (Objects.equals(_unit, requireNonNull(unit))) 247 return this; 248 UnitConverter cvtr = Parameter.converterOf(_unit, unit); 249 if (cvtr == UnitConverter.IDENTITY) { // No conversion necessary. 250 return this; 251 } 252 ArrayParam<Q> result = ArrayParam.newInstance(getName()); 253 int length = _numElements; 254 result._numElements = length; 255 result._unit = unit; 256 result._values = ArrayFactory.DOUBLES_FACTORY.array(length); 257 for (int i = 0; i < length; ++i) { 258 double value = cvtr.convert(_values[i]); 259 result._values[i] = value; 260 } 261 Arrays.fill(result._values, length, result._values.length, 0); 262 return result; 263 } 264 265 /** 266 * Returns the parameter that has the same values as this parameter but with the units 267 * changed (<em>without</em> converting the values). 268 * 269 * @param <R> The Quantity or unit type of the new unit. 270 * @param unit the unit of the parameter to be returned. 271 * @return an equivalent to this parameter but stated in the specified unit. 272 * @throws ConversionException if the current model does not allows for conversion to 273 * the specified unit. 274 */ 275 @Override 276 public <R extends Quantity> ArrayParam<R> changeTo(Unit<R> unit) throws ConversionException { 277 requireNonNull(unit, MessageFormat.format(RESOURCES.getString("paramNullErr"),"unit")); 278 if (Objects.equals(_unit, unit)) 279 return (ArrayParam<R>)this; 280 ArrayParam<R> result = (ArrayParam<R>)ArrayParam.copyOf(this); 281 result._unit = unit; 282 return result; 283 } 284 285 /** 286 * Returns a copy of this ArrayParam instance 287 * {@link javolution.context.AllocatorContext allocated} by the calling thread 288 * (possibly on the stack). 289 * 290 * @return an identical and independent copy of this parameter. 291 */ 292 @Override 293 public ArrayParam<Q> copy() { 294 return copyOf(this); 295 } 296 297 /** 298 * Method that sorts the values in this array in ascending numerical order. And 299 * returns the values as a new array. 300 * 301 * @return A new ArrayParam with the values sorted in ascending numerical order. 302 */ 303 public ArrayParam<Q> sort() { 304 ArrayParam<Q> P = copyOf(this); 305 Arrays.sort(P._values); 306 return P; 307 } 308 309 /** 310 * Compares the specified object with this parameter for strict equality same value, 311 * same units, same name, same user data. 312 * 313 * @param obj the object to compare with. 314 * @return <code>true</code> if this parameter is identical to that parameter; 315 * <code>false</code> otherwise. 316 */ 317 @Override 318 public boolean equals(Object obj) { 319 if (this == obj) 320 return true; 321 if ((obj == null) || (obj.getClass() != this.getClass())) 322 return false; 323 324 ArrayParam that = (ArrayParam)obj; 325 if (this._numElements != that._numElements) 326 return false; 327 else if (!equalArrays(this._values, that._values, this._numElements)) 328 return false; 329 330 return super.equals(obj); 331 } 332 333 private boolean equalArrays(double[] arr1, double[] arr2, int length) { 334 for (int i = 0; i < length; ++i) { 335 double v = arr1[i]; 336 if (arr2[i] != v) 337 return false; 338 } 339 return true; 340 } 341 342 /** 343 * Returns the hash code for this <code>ArrayParam</code>. 344 * 345 * @return the hash code value. 346 */ 347 @Override 348 public int hashCode() { 349 int hash = super.hashCode(); 350 351 hash = hash * 31 + _numElements; 352 hash = hash * 31 + Arrays.hashCode(_values); 353 354 return hash; 355 } 356 357 ////////////////////// 358 // Factory Creation // 359 ////////////////////// 360 /** 361 * Returns a new, preallocated or recycled <code>ArrayParam</code> instance (on the 362 * stack when executing in a <code>StackContext</code>) with the specified name, no 363 * units, and no values (zero size). 364 * 365 * @param name The name to be assigned to this parameter (may not be 366 * <code>null</code>). 367 * @return A new ArrayParam instance with the given name and not data (zero size). 368 */ 369 public static ArrayParam newInstance(CharSequence name) { 370 ArrayParam o = FACTORY.object(); 371 o._numElements = 0; 372 try { 373 o.setName(name); 374 } catch (NullPointerException e) { 375 FACTORY.recycle(o); 376 throw e; 377 } 378 return o; 379 } 380 381 private static final ObjectFactory<ArrayParam> FACTORY = new ObjectFactory<ArrayParam>() { 382 @Override 383 protected ArrayParam create() { 384 return new ArrayParam(); 385 } 386 387 @Override 388 protected void cleanup(ArrayParam obj) { 389 obj.reset(); 390 } 391 }; 392 393 private static ArrayParam copyOf(ArrayParam orig) { 394 ArrayParam param = ArrayParam.newInstance(orig.getName()); 395 param._unit = orig.getUnit(); 396 int length = orig._numElements; 397 param._numElements = length; 398 param._values = ArrayFactory.DOUBLES_FACTORY.array(length); 399 System.arraycopy(orig._values, 0, param._values, 0, length); 400 Arrays.fill(param._values, length, param._values.length, 0); 401 return param; 402 } 403}