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

  if (rank == 0) {
    psum = sum;
  }
  for (level = (int)log2(size) - 1; level >= 0; level--) {

    // only trigger the processes on the current level
    if (level == 0 || rank % (int)pow(2, level) == 0) {
      int position = rank / (int)pow(2, level);

      if (position % 2 == 0) {
        double sender_sum;
        int sending_rank = rank + (int)pow(2, level);

        // in the previous level, this process was the parent
        // of `sending_rank`, and had it as its right child,
        // so this psum value is the psum value of the parent
        // of our sibling
        MPI_Send(&psum, 1, MPI_DOUBLE,
                 sending_rank, // RIGHT CHILD
                 0, MPI_COMM_WORLD);

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

        // fix the sum to be the correct value:
	// this isn't required, so it's commented out
        //sum -= sender_sum;

        // psum <- (prefix sum of parent) - (sum of sibling)
        psum -= sender_sum;
      }

      else{
        int receiving_rank = rank - (int)pow(2, level);

        MPI_Recv(&psum, 1, MPI_DOUBLE,
                 receiving_rank, // PARENT
                 0, MPI_COMM_WORLD, &status);

        // send sum to receiving_rank so it can fix its sum
        MPI_Send(&sum, 1, MPI_DOUBLE,
                 receiving_rank, 0, MPI_COMM_WORLD);
      }

    }

    MPI_Barrier(MPI_COMM_WORLD);
  }

  // put the prefix sums associated with this node in
  // random_numbers
  double next_sum = random_numbers[num_per_proc-1];
  random_numbers[num_per_proc-1] = psum;

  int j;
  for (j = num_per_proc - 2; j >= 0; j--) {
    double next_sum_tmp = random_numbers[j];

    random_numbers[j] = random_numbers[j+1] -
                        next_sum;

    next_sum = next_sum_tmp;
  }
}

// Assembly:
subq $0x98, %rsp
movl %edi, 0x94(%rsp)
movl %esi, 0x90(%rsp)
movq %rdx, 0x88(%rsp)
movq %rcx, 0x80(%rsp)
movsd %xmm0, 0x78(%rsp)
cmpl $0x0, 0x94(%rsp)
jne 0x90c1
movsd 0x78(%rsp), %xmm0
movsd %xmm0, 0x70(%rsp)
movl 0x90(%rsp), %edi
callq 0x9ab0
cvttsd2si %xmm0, %eax
subl $0x1, %eax
movl %eax, 0x6c(%rsp)
cmpl $0x0, 0x6c(%rsp)
jl 0x927a
cmpl $0x0, 0x6c(%rsp)
je 0x9117
movl 0x94(%rsp), %eax
movl %eax, 0x1c(%rsp)
movl 0x6c(%rsp), %esi
movl $0x2, %edi
callq 0x9ad0
movl 0x1c(%rsp), %eax
cvttsd2si %xmm0, %ecx
cltd
idivl %ecx
cmpl $0x0, %edx
jne 0x925e
movl 0x94(%rsp), %eax
movl %eax, 0x18(%rsp)
movl 0x6c(%rsp), %esi
movl $0x2, %edi
callq 0x9ad0
movl 0x18(%rsp), %eax
cvttsd2si %xmm0, %ecx
cltd
idivl %ecx
movl %eax, 0x4c(%rsp)
movl 0x4c(%rsp), %eax
movl $0x2, %ecx
cltd
idivl %ecx
cmpl $0x0, %edx
jne 0x91e4
movl 0x94(%rsp), %eax
movl %eax, 0x14(%rsp)
movl 0x6c(%rsp), %esi
movl $0x2, %edi
callq 0x9ad0
movl 0x14(%rsp), %eax
cvttsd2si %xmm0, %ecx
addl %ecx, %eax
movl %eax, 0x3c(%rsp)
movl 0x3c(%rsp), %ecx
leaq 0x70(%rsp), %rdi
movl $0x1, %esi
movq 0xae40(%rip), %rdx      # 0x13fd0
xorl %r8d, %r8d
movq 0xae26(%rip), %r9       # 0x13fc0
callq 0x81b0
movl 0x3c(%rsp), %ecx
leaq 0x40(%rsp), %rdi
movl $0x1, %esi
movq 0xae1c(%rip), %rdx      # 0x13fd0
xorl %r8d, %r8d
movq 0xae02(%rip), %r9       # 0x13fc0
leaq 0x50(%rsp), %rax
movq %rax, (%rsp)
callq 0x8440
movsd 0x40(%rsp), %xmm1
movsd 0x70(%rsp), %xmm0
subsd %xmm1, %xmm0
movsd %xmm0, 0x70(%rsp)
jmp 0x925c
movl 0x94(%rsp), %eax
movl %eax, 0x10(%rsp)
movl 0x6c(%rsp), %esi
movl $0x2, %edi
callq 0x9ad0
movl 0x10(%rsp), %eax
cvttsd2si %xmm0, %ecx
subl %ecx, %eax
movl %eax, 0x38(%rsp)
movl 0x38(%rsp), %ecx
leaq 0x70(%rsp), %rdi
movl $0x1, %esi
movq 0xadb0(%rip), %rdx      # 0x13fd0
xorl %r8d, %r8d
movq 0xad96(%rip), %r9       # 0x13fc0
leaq 0x50(%rsp), %rax
movq %rax, (%rsp)
callq 0x8440
movl 0x38(%rsp), %ecx
leaq 0x78(%rsp), %rdi
movl $0x1, %esi
movq 0xad83(%rip), %rdx      # 0x13fd0
xorl %r8d, %r8d
movq 0xad69(%rip), %r9       # 0x13fc0
callq 0x81b0
jmp 0x925e
movq 0xad5b(%rip), %rdi      # 0x13fc0
callq 0x8360
movl 0x6c(%rsp), %eax
addl $-0x1, %eax
movl %eax, 0x6c(%rsp)
jmp 0x90d8
movq 0x88(%rsp), %rax
movq 0x80(%rsp), %rcx
subq $0x1, %rcx
movsd (%rax,%rcx,8), %xmm0
movsd %xmm0, 0x30(%rsp)
movsd 0x70(%rsp), %xmm0
movq 0x88(%rsp), %rax
movq 0x80(%rsp), %rcx
subq $0x1, %rcx
movsd %xmm0, (%rax,%rcx,8)
movq 0x80(%rsp), %rax
subq $0x2, %rax
movl %eax, 0x2c(%rsp)
cmpl $0x0, 0x2c(%rsp)
jl 0x932f
movq 0x88(%rsp), %rax
movslq 0x2c(%rsp), %rcx
movsd (%rax,%rcx,8), %xmm0
movsd %xmm0, 0x20(%rsp)
movq 0x88(%rsp), %rax
movl 0x2c(%rsp), %ecx
addl $0x1, %ecx
movslq %ecx, %rcx
movsd (%rax,%rcx,8), %xmm0
subsd 0x30(%rsp), %xmm0
movq 0x88(%rsp), %rax
movslq 0x2c(%rsp), %rcx
movsd %xmm0, (%rax,%rcx,8)
movsd 0x20(%rsp), %xmm0
movsd %xmm0, 0x30(%rsp)
movl 0x2c(%rsp), %eax
addl $-0x1, %eax
movl %eax, 0x2c(%rsp)
jmp 0x92c8
addq $0x98, %rsp
retq
nopw (%rax,%rax)