public static void main(final String[] arg)
      throws IOException, JSAPException, ConfigurationException, ClassNotFoundException {

    SimpleJSAP jsap =
        new SimpleJSAP(
            WeightedPageRankPowerMethod.class.getName(),
            "Computes PageRank of a graph with given graphBasename using the power method."
                + "The resulting doubles are stored in binary form in rankFile."
                + "\n[STOPPING CRITERION] The computation is stopped as soon as two successive iterates have"
                + "an L2-distance smaller than a given threshold (-t option); in any case no more than a fixed"
                + "number of iterations (-i option) is performed.",
            new Parameter[] {
              new FlaggedOption(
                  "alpha",
                  JSAP.DOUBLE_PARSER,
                  Double.toString(WeightedPageRank.DEFAULT_ALPHA),
                  JSAP.NOT_REQUIRED,
                  'a',
                  "alpha",
                  "Damping factor."),
              new FlaggedOption(
                  "maxIter",
                  JSAP.INTEGER_PARSER,
                  Integer.toString(WeightedPageRank.DEFAULT_MAX_ITER),
                  JSAP.NOT_REQUIRED,
                  'i',
                  "max-iter",
                  "Maximum number of iterations."),
              new FlaggedOption(
                  "threshold",
                  JSAP.DOUBLE_PARSER,
                  Double.toString(WeightedPageRank.DEFAULT_THRESHOLD),
                  JSAP.NOT_REQUIRED,
                  't',
                  "threshold",
                  "Threshold to determine whether to stop."),
              new FlaggedOption(
                  "coeff",
                  JSAP.STRING_PARSER,
                  JSAP.NO_DEFAULT,
                  JSAP.NOT_REQUIRED,
                  'c',
                  "coeff",
                  "Save the k-th coefficient of the Taylor polynomial using this basename."),
              new FlaggedOption(
                      "derivative",
                      JSAP.INTEGER_PARSER,
                      JSAP.NO_DEFAULT,
                      JSAP.NOT_REQUIRED,
                      'd',
                      "derivative",
                      "The order(s) of the the derivative(s) to be computed (>0).")
                  .setAllowMultipleDeclarations(true),
              new FlaggedOption(
                  "preferenceVector",
                  JSAP.STRING_PARSER,
                  JSAP.NO_DEFAULT,
                  JSAP.NOT_REQUIRED,
                  'p',
                  "preference-vector",
                  "A preference vector stored as a vector of binary doubles."),
              new FlaggedOption(
                  "preferenceObject",
                  JSAP.STRING_PARSER,
                  JSAP.NO_DEFAULT,
                  JSAP.NOT_REQUIRED,
                  'P',
                  "preference-object",
                  "A preference vector stored as a serialised DoubleList."),
              new FlaggedOption(
                  "startFilename",
                  JSAP.STRING_PARSER,
                  JSAP.NO_DEFAULT,
                  JSAP.NOT_REQUIRED,
                  '1',
                  "start",
                  "Start vector filename."),
              new FlaggedOption(
                  "buckets",
                  JSAP.STRING_PARSER,
                  JSAP.NO_DEFAULT,
                  JSAP.NOT_REQUIRED,
                  'b',
                  "buckets",
                  "The buckets of the graph; if supplied, buckets will be treated as dangling nodes."),
              new Switch("offline", 'o', "offline", "use loadOffline() to load the graph"),
              new Switch(
                  "strongly",
                  'S',
                  "strongly",
                  "use the preference vector to redistribute the dangling rank."),
              new Switch(
                  "sortedRank",
                  's',
                  "sorted-ranks",
                  "Store the ranks (from highest to lowest) into <rankBasename>-sorted.ranks."),
              new FlaggedOption(
                  "norm",
                  JSAP.STRING_PARSER,
                  WeightedPageRank.Norm.INFTY.toString(),
                  JSAP.NOT_REQUIRED,
                  'n',
                  "norm",
                  "Norm type. Possible values: " + Arrays.toString(WeightedPageRank.Norm.values())),
              new UnflaggedOption(
                  "graphBasename",
                  JSAP.STRING_PARSER,
                  JSAP.NO_DEFAULT,
                  JSAP.REQUIRED,
                  JSAP.NOT_GREEDY,
                  "The basename of the graph."),
              new UnflaggedOption(
                  "rankBasename",
                  JSAP.STRING_PARSER,
                  JSAP.NO_DEFAULT,
                  JSAP.REQUIRED,
                  JSAP.NOT_GREEDY,
                  "The filename where the resulting rank (doubles in binary form) are stored.")
            });

    JSAPResult jsapResult = jsap.parse(arg);
    if (jsap.messagePrinted()) return;

    final boolean offline = jsapResult.getBoolean("offline", false);
    final boolean strongly = jsapResult.getBoolean("strongly", false);
    final boolean sorted = jsapResult.getBoolean("sortedRank", false);
    final int[] order = jsapResult.getIntArray("derivative");
    final String graphBasename = jsapResult.getString("graphBasename");
    final String rankBasename = jsapResult.getString("rankBasename");
    final String buckets = jsapResult.getString("buckets");
    final String startFilename = jsapResult.getString("startFilename", null);
    final String coeffBasename = jsapResult.getString("coeff");
    final String norm = jsapResult.getString("norm");
    final ProgressLogger progressLogger = new ProgressLogger(LOGGER, "nodes");

    ArcLabelledImmutableGraph graph =
        offline
            ? ArcLabelledImmutableGraph.loadOffline(graphBasename, progressLogger)
            : ArcLabelledImmutableGraph.loadSequential(graphBasename, progressLogger);

    DoubleList preference = null;
    String preferenceFilename = null;
    if (jsapResult.userSpecified("preferenceVector"))
      preference =
          DoubleArrayList.wrap(
              BinIO.loadDoubles(preferenceFilename = jsapResult.getString("preferenceVector")));

    if (jsapResult.userSpecified("preferenceObject")) {
      if (jsapResult.userSpecified("preferenceVector"))
        throw new IllegalArgumentException("You cannot specify twice the preference vector");
      preference =
          (DoubleList)
              BinIO.loadObject(preferenceFilename = jsapResult.getString("preferenceObject"));
    }

    if (strongly && preference == null)
      throw new IllegalArgumentException("The 'strongly' option requires a preference vector");

    DoubleList start = null;
    if (startFilename != null) {
      LOGGER.debug("Loading start vector \"" + startFilename + "\"...");
      start = DoubleArrayList.wrap(BinIO.loadDoubles(startFilename));
      LOGGER.debug("done.");
    }

    WeightedPageRankPowerMethod pr = new WeightedPageRankPowerMethod(graph);
    pr.alpha = jsapResult.getDouble("alpha");
    pr.preference = preference;
    pr.buckets = (BitSet) (buckets == null ? null : BinIO.loadObject(buckets));
    pr.stronglyPreferential = strongly;
    pr.start = start;
    pr.norm = WeightedPageRank.Norm.valueOf(norm);
    pr.order = order != null ? order : null;
    pr.coeffBasename = coeffBasename;

    // cycle until we reach maxIter interations or the norm is less than the given threshold
    // (whichever comes first)
    pr.stepUntil(
        or(
            new WeightedPageRank.NormDeltaStoppingCriterion(jsapResult.getDouble("threshold")),
            new WeightedPageRank.IterationNumberStoppingCriterion(jsapResult.getInt("maxIter"))));

    System.err.print("Saving ranks...");
    BinIO.storeDoubles(pr.rank, rankBasename + ".ranks");
    if (pr.numNodes < 100) {
      for (int i = 0; i < pr.rank.length; i++) {
        System.err.println("PageRank[" + i + "]=" + pr.rank[i]);
      }
    }
    Properties prop = pr.buildProperties(graphBasename, preferenceFilename, startFilename);
    prop.save(rankBasename + ".properties");

    if (order != null) {
      System.err.print("Saving derivatives...");
      for (int i = 0; i < order.length; i++)
        BinIO.storeDoubles(pr.derivative[i], rankBasename + ".der-" + order[i]);
    }

    final double[] rank = pr.rank;
    pr = null; // Let us free some memory...
    graph = null;

    if (sorted) {
      System.err.print("Sorting ranks...");
      Arrays.sort(rank);
      final int n = rank.length;
      int i = n / 2;
      double t;
      // Since we need the ranks from highest to lowest, we invert their order.
      while (i-- != 0) {
        t = rank[i];
        rank[i] = rank[n - i - 1];
        rank[n - i - 1] = t;
      }
      System.err.print(" saving sorted ranks...");
      BinIO.storeDoubles(rank, rankBasename + "-sorted.ranks");
      System.err.println(" done.");
    }
  }
  public static void main(String[] args) throws JSAPException, IOException, RatingException {
    CommandLineInterface commandLineInterface = new CommandLineInterface();

    JSAPResult jsap = commandLineInterface.parse(args);
    if (!jsap.success()) {
      throw new JSAPException("Command line parsing failed.");
    }

    //////////////////////////////////////////////////////
    //             now configure everything             //
    //////////////////////////////////////////////////////
    Configuration configuration = new Configuration();

    /* configure debug level */
    String debugLevel = jsap.getString(OPTION_DEBUG_LEVEL).toUpperCase();
    Level level = Level.parse(debugLevel);
    configuration.setDebugLevel(level);

    /* configure input dir */
    MatchSetReader matchSetReader =
        new FileMatchSetReader(jsap.getFile(OPTION_INPUT_DIR), configuration);
    configuration.setMatchReader(matchSetReader);

    /* configure output dir */
    Configuration.setOutputDir(jsap.getFile(OPTION_OUTPUT_DIR));

    /* configure previous ratings file */
    configuration.setPreviousRatings(jsap.getFile(OPTION_PREVIOUS_RATINGS));

    /* configure rating algorithms */
    if (jsap.contains(OPTION_DYNAMIC_LINEAR_REGRESSION)) {
      int maxMatchSets = jsap.getInt(OPTION_DYNAMIC_LINEAR_REGRESSION);
      configuration.addRatingSystem(new DynamicLinearRegressionStrategy(maxMatchSets));
    }
    if (jsap.contains(OPTION_CONSTANT_LINEAR_REGRESSION)) {
      double learningRate = jsap.getDouble(OPTION_CONSTANT_LINEAR_REGRESSION);
      configuration.addRatingSystem(new ConstantLinearRegressionStrategy(learningRate));
    }
    if (jsap.getBoolean(OPTION_DIRECT_SCORES)) {
      configuration.addRatingSystem(new DirectScoresStrategy());
    }
    /* ****************** ADD NEW RATING SYSTEMS HERE ****************** */

    /* make ignore list */
    Set<Player> ignorePlayers =
        new IgnorePlayerSet(jsap.getFile(OPTION_INPUT_DIR), configuration.getPlayerSet());

    /* configure output methods */
    for (RatingSystemType type : configuration.getEnabledRatingSystems()) {
      if (jsap.getBoolean(OPTION_CSV_OUTPUT)) {
        configuration.addCSVOutputBuilder(type, ignorePlayers);
      }
      if (jsap.getBoolean(OPTION_GNUPLOT_OUTPUT)) {
        configuration.addGnuplotOutputBuilder(type, ignorePlayers);
      }
      if (jsap.getBoolean(OPTION_HTML_OUTPUT)) {
        configuration.addHtmlOutputBuilder(type, ignorePlayers);
      }
      /* ****************** ADD NEW OUTPUT METHODS HERE ****************** */
    }

    configuration.run();
  }