/**
   * The function computes the values of h and d for a given state
   *
   * @param state The state for which the values should be computed
   * @return An array of the form {h, d}
   */
  private double[] _computeHD(TopSpinState state) {
    double h = -1.0d;
    double d = -1.0d;

    double hMax = 0;

    int index = 0;
    for (Map.Entry<Integer, SinglePDB> currentPDBEntry : this.pdbs.entrySet()) {
      SinglePDB currentPDB = currentPDBEntry.getValue();
      int zeroToken = currentPDB.getFirstTokenInPattern();
      // If the current token is a part of the pattern
      if (zeroToken >= 0) {
        this._calculateReflection(this.tokensNumber - zeroToken, state.tokens);
        try {
          double currentHValue = currentPDB.getH(this.reflectedTokens);
          this.allHeuristicValues[index++] = currentHValue;
          hMax = Math.max(hMax, currentHValue);
        } catch (InvalidKeyException e) {
          // Bypass
        }
      }
    }
    // Finally, calculate the H and D values
    switch (this.heuristicType) {
      case MAXING:
        {
          h = hMax;
          break;
        }
      case RANDOM:
        {
          // Insert some randomness into the calculation ...
          int hIndex =
              (Utils.sumOfArrayValues(state.tokens)
                      - state.tokens[0]
                      - state.tokens[3]
                      - state.tokens[5]
                      - state.tokens[7]
                      - state.tokens[9])
                  % this.actualPDBsCount;
          h = this.allHeuristicValues[hIndex];
          break;
        }
      default:
        {
          System.out.println(
              "[ERROR] Unsupported heuristic type for TopSpin puzzle: " + this.heuristicType);
        }
    }
    // Currently:
    d = h;
    return new double[] {h, d};
  }
 @Override
 public void setAdditionalParameter(String parameterName, String value) {
   switch (parameterName) {
     case "heuristic":
       {
         switch (value) {
           case "maxing":
             {
               this.heuristicType = HeuristicType.MAXING;
               break;
             }
           case "random-pdb":
             {
               if (this.pdbs == null || this.pdbs.size() <= 1) {
                 System.out.println(
                     "[ERROR] Required more than a single PDB in order to "
                         + "allow random heuristic");
                 throw new IllegalArgumentException();
               }
               this.heuristicType = HeuristicType.RANDOM;
               break;
             }
           default:
             {
               System.err.println("Illegal heuristic type for TopSpin domain: " + value);
               throw new IllegalArgumentException();
             }
         }
         break;
       }
       /*
       case "pdbs-count": {
           this.pdbsCount = Integer.parseInt(value);
           if (this.pdbsCount <= 0 || this.pdbsCount > 2) {
               System.out.println("[ERROR] Invalid PDBs count: must be between 1 and 2");
               throw new IllegalArgumentException();
           }
           break;
       }
       */
       // The data for a single PDB in the following format:
       // "<index>|<entries-count>|<tokens-array>|<filename>"
     case "pdb-data":
       {
         if (this.actualPDBsCount >= TopSpin.MAX_PDBS_COUNT) {
           System.out.println(
               "[ERROR] Can't deal with more than " + TopSpin.MAX_PDBS_COUNT + " pdbs");
           throw new IllegalArgumentException();
         }
         ++this.actualPDBsCount;
         String[] splittedPDBData = value.split("-");
         assert splittedPDBData.length == 4;
         int index = Integer.parseInt(splittedPDBData[TopSpin.INDEX_OF_PDB_INDEX]);
         // Check if a PDB for the given index was already read
         if (this.pdbs.containsKey(index)) {
           SinglePDB previous = this.pdbs.get(index);
           System.out.println(
               "[WARNING] A PDB with index "
                   + index
                   + " was already read from "
                   + previous.getPdbFileName());
         }
         long entriesCount = Long.parseLong(splittedPDBData[TopSpin.INDEX_OF_PDB_ENTRIES_COUNT]);
         int[] tokensArray =
             Utils.stringToIntegerArray(splittedPDBData[TopSpin.INDEX_OF_PDB_TOKENS_ARRAY]);
         String pdbFileName = splittedPDBData[TopSpin.INDEX_OF_PDB_FILENAME];
         SinglePDB currentPDB = new SinglePDB(entriesCount, tokensArray, pdbFileName, true);
         this.pdbs.put(index, currentPDB);
         break;
       }
     default:
       {
         System.out.println("No such parameter: " + parameterName + " (value: " + value + ")");
         throw new IllegalArgumentException();
       }
   }
 }