/*
 * This file belongs to the Galois project, a C++ library for exploiting parallelism.
 * The code is being released under the terms of the 3-Clause BSD License (a
 * copy is located in LICENSE.txt at the top-level directory).
 *
 * Copyright (C) 2018, The University of Texas at Austin. All rights reserved.
 * UNIVERSITY EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES CONCERNING THIS
 * SOFTWARE AND DOCUMENTATION, INCLUDING ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR ANY PARTICULAR PURPOSE, NON-INFRINGEMENT AND WARRANTIES OF
 * PERFORMANCE, AND ANY WARRANTY THAT MIGHT OTHERWISE ARISE FROM COURSE OF
 * DEALING OR USAGE OF TRADE.  NO WARRANTY IS EITHER EXPRESS OR IMPLIED WITH
 * RESPECT TO THE USE OF THE SOFTWARE OR DOCUMENTATION. Under no circumstances
 * shall University be liable for incidental, special, indirect, direct or
 * consequential damages or loss of profits, interruption of business, or
 * related expenses which may arise from use of Software or Documentation,
 * including but not limited to those resulting from defects in Software and/or
 * Documentation, or loss or inaccuracy of data of any kind.
 */

#ifndef APPS_PAGERANK_PAGERANK_OLD_H
#define APPS_PAGERANK_PAGERANK_OLD_H

#include "llvm/Support/CommandLine.h"

#include <iostream>

//! d is the damping factor. Alpha is the prob that user will do a random jump,
//! i.e., 1 - d
static const float alpha = 1.0 - 0.85;
static const float alpha2 =
    0.85; // Joyce changed to this which is a usual way to define alpha.

//! maximum relative change until we deem convergence
// static const float tolerance = 0.01;
static const float tolerance = 0.0001; // Joyce

// ICC v13.1 doesn't yet support std::atomic<float> completely, emulate its
// behavior with std::atomic<int>
struct atomic_float : public std::atomic<int> {
  static_assert(sizeof(int) == sizeof(float),
                "int and float must be the same size");

  float atomicIncrement(float value) {
    while (true) {
      union {
        float as_float;
        int as_int;
      } oldValue = {read()};
      union {
        float as_float;
        int as_int;
      } newValue = {oldValue.as_float + value};
      if (this->compare_exchange_strong(oldValue.as_int, newValue.as_int))
        return newValue.as_float;
    }
  }

  float read() {
    union {
      int as_int;
      float as_float;
    } caster = {this->load(std::memory_order_relaxed)};
    return caster.as_float;
  }

  void write(float v) {
    union {
      float as_float;
      int as_int;
    } caster = {v};
    this->store(caster.as_int, std::memory_order_relaxed);
  }
};

struct PNode {
  float value;
  atomic_float accum;
  PNode() {}

  float getPageRank() { return value; }
};

//! Make values unique
template <typename GNode>
struct TopPair {
  float value;
  GNode id;

  TopPair(double v, GNode i) : value(v), id(i) {}

  bool operator<(const TopPair& b) const {
    if (value == b.value)
      return id > b.id;
    return value < b.value;
  }
};

template <typename Graph>
static void printTop(Graph& graph, int topn) {
  typedef typename Graph::GraphNode GNode;
  typedef TopPair<GNode> Pair;
  typedef std::map<Pair, GNode> Top;

  Top top;

  for (auto ii = graph.begin(), ei = graph.end(); ii != ei; ++ii) {
    GNode src  = *ii;
    auto& n    = graph.getData(src);
    auto value = n.getPageRank();
    Pair key(value, src);

    if ((int)top.size() < topn) {
      top.insert(std::make_pair(key, src));
      continue;
    }

    if (top.begin()->first < key) {
      top.erase(top.begin());
      top.insert(std::make_pair(key, src));
    }
  }

  int rank = 1;
  std::cout << "Rank PageRank Id\n";
  for (typename Top::reverse_iterator ii = top.rbegin(), ei = top.rend();
       ii != ei; ++ii, ++rank) {
    std::cout << rank << ": " << ii->first.value << " " << ii->first.id << "\n";
  }
}

extern llvm::cl::opt<unsigned int> memoryLimit;
extern llvm::cl::opt<std::string> filename;
extern llvm::cl::opt<unsigned int> maxIterations;

#endif
