/** Add a transcription to the retriever */
 public void acceptFeatureProfile(Signal featureSignal) throws noMetadataException {
   this.Files = null;
   FileLocationToFeaturesMap.put(
       featureSignal.getStringMetadata(Signal.PROP_FILE_LOCATION), featureSignal.getDataRow(0));
   FileLocationToSignalMap.put(
       featureSignal.getStringMetadata(Signal.PROP_FILE_LOCATION), featureSignal);
 }
  /** Retrieve transcriptions most similar to the query */
  public SearchResult[] retrieveMostSimilar(Signal likelihoodSignal) throws noMetadataException {
    if (this.Files == null) {
      this.finaliseIndex();
    }
    ArrayList results = new ArrayList();

    // get features
    double[] features =
        likelihoodSignal.getData()[likelihoodSignal.getColumnIndex(Signal.PROP_LIKELIHOODS)];
    double queryRootSquareSum = 0.0;

    for (int j = 0; j < features.length; j++) {
      queryRootSquareSum += Math.pow(features[j] / featureStds[j], 2.0);
    }
    queryRootSquareSum = Math.sqrt(queryRootSquareSum);

    // iterate through all files
    for (int i = 0; i < Files.length; i++) {
      float score = 0.0f;

      // get features
      double[] testFeatures = ((double[]) this.FileLocationToFeaturesMap.get(Files[i]));

      // Normalised Cosine distance between likelihoods
      double prodSum = 0.0;
      double testSum = 0.0;
      for (int j = 0; j < features.length; j++) {
        prodSum += (features[j] / featureStds[j]) * (testFeatures[j] / featureStds[j]);
        testSum += Math.pow(testFeatures[j] / featureStds[j], 2.0);
      }
      score = (float) (prodSum / (queryRootSquareSum * Math.sqrt(testSum)));

      Signal outSig = (Signal) FileLocationToSignalMap.get((String) Files[i]);
      results.add(new SearchResult(outSig, score));
    }
    SearchResult[] outputResults =
        (SearchResult[]) results.toArray(new SearchResult[results.size()]);
    java.util.Arrays.sort((Object[]) outputResults);
    return outputResults;
  }
  /**
   * Calculates the probability of class membership of a Signal Object.
   *
   * @param inputSignal The Signal object to calculate the probabilities of class membership for.
   *     This probabilities should be ordered such that the indexes match the class names returned
   *     by <code>getClassNames</code>.
   * @return An array of the probabilities of class membership
   */
  public double[] probabilities(Signal inputSignal) {

    Instances theData = Signal2Instance.convert(inputSignal, this.classAttribute);

    double[] probsAccum = new double[this.getNumClasses()];
    for (int i = 0; i < theData.numInstances(); i++) {
      double[] temp;
      try {
        temp = theRule.distributionForInstance(theData.instance(i));
        //                if(this.verbose){
        //                    System.out.print("distribution:" );
        //                    for (int j=0;j<this.getNumClasses();j++) {
        //                        System.out.print(" " + temp[j]);
        //                    }
        //                    System.out.println("");
        //                }

      } catch (Exception ex) {
        System.out.println(
            "Exception occured when calculating distribution for a vector!\n" + ex.getMessage());
        ex.printStackTrace();
        return null;
      }
      for (int j = 0; j < this.getNumClasses(); j++) {
        probsAccum[j] += Math.log(temp[j]);
      }
    }

    //        if(this.verbose){
    //            System.out.print("Log Probs:" );
    //            for (int j=0;j<this.getNumClasses();j++) {
    //                System.out.print(" " + probsAccum[j]);
    //            }
    //            System.out.println("");
    //        }

    // normalise to range 0:1
    double min = Double.POSITIVE_INFINITY;
    double max = Double.NEGATIVE_INFINITY;
    for (int j = 0; j < this.getNumClasses(); j++) {
      if (probsAccum[j] > max) {
        max = probsAccum[j];
      }
      if (probsAccum[j] < min) {
        min = probsAccum[j];
      }
    }
    for (int j = 0; j < this.getNumClasses(); j++) {
      probsAccum[j] -= min;
      probsAccum[j] /= (max - min);
    }
    // normalise to sum to 1
    double total = 0.0;
    for (int j = 0; j < this.getNumClasses(); j++) {
      total += probsAccum[j];
    }
    if (verbose) {
      DecimalFormat dec = new DecimalFormat();
      dec.setMaximumFractionDigits(3);
      if (this.verbose) {
        System.out.print("Probabilities:");
      }

      for (int j = 0; j < this.getNumClasses(); j++) {
        probsAccum[j] /= total;
        if (verbose) {
          System.out.print(" " + dec.format(probsAccum[j]));
        }
      }
      if (verbose) {
        try {
          System.out.println("\t" + inputSignal.getStringMetadata(Signal.PROP_FILE_LOCATION));
        } catch (noMetadataException nme) {
          nme.printStackTrace(System.out);
        }
      }
    }

    return probsAccum;
  }