// File: mgabilo[P]prefix-sums-mpi/prefix.cpp
// Function: start_find_sum(int, int, double*, unsigned long, double*)
void start_find_sum(int rank, int size,
     double* random_numbers, size_t num_per_proc,
     double* overall_sum)
{
  MPI_Status status;

  double sum = find_sum(random_numbers, num_per_proc);

  int still_alive = 1;
  int level;

  /* `level` is the current level of the complete binary
   * tree. At level 0, there are n nodes (processes); each
   * of these nodes has its sum already, computed by
   * find_sum.  A node with label `rank` that is in an
   * even-numbered position on the current level becomes a
   * parent on the next level; its new sum is the sum of
   * its current sum and the sum of node `rank + 2^level`.
   * A node with label `rank` in an odd-numbered position
   * on the current level is responsible for sending its
   * sum to the parent, `rank - 2^level`; after that, the
   * node becomes inactive (still_alive is set to 0).
   * After log2(size) levels have been created, node with
   * rank 0 contains the sum. NOTE: positions start at
   * ZERO, not one.
   * 
   * Note that for the sequential version, rank == 1 and
   * so this for loop will be skipped.
   */

  for (level = 0; level < (int)log2(size); level++) {
    if (still_alive) {
      int position = rank / (int)pow(2, level);

      if (position % 2 == 0) {
        // I am a receiver
        double sender_sum;
        int sending_rank = rank + (int)pow(2, level);

        MPI_Recv(&sender_sum, 1, MPI_DOUBLE, sending_rank,
                 0, MPI_COMM_WORLD, &status);

        sum += sender_sum;
      }

      else {
        // I am a sender
        int receiving_rank = rank - (int)pow(2, level);

        MPI_Send(&sum, 1, MPI_DOUBLE, receiving_rank, 0,
                 MPI_COMM_WORLD);
        still_alive = 0;
      }
    }

    MPI_Barrier(MPI_COMM_WORLD);
  }

  *overall_sum = sum;
}

// Assembly:
subq $0x78, %rsp
movl %edi, 0x74(%rsp)
movl %esi, 0x70(%rsp)
movq %rdx, 0x68(%rsp)
movq %rcx, 0x60(%rsp)
movq %r8, 0x58(%rsp)
movq 0x68(%rsp), %rdi
movq 0x60(%rsp), %rsi
callq 0x9360
movsd %xmm0, 0x38(%rsp)
movl $0x1, 0x34(%rsp)
movl $0x0, 0x30(%rsp)
movl 0x30(%rsp), %eax
movl %eax, 0x14(%rsp)
movl 0x70(%rsp), %edi
callq 0x9ab0
movl 0x14(%rsp), %eax
cvttsd2si %xmm0, %ecx
cmpl %ecx, %eax
jge 0x9065
cmpl $0x0, 0x34(%rsp)
je 0x9049
movl 0x74(%rsp), %eax
movl %eax, 0x10(%rsp)
movl 0x30(%rsp), %esi
movl $0x2, %edi
callq 0x9ad0
movl 0x10(%rsp), %eax
cvttsd2si %xmm0, %ecx
cltd
idivl %ecx
movl %eax, 0x2c(%rsp)
movl 0x2c(%rsp), %eax
movl $0x2, %ecx
cltd
idivl %ecx
cmpl $0x0, %edx
jne 0x8ff7
movl 0x74(%rsp), %eax
movl %eax, 0xc(%rsp)
movl 0x30(%rsp), %esi
movl $0x2, %edi
callq 0x9ad0
movl 0xc(%rsp), %eax
cvttsd2si %xmm0, %ecx
addl %ecx, %eax
movl %eax, 0x1c(%rsp)
movl 0x1c(%rsp), %ecx
leaq 0x20(%rsp), %rdi
movl $0x1, %esi
movq 0xb005(%rip), %rdx      # 0x13fd0
xorl %r8d, %r8d
movq 0xafeb(%rip), %r9       # 0x13fc0
leaq 0x40(%rsp), %rax
movq %rax, (%rsp)
callq 0x8440
movsd 0x20(%rsp), %xmm0
addsd 0x38(%rsp), %xmm0
movsd %xmm0, 0x38(%rsp)
jmp 0x9047
movl 0x74(%rsp), %eax
movl %eax, 0x8(%rsp)
movl 0x30(%rsp), %esi
movl $0x2, %edi
callq 0x9ad0
movl 0x8(%rsp), %eax
cvttsd2si %xmm0, %ecx
subl %ecx, %eax
movl %eax, 0x18(%rsp)
movl 0x18(%rsp), %ecx
leaq 0x38(%rsp), %rdi
movl $0x1, %esi
movq 0xafa0(%rip), %rdx      # 0x13fd0
xorl %r8d, %r8d
movq 0xaf86(%rip), %r9       # 0x13fc0
callq 0x81b0
movl $0x0, 0x34(%rsp)
jmp 0x9049
movq 0xaf70(%rip), %rdi      # 0x13fc0
callq 0x8360
movl 0x30(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x30(%rsp)
jmp 0x8f30
movsd 0x38(%rsp), %xmm0
movq 0x58(%rsp), %rax
movsd %xmm0, (%rax)
addq $0x78, %rsp
retq
nopl (%rax)