// File: mgabilo[P]prefix-sums-mpi/prefix.cpp
// Function: main
int main(int argc, char **argv)
{
  int rank, size;

  MPI_Init(&argc, &argv);

  MPI_Comm_size(MPI_COMM_WORLD, &size);
  MPI_Comm_rank(MPI_COMM_WORLD, &rank);

  MPI_Barrier(MPI_COMM_WORLD);

  /* `num` is the total number of random numbers between all
   * processes; allow an optional parameter to set `num`
   */
  size_t num = 90000000;
  size_t pos = 5;
  if (argc == 3) { 
    num = (size_t) atoi(argv[1]);
    pos = (size_t) atoi(argv[2]);
  }

  /* generate some random numbers for `rank` */
  size_t num_per_proc = num / size;
  double* random_numbers = generate_random(num_per_proc,
                                           rank);

  #ifdef DEBUG
    if (rank == 0)
      printf("Generated %zd random numbers per process\n",
             num_per_proc);
  #endif

  double sum;
  double elapsed = 0;

  double start = MPI_Wtime();
  start_find_sum(rank, size, random_numbers, num_per_proc,
                 &sum);
  start_find_psum(rank, size, random_numbers, num_per_proc,
                  sum);

  // this barrier ensures that all processes have
  // finished computing their prefix sums, since we don't
  // want to set `end` before that.
  MPI_Barrier(MPI_COMM_WORLD);

  // print the requested prefix sum
  size_t start_pos = num_per_proc * rank;
  size_t end_pos = num_per_proc * (rank+1) - 1;
  if (pos >= start_pos && pos <= end_pos) {
    printf("Prefix sum number %zu is %lf\n",
          pos, random_numbers[pos - start_pos]);
  }
  double end = MPI_Wtime();

  if (rank == 0) {
    elapsed = end - start;
    printf("Took %lf seconds\n", elapsed);
  }

  #ifdef DEBUG
    size_t j;
    for (j = 0; j < num_per_proc; j++) {
      size_t j_real_pos = start_pos + j;
      printf("Prefix sum %02zu: %lf\n", j_real_pos,
            random_numbers[j]);
    }
  #endif

  free(random_numbers);
  MPI_Finalize();
  return 0;
}

// Assembly:
subq $0x68, %rsp
movl $0x0, 0x64(%rsp)
movl %edi, 0x60(%rsp)
movq %rsi, 0x58(%rsp)
leaq 0x60(%rsp), %rdi
leaq 0x58(%rsp), %rsi
callq 0x87f0
movq 0xb325(%rip), %rdi      # 0x13fc0
leaq 0x50(%rsp), %rsi
callq 0x8610
movq 0xb314(%rip), %rdi      # 0x13fc0
leaq 0x54(%rsp), %rsi
callq 0x87e0
movq 0xb303(%rip), %rdi      # 0x13fc0
callq 0x8360
movq $0x55d4a80, 0x48(%rsp)  # imm = 0x55D4A80
movq $0x5, 0x40(%rsp)
cmpl $0x3, 0x60(%rsp)
jne 0x8d05
movq 0x58(%rsp), %rax
movq 0x8(%rax), %rdi
callq 0x88a0
cltq
movq %rax, 0x48(%rsp)
movq 0x58(%rsp), %rax
movq 0x10(%rax), %rdi
callq 0x88a0
cltq
movq %rax, 0x40(%rsp)
movq 0x48(%rsp), %rax
movslq 0x50(%rsp), %rcx
xorl %edx, %edx
divq %rcx
movq %rax, 0x38(%rsp)
movq 0x38(%rsp), %rdi
movl 0x54(%rsp), %esi
callq 0x8e40
movq %rax, 0x30(%rsp)
xorps %xmm0, %xmm0
movsd %xmm0, 0x20(%rsp)
callq 0x86c0
movsd %xmm0, 0x18(%rsp)
movl 0x54(%rsp), %edi
movl 0x50(%rsp), %esi
movq 0x30(%rsp), %rdx
movq 0x38(%rsp), %rcx
leaq 0x28(%rsp), %r8
callq 0x8ef0
movl 0x54(%rsp), %edi
movl 0x50(%rsp), %esi
movq 0x30(%rsp), %rdx
movq 0x38(%rsp), %rcx
movsd 0x28(%rsp), %xmm0
callq 0x9080
movq 0xb240(%rip), %rdi      # 0x13fc0
callq 0x8360
movq 0x38(%rsp), %rax
movslq 0x54(%rsp), %rcx
imulq %rcx, %rax
movq %rax, 0x10(%rsp)
movq 0x38(%rsp), %rax
movl 0x54(%rsp), %ecx
addl $0x1, %ecx
movslq %ecx, %rcx
imulq %rcx, %rax
subq $0x1, %rax
movq %rax, 0x8(%rsp)
movq 0x40(%rsp), %rax
cmpq 0x10(%rsp), %rax
jb 0x8df3
movq 0x40(%rsp), %rax
cmpq 0x8(%rsp), %rax
ja 0x8df3
movq 0x40(%rsp), %rsi
movq 0x30(%rsp), %rax
movq 0x40(%rsp), %rcx
subq 0x10(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm0
leaq 0x622c(%rip), %rdi      # 0xf018
movb $0x1, %al
callq 0x8040
callq 0x86c0
movsd %xmm0, (%rsp)
cmpl $0x0, 0x54(%rsp)
jne 0x8e29
movsd (%rsp), %xmm0
subsd 0x18(%rsp), %xmm0
movsd %xmm0, 0x20(%rsp)
movsd 0x20(%rsp), %xmm0
leaq 0x6214(%rip), %rdi      # 0xf036
movb $0x1, %al
callq 0x8040
movq 0x30(%rsp), %rdi
callq 0x8620
callq 0x83d0
xorl %eax, %eax
addq $0x68, %rsp
retq
nop