gt:                             weight = Integer.parseInt(weightString);
context: /*
   * MIT License
   *
   * Copyright (c) 2002-2021 Mikko Tommila
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy
   * of this software and associated documentation files (the "Software"), to deal
   * in the Software without restriction, including without limitation the rights
   * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
   * copies of the Software, and to permit persons to whom the Software is
   * furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in all
   * copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
   * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
   * SOFTWARE.
   */
  package org.apfloat.samples;
  
  import java.io.PrintWriter;
  import java.io.IOException;
  import java.util.Arrays;
  import java.util.List;
  import java.util.LinkedList;
  import java.util.ArrayList;
  import java.util.SortedSet;
  import java.util.TreeSet;
  import java.util.Enumeration;
  import java.util.ResourceBundle;
  import java.util.MissingResourceException;
  
  import org.apfloat.Apfloat;
  import org.apfloat.ApfloatContext;
  import org.apfloat.ApfloatMath;
  import org.apfloat.ApfloatRuntimeException;
  
  /**
   * Calculates pi using a cluster of servers.
   * The servers should be running {@link OperationServer}.
   *
   * The names and ports of the cluster nodes are read from the file
   * <code>cluster.properties</code>, or a <code>ResourceBundle</code>
   * by the name "cluster". The format of the property file is as
   * follows:
   *
   * <pre>
   * server1=hostname.company.com:1234
   * server2=hostname2.company.com:2345
   * server3=hostname3.company.com:3456
   * weight1=100
   * weight2=110
   * weight3=50
   * </pre>
   *
   * The server addresses are specified as hostname:port. Weights can
   * (but don't have to) be assigned to nodes to indicate the relative
   * performance of each node, to allow distributing a suitable amount
   * of work for each node. For example, <code>weight2</code> is the
   * relative performance of <code>server2</code> etc. The weights must
   * be integers in the range 1...1000.<p>
   *
   * Guidelines for configuring the servers:
   *
   * <ul>
   *   <li>If the machines are not identical, give proper weights to every
   *       machine. This can improve performance greatly.</li>
   *   <li>If the machines are somewhat similar (e.g. same processor but
   *       different clock frequency), you can calculate the weight roughly
   *       as <code>clockFrequency * numberOfProcessors</code>. For example,
   *       a machine with two 1600MHz processors is four times as fast as
   *       a machine with one 800MHz processor.
   *       </li>
   *   <li>If the machines are very heterogenous, you can benchmark their
   *       performance by running e.g. {@link PiParallel} with one
   *       million digits. Remember to specify the correct number of
   *       CPUs on each machine.</li>
   *   <li>Different JVMs can have different performance. For example,
   *       Sun's Java client VM achieves roughly two thirds of the
   *       performance of the server VM when running this application.</li>
   *   <li>When running {@link OperationServer} on the cluster nodes,
   *       specify the number of worker threads for each server to be
   *       the same as the number of CPUs of the machine.</li>
   *   <li>Additionally, you should specify the number of processors
   *       correctly in the <code>apfloat.properties</code> file
   *       for each cluster server.</li>
   * </ul>
   * <p>
   *
   * Similarly as with {@link PiParallel}, if some nodes have multiple
   * CPUs, to get any performance gain from running many
   * threads in parallel, the JVM must be executing native threads.
   * If the JVM is running in green threads mode, there is no
   * advantage of having multiple threads, as the JVM will in fact
   * execute just one thread and divide its time to multiple
   * simulated threads.
   *
   * @version 1.9.0
   * @author Mikko Tommila
   */
  
  public class PiDistributed
      extends PiParallel
  {
      /**
       * Distributed version of the binary splitting algorithm.
       * Uses multiple computers to calculate pi in parallel.
       */
  
      protected static class DistributedBinarySplittingPiCalculator
          extends ParallelBinarySplittingPiCalculator
      {
          /**
           * Construct a distributed pi calculator with the specified precision and radix.
           *
           * @param series The binary splitting series to be used.
           */
  
          public DistributedBinarySplittingPiCalculator(BinarySplittingSeries series)
          {
              super(series);
          }
  
          /**
           * Entry point for the distributed binary splitting algorithm.
           *
           * @param n1 Start term.
           * @param n2 End term.
           * @param T Algorithm parameter.
           * @param Q Algorithm parameter.
           * @param P Algorithm parameter.
           * @param F Pointer to inverse square root parameter.
           * @param nodes The operation executors to be used for the calculation.
           */
  
          public void r(long n1, long n2, ApfloatHolder T, ApfloatHolder Q, ApfloatHolder P, ApfloatHolder F, Node[] nodes)
              throws ApfloatRuntimeException
          {
              if (nodes.length == 1)
              {
                  // End of splitting work between nodes
                  // Calculate remaining terms on the node
                  // Splitting of work continues on the server node using multiple threads
  
                  if (DEBUG) Pi.err.println("PiDistributed.r(" + n1 + ", " + n2 + ") transferring to server side node " + nodes[0]);
  
                  ApfloatHolder[] TQP = nodes[0].execute(() ->
                  {
                      // Continue splitting by threads on server side
                      r(n1, n2, T, Q, P, null);
  
                      return new ApfloatHolder[] { T, Q, P };
                  });
  
                  T.setApfloat(TQP[0].getApfloat());
                  Q.setApfloat(TQP[1].getApfloat());
                  if (P != null) P.setApfloat(TQP[2].getApfloat());
              }
              else
              {
                  // Multiple nodes available; split work in ratio of node weights and execute in parallel
                  // This split is done on the client side
  
                  Object[] objs = splitNodes(nodes);
  
                  Node[] nodes1 = (Node[]) objs[0],
                               nodes2 = (Node[]) objs[2];
                  long weight1 = (Long) objs[1],
                       weight2 = (Long) objs[3];
  
                  long nMiddle = n1 + (n2 - n1) * weight1 / (weight1 + weight2);
                  ApfloatHolder LT = new ApfloatHolder(),
                                LQ = new ApfloatHolder(),
                                LP = new ApfloatHolder();
  
                  if (DEBUG) Pi.err.println("PiDistributed.r(" + n1 + ", " + n2 + ") splitting " + formatArray(nodes) + " to r(" + n1 + ", " + nMiddle + ") " + formatArray(nodes1) + ", r(" + nMiddle + ", " + n2 + ") " + formatArray(nodes2));
  
                  BackgroundOperation<Object> operation;
  
                  // Call recursively this r() method to further split the term calculation
                  operation = new BackgroundOperation<>(() ->
                  {
                      r(n1, nMiddle, LT, LQ, LP, null, nodes1);
                      return null;
                  });
                  r(nMiddle, n2, T, Q, P, null, nodes2);
                  operation.getResult();                          // Waits for operation to complete
  
                  // Calculate the combining multiplies using available nodes in parallel
  
                  // Up to 4 calculations will be executed in parallel
                  // If more than 4 nodes (threads) are available, each calculation can use multiple nodes (threads)
                  assert (P == null || F == null);
                  int numberNeeded = (P != null || F != null ? 1 : 0) + 3;
                  nodes = recombineNodes(nodes, numberNeeded);
  
                  Operation<Apfloat> sqrtOperation = () -> ApfloatMath.inverseRoot(F.getApfloat(), 2),
                                     T1operation = () -> Q.getApfloat().multiply(LT.getApfloat()),
                                     T2operation = () -> LP.getApfloat().multiply(T.getApfloat()),
                                     Toperation = () -> T1operation.execute().add(T2operation.execute()),
                                     Qoperation = () -> LQ.getApfloat().multiply(Q.getApfloat()),
                                     Poperation = () -> LP.getApfloat().multiply(P.getApfloat());
                  Operation<Apfloat[]> QPoperation = () -> new Apfloat[] { Qoperation.execute(),
                                                                           P == null ? null : Poperation.execute() };
  
                  int availableNodes = nodes.length;
  
                  BackgroundOperation<Apfloat> sqrtBackgroundOperation = null,
                                               operation1,
                                               operation2,
                                               operation3 = null;
                  if (F != null && availableNodes > 1)
                  {
                      if (DEBUG) Pi.err.println("PiDistributed.r(" + n1 + ", " + n2 + ") calculating isqrt on node " + nodes[availableNodes - 1]);
  
                      sqrtBackgroundOperation = nodes[availableNodes - 1].executeBackground(sqrtOperation);
                      availableNodes--;
                  }
  
                  Apfloat t = null,
                          q = null,
                          p = null;
  
                  switch (availableNodes)
                  {
                      case 1:
                      {
                          t = nodes[0].execute(Toperation);
                          q = nodes[0].execute(Qoperation);
                          if (P != null) p = nodes[0].execute(Poperation);
                          break;
                      }
                      case 2:
                      {
                          operation1 = nodes[1].executeBackground(T1operation);
                          Apfloat tmp1 = nodes[0].execute(T2operation),
                                  tmp2 = operation1.getResult();
                          operation1 = nodes[1].executeBackground(Qoperation);
                          t = executeAdd(nodes[0], tmp1, tmp2);
                          if (P != null) p = nodes[0].execute(Poperation);
                          q = operation1.getResult();
                          break;
                      }
                      case 3:
                      {
                          BackgroundOperation<Apfloat[]> operation1a;
                          operation1a = nodes[2].executeBackground(QPoperation);
                          operation2 = nodes[1].executeBackground(T1operation);
                          Apfloat tmp1 = nodes[0].execute(T2operation),
                                  tmp2 = operation2.getResult();
                          t = executeAdd(nodes[1], tmp1, tmp2);
                          Apfloat[] QP = operation1a.getResult();
                          q = QP[0];
                          if (P != null) p = QP[1];
                          break;
                      }
                      default:
                      {
                          operation1 = nodes[availableNodes - 1].executeBackground(T1operation);
                          operation2 = nodes[availableNodes - 3].executeBackground(Qoperation);
                          if (P != null) operation3 = nodes[availableNodes - 4].executeBackground(Poperation);
                          Apfloat tmp1 = nodes[availableNodes - 2].execute(T2operation),
                                  tmp2 = operation1.getResult();
                          t = executeAdd(nodes[availableNodes - 1], tmp1, tmp2);
                          q = operation2.getResult();
                          if (P != null) p = operation3.getResult();
                          break;
                      }
                  }
  
                  T.setApfloat(t);
                  Q.setApfloat(q);
                  if (P != null) P.setApfloat(p);
  
                  if (sqrtBackgroundOperation != null)
                  {
                      F.setApfloat(sqrtBackgroundOperation.getResult());
                  }
              }
          }
  
          /**
           * Get the available set of operation executor nodes.
           * This implementation returns {@link RemoteOperationExecutor}s,
           * which execute operations on the cluster's nodes.
           *
           * @return The nodes of the cluster.
           */
  
          public Node[] getNodes()
          {
              ResourceBundle resourceBundle = null;
  
              try
              {
                  resourceBundle = ResourceBundle.getBundle("cluster");
              }
              catch (MissingResourceException mre)
              {
                  System.err.println("ResourceBundle \"cluster\" not found");
  
                  System.exit(1);
              }
  
              Node[] nodes = null;
              List<Node> list = new ArrayList<>();
              long totalWeight = 0;
              int weightedNodes = 0;
  
              // Loop through all properties in the file
              Enumeration<String> keys = resourceBundle.getKeys();
              while (keys.hasMoreElements())
              {
                  String key = keys.nextElement();
                  // Only process the server properties
                  if (key.startsWith("server"))
                  {
                      int weight = -1;                    // -1 means unspecified here
  
                      // Check if a weight is specified for this server
                      try
                      {
                          String weightString = resourceBundle.getString("weight" + key.substring(6));
  
                          try
                          {
  