// File: /workspace/github_cpp_makefile_star2_O3/fabianp[P]ProxASAGA/proxasaga_atomic.cpp
// Function: saga_single_thread(std::atomic<double>*, std::atomic<double>*, std::atomic<double>*, double*, long*, long*, double*, double*, long, long, double, double, double, long, double*, double*, int, long)
void saga_single_thread(
        std::atomic<double>* x, std::atomic<double>* memory_gradient, std::atomic<double>* gradient_average,
        double* A_data, int64_t* A_indices, int64_t* A_indptr, double* b,
        double* d, int64_t n_samples, int64_t n_features, double alpha,
        double beta, double step_size, int64_t max_iter, double* trace_x,
        double* trace_time, int thread_id, int64_t iter_freq) {
    int64_t i, j, j_idx, local_counter=0, global_counter;
    double p, grad_i, incr, old_grad, delta;
    std::random_device rd;
    std::mt19937 rng(rd());
    std::uniform_int_distribution<int64_t> uni(0, n_samples-1);
    struct timespec start, finish;
    double elapsed;
    clock_gettime(CLOCK_MONOTONIC, &start);

    while (true) {
      /* take a snapshot of the current vector of iterates in trace_x
         and time in trace_time, in order to plot convergence later on */
      if (local_counter % iter_freq == 0 && thread_id == 0) {
          int64_t c = local_counter / iter_freq;
          for (j=0; j<n_features; j++) {
              trace_x[n_features * c + j] = x[j];
          }
          clock_gettime(CLOCK_MONOTONIC, &finish);
          elapsed = (finish.tv_sec - start.tv_sec);
          elapsed += (finish.tv_nsec - start.tv_nsec) / 1000000000.0;
          trace_time[c] = (double) elapsed;
          printf(".. iteration %lld, time elapsed %f min ..\n", c, elapsed / 60.);
      }

      i = uni(rng);
      p = 0.;
      for (j=A_indptr[i]; j < A_indptr[i+1]; j++) {
          j_idx = A_indices[j];
          p += x[j_idx] * A_data[j];
      }
      grad_i = partial_gradient(p, b[i]);
      old_grad = memory_gradient[i].load();
      while (!memory_gradient[i].compare_exchange_weak(old_grad, grad_i))
          ;
      incr = grad_i - old_grad;

      // .. update coefficients ..
      for (j=A_indptr[i]; j < A_indptr[i+1]; j++){
          j_idx = A_indices[j];
          delta = incr * A_data[j] + d[j_idx] * (gradient_average[j_idx] + alpha * x[j_idx]);
          x[j_idx] = prox(x[j_idx] - step_size * delta, beta * step_size * d[j_idx]);
          add_atomic(&gradient_average[j_idx], incr * A_data[j] / n_samples);
      }
      local_counter ++;
      if (local_counter >= iter_freq * max_iter){
          if (thread_id == 0) {
              printf("..  done %lld iterations ..\n", local_counter / iter_freq);
          }
          return;
      }
  }

}

// Assembly:
pushq %rbp
movq %rsp, %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x2798, %rsp           # imm = 0x2798
movapd %xmm2, -0x50(%rbp)
movsd %xmm1, -0x30(%rbp)
movsd %xmm0, -0x78(%rbp)
movq %r9, %rbx
movq %r8, -0x40(%rbp)
movq %rcx, %r15
movq %rdx, %r12
movq %rsi, -0x58(%rbp)
movq %rdi, %r13
leaq -0x27b8(%rbp), %r14
movq %r14, %rdi
callq 0x2140
movq %r14, %rdi
callq 0x2220
movq 0x30(%rbp), %r14
movq 0x20(%rbp), %rsi
movl %eax, %eax
movq %rax, -0x1430(%rbp)
movl $0x1, %ecx
movq %rax, %rdx
shrq $0x1e, %rdx
xorl %eax, %edx
imull $0x6c078965, %edx, %eax # imm = 0x6C078965
addl %ecx, %eax
movq %rax, -0x1430(%rbp,%rcx,8)
incq %rcx
cmpq $0x270, %rcx            # imm = 0x270
jne 0x2430
leaq -0x1430(%rbp), %rax
movq $0x270, 0x1380(%rax) # imm = 0x270
leaq -0x1(%rsi), %rax
leaq -0xa8(%rbp), %rcx
movq $0x0, (%rcx)
movq %rax, 0x8(%rcx)
leaq -0x98(%rbp), %rsi
movl $0x1, %edi
callq 0x21c0
movapd -0x50(%rbp), %xmm0
movsd -0x30(%rbp), %xmm1
mulsd %xmm0, %xmm1
movsd %xmm1, -0x30(%rbp)
xorpd 0x1b57(%rip), %xmm0     # 0x4000
movapd %xmm0, -0x50(%rbp)
xorps %xmm0, %xmm0
cvtsi2sdq 0x20(%rbp), %xmm0
movsd %xmm0, -0x70(%rbp)
imulq 0x50(%rbp), %r14
cmpq $0x2, %r14
movl $0x1, %eax
cmovgeq %r14, %rax
movq %rax, -0x38(%rbp)
xorl %eax, %eax
movq %rax, -0x68(%rbp)
cqto
idivq 0x50(%rbp)
cmpl $0x0, 0x48(%rbp)
jne 0x2588
testq %rdx, %rdx
jne 0x2588
movq %rax, %r14
movq 0x28(%rbp), %rsi
testq %rsi, %rsi
jle 0x251f
movq %r14, %rax
imulq %rsi, %rax
movq 0x38(%rbp), %rcx
leaq (%rcx,%rax,8), %rax
xorl %ecx, %ecx
movq (%r13,%rcx,8), %rdx
movq %rdx, (%rax,%rcx,8)
incq %rcx
cmpq %rcx, %rsi
jne 0x250e
movl $0x1, %edi
leaq -0x88(%rbp), %rsi
callq 0x21c0
movq -0x88(%rbp), %rax
movq -0x80(%rbp), %rcx
subq -0x98(%rbp), %rax
xorps %xmm1, %xmm1
cvtsi2sd %rax, %xmm1
subq -0x90(%rbp), %rcx
xorps %xmm0, %xmm0
cvtsi2sd %rcx, %xmm0
divsd 0x1aaf(%rip), %xmm0     # 0x4010
addsd %xmm1, %xmm0
movq 0x40(%rbp), %rax
movsd %xmm0, (%rax,%r14,8)
divsd 0x1aa1(%rip), %xmm0     # 0x4018
leaq 0x1aba(%rip), %rdi      # 0x4038
movq %r14, %rsi
movb $0x1, %al
callq 0x2050
leaq -0xa8(%rbp), %rdi
leaq -0x1430(%rbp), %rsi
movq %rdi, %rdx
callq 0x2090
movq %rax, %r14
movq (%rbx,%rax,8), %rax
xorpd %xmm0, %xmm0
cmpq 0x8(%rbx,%r14,8), %rax
movq -0x40(%rbp), %rdx
jge 0x25d3
movq (%rdx,%rax,8), %rcx
movsd (%r13,%rcx,8), %xmm1
mulsd (%r15,%rax,8), %xmm1
addsd %xmm1, %xmm0
incq %rax
cmpq 0x8(%rbx,%r14,8), %rax
jl 0x25b4
movq 0x10(%rbp), %rax
movsd (%rax,%r14,8), %xmm1
movsd %xmm1, -0x60(%rbp)
mulsd %xmm1, %xmm0
ucomisd 0x1a42(%rip), %xmm0     # 0x4030
jbe 0x260b
xorpd 0x1a08(%rip), %xmm0     # 0x4000
callq 0x2290
movsd 0x1a1b(%rip), %xmm2     # 0x4020
movapd %xmm2, %xmm1
jmp 0x261c
callq 0x2290
movapd %xmm0, %xmm1
movsd 0x1a04(%rip), %xmm2     # 0x4020
addsd %xmm2, %xmm0
divsd %xmm0, %xmm1
addsd 0x19fc(%rip), %xmm1     # 0x4028
movsd -0x60(%rbp), %xmm4
mulsd %xmm1, %xmm4
movq -0x58(%rbp), %rsi
movq (%rsi,%r14,8), %rcx
movq %xmm4, %rdx
movq %rcx, %rax
lock
cmpxchgq %rdx, (%rsi,%r14,8)
je 0x2652
movq %rax, %rcx
jmp 0x2645
movq (%rbx,%r14,8), %rdx
cmpq 0x8(%rbx,%r14,8), %rdx
jge 0x271a
movq %rcx, %xmm0
subsd %xmm0, %xmm4
movq -0x40(%rbp), %rax
movq (%rax,%rdx,8), %rcx
movsd (%r15,%rdx,8), %xmm0
mulsd %xmm4, %xmm0
movsd (%r12,%rcx,8), %xmm1
movsd (%r13,%rcx,8), %xmm2
mulsd -0x78(%rbp), %xmm2
addsd %xmm1, %xmm2
movq 0x18(%rbp), %rax
mulsd (%rax,%rcx,8), %xmm2
addsd %xmm0, %xmm2
mulsd -0x50(%rbp), %xmm2
addsd (%r13,%rcx,8), %xmm2
movsd (%rax,%rcx,8), %xmm0
mulsd -0x30(%rbp), %xmm0
movapd %xmm2, %xmm1
subsd %xmm0, %xmm1
xorpd %xmm3, %xmm3
maxsd %xmm3, %xmm1
xorpd 0x1933(%rip), %xmm2     # 0x4000
subsd %xmm0, %xmm2
maxsd %xmm3, %xmm2
subsd %xmm2, %xmm1
movq %xmm1, %rax
xchgq %rax, (%r13,%rcx,8)
movsd (%r15,%rdx,8), %xmm0
mulsd %xmm4, %xmm0
divsd -0x70(%rbp), %xmm0
movq (%r12,%rcx,8), %rax
movq %rax, %xmm1
addsd %xmm0, %xmm1
movq %xmm1, %rsi
lock
cmpxchgq %rsi, (%r12,%rcx,8)
jne 0x26f6
incq %rdx
cmpq 0x8(%rbx,%r14,8), %rdx
jl 0x266a
movq -0x68(%rbp), %rax
incq %rax
cmpq -0x38(%rbp), %rax
jne 0x24d4
cmpl $0x0, 0x48(%rbp)
jne 0x274c
movq -0x38(%rbp), %rax
cqto
idivq 0x50(%rbp)
leaq 0x1921(%rip), %rdi      # 0x4063
movq %rax, %rsi
xorl %eax, %eax
callq 0x2050
leaq -0x27b8(%rbp), %rdi
callq 0x21d0
addq $0x2798, %rsp           # imm = 0x2798
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
jmp 0x2785
jmp 0x276e
movq %rax, %rbx
leaq -0x27b8(%rbp), %rdi
callq 0x21d0
movq %rbx, %rdi
callq 0x2280
movq %rax, %rdi
callq 0x31d8