blob: b1d19124b1a0c86da012ada2ce798ee1bbbb8773 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
package org.apache.commons.math.linear;
import java.io.Serializable;
import org.apache.commons.math.MathRuntimeException;
import org.apache.commons.math.exception.util.LocalizedFormats;
import org.apache.commons.math.util.OpenIntToDoubleHashMap;
import org.apache.commons.math.util.OpenIntToDoubleHashMap.Iterator;
import org.apache.commons.math.util.FastMath;
/**
* This class implements the {@link RealVector} interface with a {@link OpenIntToDoubleHashMap} backing store.
* @version $Revision: 1073262 $ $Date: 2011-02-22 10:02:25 +0100 (mar. 22 févr. 2011) $
* @since 2.0
*/
public class OpenMapRealVector extends AbstractRealVector implements SparseRealVector, Serializable {
/** Default Tolerance for having a value considered zero. */
public static final double DEFAULT_ZERO_TOLERANCE = 1.0e-12;
/** Serializable version identifier. */
private static final long serialVersionUID = 8772222695580707260L;
/** Entries of the vector. */
private final OpenIntToDoubleHashMap entries;
/** Dimension of the vector. */
private final int virtualSize;
/** Tolerance for having a value considered zero. */
private final double epsilon;
/**
* Build a 0-length vector.
* <p>Zero-length vectors may be used to initialized construction of vectors
* by data gathering. We start with zero-length and use either the {@link
* #OpenMapRealVector(OpenMapRealVector, int)} constructor
* or one of the <code>append</code> method ({@link #append(double)}, {@link
* #append(double[])}, {@link #append(RealVector)}) to gather data
* into this vector.</p>
*/
public OpenMapRealVector() {
this(0, DEFAULT_ZERO_TOLERANCE);
}
/**
* Construct a (dimension)-length vector of zeros.
* @param dimension size of the vector
*/
public OpenMapRealVector(int dimension) {
this(dimension, DEFAULT_ZERO_TOLERANCE);
}
/**
* Construct a (dimension)-length vector of zeros, specifying zero tolerance.
* @param dimension Size of the vector
* @param epsilon The tolerance for having a value considered zero
*/
public OpenMapRealVector(int dimension, double epsilon) {
virtualSize = dimension;
entries = new OpenIntToDoubleHashMap(0.0);
this.epsilon = epsilon;
}
/**
* Build a resized vector, for use with append.
* @param v The original vector
* @param resize The amount to resize it
*/
protected OpenMapRealVector(OpenMapRealVector v, int resize) {
virtualSize = v.getDimension() + resize;
entries = new OpenIntToDoubleHashMap(v.entries);
epsilon = v.epsilon;
}
/**
* Build a vector with known the sparseness (for advanced use only).
* @param dimension The size of the vector
* @param expectedSize The expected number of non-zero entries
*/
public OpenMapRealVector(int dimension, int expectedSize) {
this(dimension, expectedSize, DEFAULT_ZERO_TOLERANCE);
}
/**
* Build a vector with known the sparseness and zero tolerance setting (for advanced use only).
* @param dimension The size of the vector
* @param expectedSize The expected number of non-zero entries
* @param epsilon The tolerance for having a value considered zero
*/
public OpenMapRealVector(int dimension, int expectedSize, double epsilon) {
virtualSize = dimension;
entries = new OpenIntToDoubleHashMap(expectedSize, 0.0);
this.epsilon = epsilon;
}
/**
* Create from a double array.
* Only non-zero entries will be stored
* @param values The set of values to create from
*/
public OpenMapRealVector(double[] values) {
this(values, DEFAULT_ZERO_TOLERANCE);
}
/**
* Create from a double array, specifying zero tolerance.
* Only non-zero entries will be stored
* @param values The set of values to create from
* @param epsilon The tolerance for having a value considered zero
*/
public OpenMapRealVector(double[] values, double epsilon) {
virtualSize = values.length;
entries = new OpenIntToDoubleHashMap(0.0);
this.epsilon = epsilon;
for (int key = 0; key < values.length; key++) {
double value = values[key];
if (!isDefaultValue(value)) {
entries.put(key, value);
}
}
}
/**
* Create from a Double array.
* Only non-zero entries will be stored
* @param values The set of values to create from
*/
public OpenMapRealVector(Double[] values) {
this(values, DEFAULT_ZERO_TOLERANCE);
}
/**
* Create from a Double array.
* Only non-zero entries will be stored
* @param values The set of values to create from
* @param epsilon The tolerance for having a value considered zero
*/
public OpenMapRealVector(Double[] values, double epsilon) {
virtualSize = values.length;
entries = new OpenIntToDoubleHashMap(0.0);
this.epsilon = epsilon;
for (int key = 0; key < values.length; key++) {
double value = values[key].doubleValue();
if (!isDefaultValue(value)) {
entries.put(key, value);
}
}
}
/**
* Copy constructor.
* @param v The instance to copy from
*/
public OpenMapRealVector(OpenMapRealVector v) {
virtualSize = v.getDimension();
entries = new OpenIntToDoubleHashMap(v.getEntries());
epsilon = v.epsilon;
}
/**
* Generic copy constructor.
* @param v The instance to copy from
*/
public OpenMapRealVector(RealVector v) {
virtualSize = v.getDimension();
entries = new OpenIntToDoubleHashMap(0.0);
epsilon = DEFAULT_ZERO_TOLERANCE;
for (int key = 0; key < virtualSize; key++) {
double value = v.getEntry(key);
if (!isDefaultValue(value)) {
entries.put(key, value);
}
}
}
/**
* Get the entries of this instance.
* @return entries of this instance
*/
private OpenIntToDoubleHashMap getEntries() {
return entries;
}
/**
* Determine if this value is within epsilon of zero.
* @param value The value to test
* @return <code>true</code> if this value is within epsilon to zero, <code>false</code> otherwise
* @since 2.1
*/
protected boolean isDefaultValue(double value) {
return FastMath.abs(value) < epsilon;
}
/** {@inheritDoc} */
@Override
public RealVector add(RealVector v) throws IllegalArgumentException {
checkVectorDimensions(v.getDimension());
if (v instanceof OpenMapRealVector) {
return add((OpenMapRealVector) v);
} else {
return super.add(v);
}
}
/**
* Optimized method to add two OpenMapRealVectors. Copies the larger vector, iterates over the smaller.
* @param v Vector to add with
* @return The sum of <code>this</code> with <code>v</code>
* @throws IllegalArgumentException If the dimensions don't match
*/
public OpenMapRealVector add(OpenMapRealVector v) throws IllegalArgumentException{
checkVectorDimensions(v.getDimension());
boolean copyThis = entries.size() > v.entries.size();
OpenMapRealVector res = copyThis ? this.copy() : v.copy();
Iterator iter = copyThis ? v.entries.iterator() : entries.iterator();
OpenIntToDoubleHashMap randomAccess = copyThis ? entries : v.entries;
while (iter.hasNext()) {
iter.advance();
int key = iter.key();
if (randomAccess.containsKey(key)) {
res.setEntry(key, randomAccess.get(key) + iter.value());
} else {
res.setEntry(key, iter.value());
}
}
return res;
}
/**
* Optimized method to append a OpenMapRealVector.
* @param v vector to append
* @return The result of appending <code>v</code> to self
*/
public OpenMapRealVector append(OpenMapRealVector v) {
OpenMapRealVector res = new OpenMapRealVector(this, v.getDimension());
Iterator iter = v.entries.iterator();
while (iter.hasNext()) {
iter.advance();
res.setEntry(iter.key() + virtualSize, iter.value());
}
return res;
}
/** {@inheritDoc} */
public OpenMapRealVector append(RealVector v) {
if (v instanceof OpenMapRealVector) {
return append((OpenMapRealVector) v);
}
return append(v.getData());
}
/** {@inheritDoc} */
public OpenMapRealVector append(double d) {
OpenMapRealVector res = new OpenMapRealVector(this, 1);
res.setEntry(virtualSize, d);
return res;
}
/** {@inheritDoc} */
public OpenMapRealVector append(double[] a) {
OpenMapRealVector res = new OpenMapRealVector(this, a.length);
for (int i = 0; i < a.length; i++) {
res.setEntry(i + virtualSize, a[i]);
}
return res;
}
/**
* {@inheritDoc}
* @since 2.1
*/
@Override
public OpenMapRealVector copy() {
return new OpenMapRealVector(this);
}
/**
* Optimized method to compute the dot product with an OpenMapRealVector.
* Iterates over the smaller of the two.
* @param v The vector to compute the dot product with
* @return The dot product of <code>this</code> and <code>v</code>
* @throws IllegalArgumentException If the dimensions don't match
*/
public double dotProduct(OpenMapRealVector v) throws IllegalArgumentException {
checkVectorDimensions(v.getDimension());
boolean thisIsSmaller = entries.size() < v.entries.size();
Iterator iter = thisIsSmaller ? entries.iterator() : v.entries.iterator();
OpenIntToDoubleHashMap larger = thisIsSmaller ? v.entries : entries;
double d = 0;
while(iter.hasNext()) {
iter.advance();
d += iter.value() * larger.get(iter.key());
}
return d;
}
/** {@inheritDoc} */
@Override
public double dotProduct(RealVector v) throws IllegalArgumentException {
if(v instanceof OpenMapRealVector) {
return dotProduct((OpenMapRealVector)v);
} else {
return super.dotProduct(v);
}
}
/** {@inheritDoc} */
public OpenMapRealVector ebeDivide(RealVector v) throws IllegalArgumentException {
checkVectorDimensions(v.getDimension());
OpenMapRealVector res = new OpenMapRealVector(this);
Iterator iter = res.entries.iterator();
while (iter.hasNext()) {
iter.advance();
res.setEntry(iter.key(), iter.value() / v.getEntry(iter.key()));
}
return res;
}
/** {@inheritDoc} */
@Override
public OpenMapRealVector ebeDivide(double[] v) throws IllegalArgumentException {
checkVectorDimensions(v.length);
OpenMapRealVector res = new OpenMapRealVector(this);
Iterator iter = res.entries.iterator();
while (iter.hasNext()) {
iter.advance();
res.setEntry(iter.key(), iter.value() / v[iter.key()]);
}
return res;
}
/** {@inheritDoc} */
public OpenMapRealVector ebeMultiply(RealVector v) throws IllegalArgumentException {
checkVectorDimensions(v.getDimension());
OpenMapRealVector res = new OpenMapRealVector(this);
Iterator iter = res.entries.iterator();
while (iter.hasNext()) {
iter.advance();
res.setEntry(iter.key(), iter.value() * v.getEntry(iter.key()));
}
return res;
}
/** {@inheritDoc} */
@Override
public OpenMapRealVector ebeMultiply(double[] v) throws IllegalArgumentException {
checkVectorDimensions(v.length);
OpenMapRealVector res = new OpenMapRealVector(this);
Iterator iter = res.entries.iterator();
while (iter.hasNext()) {
iter.advance();
res.setEntry(iter.key(), iter.value() * v[iter.key()]);
}
return res;
}
/** {@inheritDoc} */
public OpenMapRealVector getSubVector(int index, int n) throws MatrixIndexException {
checkIndex(index);
checkIndex(index + n - 1);
OpenMapRealVector res = new OpenMapRealVector(n);
int end = index + n;
Iterator iter = entries.iterator();
while (iter.hasNext()) {
iter.advance();
int key = iter.key();
if (key >= index && key < end) {
res.setEntry(key - index, iter.value());
}
}
return res;
}
/** {@inheritDoc} */
@Override
public double[] getData() {
double[] res = new double[virtualSize];
Iterator iter = entries.iterator();
while (iter.hasNext()) {
iter.advance();
res[iter.key()] = iter.value();
}
return res;
}
/** {@inheritDoc} */
public int getDimension() {
return virtualSize;
}
/**
* Optimized method to compute distance.
* @param v The vector to compute distance to
* @return The distance from <code>this</code> and <code>v</code>
* @throws IllegalArgumentException If the dimensions don't match
*/
public double getDistance(OpenMapRealVector v) throws IllegalArgumentException {
Iterator iter = entries.iterator();
double res = 0;
while (iter.hasNext()) {
iter.advance();
int key = iter.key();
double delta;
delta = iter.value() - v.getEntry(key);
res += delta * delta;
}
iter = v.getEntries().iterator();
while (iter.hasNext()) {
iter.advance();
int key = iter.key();
if (!entries.containsKey(key)) {
final double value = iter.value();
res += value * value;
}
}
return FastMath.sqrt(res);
}
/** {@inheritDoc} */
@Override
public double getDistance(RealVector v) throws IllegalArgumentException {
checkVectorDimensions(v.getDimension());
if (v instanceof OpenMapRealVector) {
return getDistance((OpenMapRealVector) v);
}
return getDistance(v.getData());
}
/** {@inheritDoc} */
@Override
public double getDistance(double[] v) throws IllegalArgumentException {
checkVectorDimensions(v.length);
double res = 0;
for (int i = 0; i < v.length; i++) {
double delta = entries.get(i) - v[i];
res += delta * delta;
}
return FastMath.sqrt(res);
}
/** {@inheritDoc} */
public double getEntry(int index) throws MatrixIndexException {
checkIndex(index);
return entries.get(index);
}
/**
* Distance between two vectors.
* <p>This method computes the distance consistent with
* L<sub>1</sub> norm, i.e. the sum of the absolute values of
* elements differences.</p>
* @param v vector to which distance is requested
* @return distance between two vectors.
*/
public double getL1Distance(OpenMapRealVector v) {
double max = 0;
Iterator iter = entries.iterator();
while (iter.hasNext()) {
iter.advance();
double delta = FastMath.abs(iter.value() - v.getEntry(iter.key()));
max += delta;
}
iter = v.getEntries().iterator();
while (iter.hasNext()) {
iter.advance();
int key = iter.key();
if (!entries.containsKey(key)) {
double delta = FastMath.abs(iter.value());
max += FastMath.abs(delta);
}
}
return max;
}
/** {@inheritDoc} */
@Override
public double getL1Distance(RealVector v) throws IllegalArgumentException {
checkVectorDimensions(v.getDimension());
if (v instanceof OpenMapRealVector) {
return getL1Distance((OpenMapRealVector) v);
}
return getL1Distance(v.getData());
}
/** {@inheritDoc} */
@Override
public double getL1Distance(double[] v) throws IllegalArgumentException {
checkVectorDimensions(v.length);
double max = 0;
for (int i = 0; i < v.length; i++) {
double delta = FastMath.abs(getEntry(i) - v[i]);
max += delta;
}
return max;
}
/**
* Optimized method to compute LInfDistance.
* @param v The vector to compute from
* @return the LInfDistance
*/
private double getLInfDistance(OpenMapRealVector v) {
double max = 0;
Iterator iter = entries.iterator();
while (iter.hasNext()) {
iter.advance();
double delta = FastMath.abs(iter.value() - v.getEntry(iter.key()));
if (delta > max) {
max = delta;
}
}
iter = v.getEntries().iterator();
while (iter.hasNext()) {
iter.advance();
int key = iter.key();
if (!entries.containsKey(key)) {
if (iter.value() > max) {
max = iter.value();
}
}
}
return max;
}
/** {@inheritDoc} */
@Override
public double getLInfDistance(RealVector v) throws IllegalArgumentException {
checkVectorDimensions(v.getDimension());
if (v instanceof OpenMapRealVector) {
return getLInfDistance((OpenMapRealVector) v);
}
return getLInfDistance(v.getData());
}
/** {@inheritDoc} */
@Override
public double getLInfDistance(double[] v) throws IllegalArgumentException {
checkVectorDimensions(v.length);
double max = 0;
for (int i = 0; i < v.length; i++) {
double delta = FastMath.abs(getEntry(i) - v[i]);
if (delta > max) {
max = delta;
}
}
return max;
}
/** {@inheritDoc} */
public boolean isInfinite() {
boolean infiniteFound = false;
Iterator iter = entries.iterator();
while (iter.hasNext()) {
iter.advance();
final double value = iter.value();
if (Double.isNaN(value)) {
return false;
}
if (Double.isInfinite(value)) {
infiniteFound = true;
}
}
return infiniteFound;
}
/** {@inheritDoc} */
public boolean isNaN() {
Iterator iter = entries.iterator();
while (iter.hasNext()) {
iter.advance();
if (Double.isNaN(iter.value())) {
return true;
}
}
return false;
}
/** {@inheritDoc} */
@Override
public OpenMapRealVector mapAdd(double d) {
return copy().mapAddToSelf(d);
}
/** {@inheritDoc} */
@Override
public OpenMapRealVector mapAddToSelf(double d) {
for (int i = 0; i < virtualSize; i++) {
setEntry(i, getEntry(i) + d);
}
return this;
}
/** {@inheritDoc} */
@Override
public RealMatrix outerProduct(double[] v) throws IllegalArgumentException {
checkVectorDimensions(v.length);
RealMatrix res = new OpenMapRealMatrix(virtualSize, virtualSize);
Iterator iter = entries.iterator();
while (iter.hasNext()) {
iter.advance();
int row = iter.key();
double value = iter.value();
for (int col = 0; col < virtualSize; col++) {
res.setEntry(row, col, value * v[col]);
}
}
return res;
}
/** {@inheritDoc} */
public RealVector projection(RealVector v) throws IllegalArgumentException {
checkVectorDimensions(v.getDimension());
return v.mapMultiply(dotProduct(v) / v.dotProduct(v));
}
/** {@inheritDoc} */
@Override
public OpenMapRealVector projection(double[] v) throws IllegalArgumentException {
checkVectorDimensions(v.length);
return (OpenMapRealVector) projection(new OpenMapRealVector(v));
}
/** {@inheritDoc} */
public void setEntry(int index, double value) throws MatrixIndexException {
checkIndex(index);
if (!isDefaultValue(value)) {
entries.put(index, value);
} else if (entries.containsKey(index)) {
entries.remove(index);
}
}
/** {@inheritDoc} */
@Override
public void setSubVector(int index, RealVector v) throws MatrixIndexException {
checkIndex(index);
checkIndex(index + v.getDimension() - 1);
setSubVector(index, v.getData());
}
/** {@inheritDoc} */
@Override
public void setSubVector(int index, double[] v) throws MatrixIndexException {
checkIndex(index);
checkIndex(index + v.length - 1);
for (int i = 0; i < v.length; i++) {
setEntry(i + index, v[i]);
}
}
/** {@inheritDoc} */
@Override
public void set(double value) {
for (int i = 0; i < virtualSize; i++) {
setEntry(i, value);
}
}
/**
* Optimized method to subtract OpenMapRealVectors.
* @param v The vector to subtract from <code>this</code>
* @return The difference of <code>this</code> and <code>v</code>
* @throws IllegalArgumentException If the dimensions don't match
*/
public OpenMapRealVector subtract(OpenMapRealVector v) throws IllegalArgumentException{
checkVectorDimensions(v.getDimension());
OpenMapRealVector res = copy();
Iterator iter = v.getEntries().iterator();
while (iter.hasNext()) {
iter.advance();
int key = iter.key();
if (entries.containsKey(key)) {
res.setEntry(key, entries.get(key) - iter.value());
} else {
res.setEntry(key, -iter.value());
}
}
return res;
}
/** {@inheritDoc} */
@Override
public OpenMapRealVector subtract(RealVector v) throws IllegalArgumentException {
checkVectorDimensions(v.getDimension());
if (v instanceof OpenMapRealVector) {
return subtract((OpenMapRealVector) v);
}
return subtract(v.getData());
}
/** {@inheritDoc} */
@Override
public OpenMapRealVector subtract(double[] v) throws IllegalArgumentException {
checkVectorDimensions(v.length);
OpenMapRealVector res = new OpenMapRealVector(this);
for (int i = 0; i < v.length; i++) {
if (entries.containsKey(i)) {
res.setEntry(i, entries.get(i) - v[i]);
} else {
res.setEntry(i, -v[i]);
}
}
return res;
}
/** {@inheritDoc} */
@Override
public OpenMapRealVector unitVector() {
OpenMapRealVector res = copy();
res.unitize();
return res;
}
/** {@inheritDoc} */
@Override
public void unitize() {
double norm = getNorm();
if (isDefaultValue(norm)) {
throw MathRuntimeException.createArithmeticException(LocalizedFormats.CANNOT_NORMALIZE_A_ZERO_NORM_VECTOR);
}
Iterator iter = entries.iterator();
while (iter.hasNext()) {
iter.advance();
entries.put(iter.key(), iter.value() / norm);
}
}
/** {@inheritDoc} */
@Override
public double[] toArray() {
return getData();
}
/** {@inheritDoc}
* <p> Implementation Note: This works on exact values, and as a result
* it is possible for {@code a.subtract(b)} to be the zero vector, while
* {@code a.hashCode() != b.hashCode()}.</p>
*/
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(epsilon);
result = prime * result + (int) (temp ^ (temp >>> 32));
result = prime * result + virtualSize;
Iterator iter = entries.iterator();
while (iter.hasNext()) {
iter.advance();
temp = Double.doubleToLongBits(iter.value());
result = prime * result + (int) (temp ^ (temp >>32));
}
return result;
}
/**
* <p> Implementation Note: This performs an exact comparison, and as a result
* it is possible for {@code a.subtract(b}} to be the zero vector, while
* {@code a.equals(b) == false}.</p>
* {@inheritDoc}
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof OpenMapRealVector)) {
return false;
}
OpenMapRealVector other = (OpenMapRealVector) obj;
if (virtualSize != other.virtualSize) {
return false;
}
if (Double.doubleToLongBits(epsilon) !=
Double.doubleToLongBits(other.epsilon)) {
return false;
}
Iterator iter = entries.iterator();
while (iter.hasNext()) {
iter.advance();
double test = other.getEntry(iter.key());
if (Double.doubleToLongBits(test) != Double.doubleToLongBits(iter.value())) {
return false;
}
}
iter = other.getEntries().iterator();
while (iter.hasNext()) {
iter.advance();
double test = iter.value();
if (Double.doubleToLongBits(test) != Double.doubleToLongBits(getEntry(iter.key()))) {
return false;
}
}
return true;
}
/**
*
* @return the percentage of none zero elements as a decimal percent.
* @deprecated as of 2.2 replaced by the correctly spelled {@link #getSparsity()}
*/
@Deprecated
public double getSparcity() {
return getSparsity();
}
/**
*
* @return the percentage of none zero elements as a decimal percent.
* @since 2.2
*/
public double getSparsity() {
return (double)entries.size()/(double)getDimension();
}
/** {@inheritDoc} */
@Override
public java.util.Iterator<Entry> sparseIterator() {
return new OpenMapSparseIterator();
}
/**
* Implementation of <code>Entry</code> optimized for OpenMap.
* <p>This implementation does not allow arbitrary calls to <code>setIndex</code>
* since the order that entries are returned is undefined.
*/
protected class OpenMapEntry extends Entry {
/** Iterator pointing to the entry. */
private final Iterator iter;
/** Build an entry from an iterator point to an element.
* @param iter iterator pointing to the entry
*/
protected OpenMapEntry(Iterator iter) {
this.iter = iter;
}
/** {@inheritDoc} */
@Override
public double getValue() {
return iter.value();
}
/** {@inheritDoc} */
@Override
public void setValue(double value) {
entries.put(iter.key(), value);
}
/** {@inheritDoc} */
@Override
public int getIndex() {
return iter.key();
}
}
/**
* Iterator class to do iteration over just the non-zero elements.
* <p>This implementation is fail-fast, so cannot be used to modify any zero element.
*
*/
protected class OpenMapSparseIterator implements java.util.Iterator<Entry> {
/** Underlying iterator. */
private final Iterator iter;
/** Current entry. */
private final Entry current;
/** Simple constructor. */
protected OpenMapSparseIterator() {
iter = entries.iterator();
current = new OpenMapEntry(iter);
}
/** {@inheritDoc} */
public boolean hasNext() {
return iter.hasNext();
}
/** {@inheritDoc} */
public Entry next() {
iter.advance();
return current;
}
/** {@inheritDoc} */
public void remove() {
throw new UnsupportedOperationException("Not supported");
}
}
}