content: /**
   * Copyright 2000-2009 DFKI GmbH.
   * All Rights Reserved.  Use is subject to license terms.
   * <p/>
   * This file is part of MARY TTS.
   * <p/>
   * MARY TTS is free software: you can redistribute it and/or modify
   * it under the terms of the GNU Lesser General Public License as published by
   * the Free Software Foundation, version 3 of the License.
   * <p/>
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU Lesser General Public License for more details.
   * <p/>
   * You should have received a copy of the GNU Lesser General Public License
   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   */
  package marytts.features;
  
  
  import java.io.DataOutput;
  import java.io.IOException;
  
  /**
   * Compact representation of a list of byte-valued, short-valued and float-valued
   * (continuous) features.
   * In the user interface, features are identified through one index number,
   * covering all three types of features. For example, a feature vector consisting
   * of three bytes, four shorts and two floats will have nine features.
   * Use getByteFeature(), getShortFeature() and getContinuousFeature() to
   * access the features as primitives; note that you have to respect the index
   * restrictions, i.e. in the example, calling getShortFeature(6) would be OK,
   * but getShortFeature(2) or getShortFeature(8) would throw an IndexOutOfBoundsException.
   * Use isShortFeature(i) to test whether a given feature is a short feature.
   * Alternatively, you can use an Object interface to access all features in
   * a uniform way: getFeature(i) will return a Number object for all valid indexes.
   *
   * @author Marc Schr&ouml;der
   */
  public class FeatureVector {
      public final int unitIndex;
  
      public final byte[] byteValuedDiscreteFeatures;
      public final short[] shortValuedDiscreteFeatures;
      public final float[] continuousFeatures;
  
      public FeatureVector(byte[] byteValuedDiscreteFeatures, short[] shortValuedDiscreteFeatures, float[] continuousFeatures,
                           int setUnitIndex) {
          this.byteValuedDiscreteFeatures = byteValuedDiscreteFeatures;
          this.shortValuedDiscreteFeatures = shortValuedDiscreteFeatures;
          this.continuousFeatures = continuousFeatures;
          if (setUnitIndex < 0) {
              throw new RuntimeException("The unit index can't be negative or null when instanciating a new feature vector.");
          }
          this.unitIndex = setUnitIndex;
      }
  
      /**
       * Is this an edge vector?
       *
       * @return isEdge
       */
      public boolean isEdgeVector(int edgeIndex) {
          String edgeValue = getFeature(edgeIndex).toString();
          return (!edgeValue.equals(FeatureDefinition.NULLVALUE));
      }
  
      public FeatureType getFeatureType(int featureIndex) {
          FeatureType t = null;
          if (featureIndex < 0 || featureIndex >= byteValuedDiscreteFeatures.length + shortValuedDiscreteFeatures.length + continuousFeatures.length) {
              throw new IllegalArgumentException("Index " + featureIndex + " is out of range [0, " + getLength() + "[");
          }
          if (featureIndex < byteValuedDiscreteFeatures.length) {
              t = FeatureType.byteValued;
          } else if (featureIndex < byteValuedDiscreteFeatures.length + shortValuedDiscreteFeatures.length) {
              t = FeatureType.shortValued;
          } else if (featureIndex < byteValuedDiscreteFeatures.length + shortValuedDiscreteFeatures.length + continuousFeatures.length) {
              t = FeatureType.floatValued;
          }
          return t;
      }
  
      /**
       * Get the total number of features in this feature vector.
       *
       * @return the number of features, irrespective of their type
       */
      public int getLength() {
          return byteValuedDiscreteFeatures.length + shortValuedDiscreteFeatures.length + continuousFeatures.length;
      }
  
      /**
       * Get the index of the unit to which the current feature vector applies.
       *
       * @return The related unit index.
       */
      public int getUnitIndex() {
          return (unitIndex);
      }
  
      /**
       * The number of byte features in this feature vector.
       *
       * @return a number of byte features, possibly 0.
       */
      public int getNumberOfByteFeatures() {
          return byteValuedDiscreteFeatures.length;
      }
  
      /**
       * The number of short features in this feature vector.
       *
       * @return a number of short features, possibly 0.
       */
      public int getNumberOfShortFeatures() {
          return shortValuedDiscreteFeatures.length;
      }
  
      /**
       * The number of continuous features in this feature vector.
       *
       * @return a number of continuous features, possibly 0.
       */
      public int getNumberOfContinuousFeatures() {
          return continuousFeatures.length;
      }
  
      /**
       * A uniform way to access features in this feature vector.
       *
       * @param index a feature index between 0 and getLength()-1
       * @return a Number object, which will be a Byte, a Short or a Float
       * depending on the type of the feature with the given index number.
       */
      public Number getFeature(int index) {
          if (index < byteValuedDiscreteFeatures.length)
              return new Byte(byteValuedDiscreteFeatures[index]);
          index -= byteValuedDiscreteFeatures.length;
          if (index < shortValuedDiscreteFeatures.length)
              return new Short(shortValuedDiscreteFeatures[index]);
          index -= shortValuedDiscreteFeatures.length;
          if (index < continuousFeatures.length)
              return new Float(continuousFeatures[index]);
          throw new IndexOutOfBoundsException();
      }
  
      /**
       * A wrapper to getFeature(), to get the result as an int value, e.g.,
       * for subsequent array indexing.
       *
       * @param index A feature index between 0 and getLength()-1.
       * @return The feature value, as an int.
       * @see FeatureVector#getFeature(int)
       */
      public int getFeatureAsInt(int index) {
          return (getFeature(index).intValue());
      }
  
      /**
       * A wrapper to getFeature(), to get the result as an String value, e.g.,
       * for subsequent System.out output.
       *
       * @param index  A feature index between 0 and getLength()-1.
       * @param feaDef A FeatureDefinition object allowing to decode the feature value.
       * @return The feature value, as a String.
       * @see FeatureVector#getFeature(int)
       */
      public String getFeatureAsString(int index, FeatureDefinition feaDef) {
          if (index < byteValuedDiscreteFeatures.length)
              return feaDef.getFeatureValueAsString(index, byteValuedDiscreteFeatures[index]);
          throw new IndexOutOfBoundsException();
      }
  
      /**
       * An efficient way to access byte-valued features in this feature vector.
       *
       * @param index the index number of the byte-valued feature in this feature vector.
       * @return the byte value of the feature with the given index.
       * @throws IndexOutOfBoundsException if index<0 or index >= getNumberOfByteFeatures().
       * @see #getNumberOfByteFeatures()
       * @see #isByteFeature()
       */
      public final byte getByteFeature(int index) {
          if (index < 0 || index >= byteValuedDiscreteFeatures.length) {
              throw new IndexOutOfBoundsException(index + " is not between 0 and " + byteValuedDiscreteFeatures.length);
          }
          return byteValuedDiscreteFeatures[index];
      }
  
      /**
       * An efficient way to access short-valued features in this feature vector.
       *
       * @param index the index number of the short-valued feature in this feature vector.
       * @return the short value of the feature with the given index.
       * @throws IndexOutOfBoundsException if index<getNumberOfByteFeatures() or index >= getNumberOfByteFeatures()+getNumberOfShortFeatures().
       * @see #getNumberOfByteFeatures()
       * @see #getNumberOfShortFeatures()
       * @see #isShortFeature()
       */
      public final short getShortFeature(int index) {
          return shortValuedDiscreteFeatures[index - byteValuedDiscreteFeatures.length];
      }
  
      /**
       * An efficient way to access continuous features in this feature vector.
       *
       * @param index the index number of the continuous feature in this feature vector.
       * @return the float value of the feature with the given index.
       * @throws IndexOutOfBoundsException if index<getNumberOfByteFeatures()+getNumberOfShortFeatures() or index >= getLength().
       * @see #getNumberOfByteFeatures()
       * @see #getNumberOfShortFeatures()
       * @see #getNumberOfContinuousFeatures()
       * @see #getLength()
       * @see #isContinuousFeature()
       */
      public final float getContinuousFeature(int index) {
          return continuousFeatures[index - byteValuedDiscreteFeatures.length - shortValuedDiscreteFeatures.length];
      }
  
      /**
       * Test whether the feature with the given index number is a byte feature.
       *
       * @param index
       * @return This will return true exactly if getByteFeature(index) would return a value
       * without throwing an exception, i.e. if index>=0 and index < getNumberOfByteFeatures().
       */
      public boolean isByteFeature(int index) {
          return 0 <= index && index < byteValuedDiscreteFeatures.length;
      }
  
      /**
       * Test whether the feature with the given index number is a short feature.
       *
       * @param index
       * @return This will return true exactly if getShortFeature(index) would return a value
       * without throwing an exception, i.e. if index>=getNumberOfByteFeatures() and index < getNumberOfByteFeatures()+getNumberOfShortFeatures().
       */
      public boolean isShortFeature(int index) {
          return byteValuedDiscreteFeatures.length <= index && index < byteValuedDiscreteFeatures.length + shortValuedDiscreteFeatures.length;
      }
  
      /**
       * Test whether the feature with the given index number is a continuous feature.
       *
       * @param index
       * @return This will return true exactly if getContinuousFeature(index) would return a value
       * without throwing an exception, i.e. if index>=getNumberOfByteFeatures()+getNumberOfShortFeatures() and index < getLength().
       */
      public boolean isContinuousFeature(int index) {
          return byteValuedDiscreteFeatures.length + shortValuedDiscreteFeatures.length <= index && index < byteValuedDiscreteFeatures.length + shortValuedDiscreteFeatures.length + continuousFeatures.length;
      }
  
      public byte[] getByteValuedDiscreteFeatures() {
          return byteValuedDiscreteFeatures;
      }
  
      public short[] getShortValuedDiscreteFeatures() {
          return shortValuedDiscreteFeatures;
      }
  
      public float[] getContinuousFeatures() {
          return continuousFeatures;
      }
  
      /**
       * Write a binary representation of this feature vector to the given data output.
       *
       * @param out the DataOutputStream or RandomAccessFile in which to write the binary
       *            representation of the feature vector.
       * @return
       */
      public void writeTo(DataOutput out) throws IOException {
          if (byteValuedDiscreteFeatures != null) {
              out.write(byteValuedDiscreteFeatures);
          }
          if (shortValuedDiscreteFeatures != null) {
              for (int i = 0; i < shortValuedDiscreteFeatures.length; i++) {
                  out.writeShort(shortValuedDiscreteFeatures[i]);
              }
          }
          if (continuousFeatures != null) {
              for (int i = 0; i < continuousFeatures.length; i++) {
                  out.writeFloat(continuousFeatures[i]);
              }
          }
      }
  
      /**
       * Return a string representation of this set of target features; feature values separated by spaces.
       */
      @Override
      public String toString() {
          StringBuilder out = new StringBuilder();
          for (int i = 0; i < byteValuedDiscreteFeatures.length; i++) {
              if (out.length() > 0) out.append(" ");
              out.append(byteValuedDiscreteFeatures[i]);
          }
          for (int i = 0; i < shortValuedDiscreteFeatures.length; i++) {
              if (out.length() > 0) out.append(" ");
              out.append(shortValuedDiscreteFeatures[i]);
          }
          for (int i = 0; i < continuousFeatures.length; i++) {
              if (out.length() > 0) out.append(" ");
              out.append(continuousFeatures[i]);
          }
          return out.toString();
      }
  
      public enum FeatureType {byteValued, shortValued, floatValued}
  
  }
  
  