// File: /workspace/github_cpp_makefile_star2_O2/tbjohns[P]BlitzL1/src/solver.cpp
// Function: BlitzL1::Solver::run_prox_newton_iteration(double*, double&, double, BlitzL1::Loss const*, BlitzL1::Dataset const*)
value_t Solver::run_prox_newton_iteration(value_t *x,
                                            value_t &intercept,
                                            value_t lambda,
                                            const Loss *loss_function,
                                            const Dataset *data)
  {
    // Runs one iteration of subproblem solver
    // Returns theta_scale, which is the largest scaler value <= 1
    // for which scaler*theta is feasible for the subproblem

    // Initialize iteration:
    size_t n = data->get_n();
    vector<value_t> Delta_x(working_set_size, 0.0);
    vector<value_t> A_Delta_x(n, 0.0);
    value_t Delta_intercept = 0.0;

    // Permutation vector for shuffling indices:
    vector<size_t> rand_permutation;
    rand_permutation.reserve(working_set_size);
    for (size_t ind = 0; ind < working_set_size; ++ind)
      rand_permutation.push_back(ind);

    // Set up hessian values:
    vector<value_t> H;
    loss_function->compute_H(H, theta, Ax, data);
    vector<value_t> hess_cache(working_set_size, 0.0);
    for (size_t ind = 0; ind < working_set_size; ++ind)
    {
      size_t j = prioritized_features[ind];
      hess_cache[ind] = data->get_column(j)->h_norm_sq(H);
    }

    // Cache values for updating intercept:
    value_t sum_theta = sum_vector(theta);
    value_t sum_H = sum_vector(H);

    // Finish set up for approximate newton step:
    value_t prox_newton_epsilon = 0.0;
    int max_cd_itr = MAX_PROX_NEWTON_CD_ITR;
    if (first_prox_newton_iteration)
    {
      max_cd_itr = MIN_PROX_NEWTON_CD_ITR;
      first_prox_newton_iteration = false;

      // Set up gradient values:
      prox_newton_grad_cache.resize(working_set_size);
      for (size_t ind = 0; ind < working_set_size; ++ind)
      {
        size_t j = prioritized_features[ind];
        prox_newton_grad_cache[ind] = data->get_column(j)->inner_product(theta);
      }
    }
    else
    {
      prox_newton_epsilon = PROX_NEWTON_EPSILON_RATIO * prox_newton_grad_diff;
    }

    // std::random_device rd;
    std::mt19937 g(0);

    // Approximately solve for newton direction:
    for (int cd_itr = 0; cd_itr < max_cd_itr; ++cd_itr)
    {

      // Shuffle indices:
      shuffle(rand_permutation.begin(), rand_permutation.end(), g);
      for (size_t rp_ind = 0; rp_ind < working_set_size; ++rp_ind)
      {
        size_t new_index = rand_permutation[rp_ind];
        swap(prioritized_features[rp_ind], prioritized_features[new_index]);
        swap(Delta_x[rp_ind], Delta_x[new_index]);
        swap(prox_newton_grad_cache[rp_ind], prox_newton_grad_cache[new_index]);
        swap(hess_cache[rp_ind], hess_cache[new_index]);
      }

      // Compute newton direction using coordinate descent:
      value_t sum_sq_hess_diff = 0.0;
      for (size_t ind = 0; ind < working_set_size; ++ind)
      {
        size_t j = prioritized_features[ind];
        const Column *col = data->get_column(j);

        // Compute hessian and gradient for coordinate j:
        value_t hess = hess_cache[ind];
        if (hess <= 0.0)
          continue;

        value_t grad = prox_newton_grad_cache[ind] + col->weighted_inner_product(A_Delta_x, H);

        // Apply coordinate descent update:
        value_t old_value = x[j] + Delta_x[ind];
        value_t proposal = old_value - grad / hess;
        value_t new_value = soft_threshold(proposal, lambda / hess);
        value_t diff = new_value - old_value;

        if (diff == 0.0)
          continue;

        Delta_x[ind] = new_value - x[j];
        col->add_multiple(A_Delta_x, diff);
        sum_sq_hess_diff += diff * diff * hess * hess;
      }

      // Update intercept:
      if (use_intercept)
      {
        value_t ip = inner_product(H, A_Delta_x);
        value_t diff = -(ip + sum_theta) / sum_H;
        Delta_intercept += diff;
        add_scaler(A_Delta_x, diff);
      }

      if (sum_sq_hess_diff < prox_newton_epsilon && cd_itr + 1 >= MIN_PROX_NEWTON_CD_ITR)
      {
        break;
      }
    }

    // Apply update with backtracking:
    value_t t = 1.0;
    value_t last_t = 0.0;
    for (int backtrack_itr = 0; backtrack_itr < MAX_BACKTRACK_ITR; ++backtrack_itr)
    {
      value_t diff_t = t - last_t;

      intercept += diff_t * Delta_intercept;
      value_t subgrad_t = 0.0;

      for (size_t ind = 0; ind < working_set_size; ++ind)
      {
        size_t j = prioritized_features[ind];
        x[j] += diff_t * Delta_x[ind];
        if (x[j] < 0)
          subgrad_t -= lambda * Delta_x[ind];
        else if (x[j] > 0)
          subgrad_t += lambda * Delta_x[ind];
        else
          subgrad_t -= lambda * fabs(Delta_x[ind]);
      }
      for (size_t i = 0; i < n; ++i)
      {
        Ax[i] += diff_t * A_Delta_x[i];
      }

      loss_function->compute_dual_points(Ax, theta, aux_dual, data);
      subgrad_t += inner_product(A_Delta_x, theta);

      if (subgrad_t < 0)
      {
        break;
      }
      else
      {
        last_t = t;
        t *= 0.5;
      }
    }

    // Update intercept exactly:
    value_t delta_intercept = update_intercept(intercept, loss_function, data);

    // Cache gradients for next iteration:
    if (t != 1.0)
    {
      for (size_t i = 0; i < A_Delta_x.size(); ++i)
      {
        A_Delta_x[i] = t * A_Delta_x[i] + delta_intercept;
      }
    }
    for (size_t ind = 0; ind < working_set_size; ++ind)
    {
      size_t j = prioritized_features[ind];
      const Column *col = data->get_column(j);
      value_t actual_grad = col->inner_product(theta);
      value_t approximate_grad = prox_newton_grad_cache[ind] + col->weighted_inner_product(A_Delta_x, H);

      prox_newton_grad_cache[ind] = actual_grad;
      value_t diff = actual_grad - approximate_grad;
      prox_newton_grad_diff += diff * diff;
    }

    // Compute theta_scale return value:
    value_t max_grad_working_set = max_abs(prox_newton_grad_cache);
    if (max_grad_working_set > lambda)
      return lambda / max_grad_working_set;
    else
      return 1.0;
  }

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x14b8, %rsp           # imm = 0x14B8
movq %rcx, 0x38(%rsp)
movapd %xmm0, 0x50(%rsp)
movq %rdx, 0x98(%rsp)
movq %rsi, %r13
movq %rdi, %r14
movq %r8, 0x10(%rsp)
movq 0x18(%r8), %r12
movq 0x98(%rdi), %rsi
leaq 0x130(%rsp), %rbx
andq $0x0, (%rbx)
leaq 0xe8(%rsp), %rdi
leaq 0x68(%rsp), %rcx
movq %rbx, %rdx
callq 0x7770
andq $0x0, (%rbx)
leaq 0x68(%rsp), %rdi
leaq 0x130(%rsp), %rdx
leaq 0xd0(%rsp), %rcx
movq %r12, %rsi
callq 0x7770
xorpd %xmm0, %xmm0
leaq 0xd0(%rsp), %rdi
movapd %xmm0, (%rdi)
andq $0x0, 0x10(%rdi)
movq 0x98(%r14), %rsi
callq 0x74d0
xorl %eax, %eax
leaq 0xd0(%rsp), %rbx
leaq 0x130(%rsp), %r15
movq %rax, 0x130(%rsp)
cmpq 0x98(%r14), %rax
jae 0x93aa
movq %rbx, %rdi
movq %r15, %rsi
callq 0x7220
movq 0x130(%rsp), %rax
incq %rax
jmp 0x9381
leaq 0xb0(%rsp), %rsi
andq $0x0, 0x10(%rsi)
xorpd %xmm0, %xmm0
movapd %xmm0, (%rsi)
leaq 0x18(%r14), %rcx
movq 0x38(%rsp), %rdi
movq (%rdi), %rax
movq %r14, %rdx
movq %rcx, 0xa8(%rsp)
movq 0x10(%rsp), %r8
callq *0x18(%rax)
movq 0x98(%r14), %rsi
leaq 0x130(%rsp), %rdx
andq $0x0, (%rdx)
leaq 0x118(%rsp), %rdi
leaq 0x1f(%rsp), %rcx
callq 0x7770
xorl %r15d, %r15d
leaq 0xb0(%rsp), %rbx
cmpq 0x98(%r14), %r15
jae 0x944a
movq 0xa0(%r14), %rax
movslq (%rax,%r15,8), %rax
movq 0x10(%rsp), %rcx
movq (%rcx), %rcx
movq (%rcx,%rax,8), %rdi
movq (%rdi), %rax
movq %rbx, %rsi
callq *0x10(%rax)
movq 0x118(%rsp), %rax
movsd %xmm0, (%rax,%r15,8)
incq %r15
jmp 0x940e
movq %r14, %rdi
callq 0x7300
movsd %xmm0, 0x110(%rsp)
leaq 0xb0(%rsp), %rdi
callq 0x7300
movsd %xmm0, 0x108(%rsp)
cmpb $0x1, 0xf0(%r14)
jne 0x94d9
movb $0x0, 0xf0(%r14)
leaq 0x100(%r14), %rbx
movq 0x98(%r14), %rsi
movq %rbx, %rdi
callq 0x7470
xorl %r15d, %r15d
pushq $0x2
popq %rax
movl %eax, 0x34(%rsp)
cmpq 0x98(%r14), %r15
jae 0x94fc
movq 0xa0(%r14), %rax
movslq (%rax,%r15,8), %rax
movq 0x10(%rsp), %rcx
movq (%rcx), %rcx
movq (%rcx,%rax,8), %rdi
movq (%rdi), %rax
movq %r14, %rsi
callq *(%rax)
movq (%rbx), %rax
movsd %xmm0, (%rax,%r15,8)
incq %r15
jmp 0x94a3
movsd 0xf8(%r14), %xmm0
mulsd 0x2d3e(%rip), %xmm0     # 0xc228
movsd %xmm0, 0x90(%rsp)
pushq $0x14
popq %rax
movl %eax, 0x34(%rsp)
jmp 0x9509
xorpd %xmm0, %xmm0
movsd %xmm0, 0x90(%rsp)
movq %r12, 0x100(%rsp)
xorl %ebx, %ebx
leaq 0x130(%rsp), %rdi
xorl %esi, %esi
callq 0x78a0
leaq 0x100(%r14), %rax
movq %rax, 0x48(%rsp)
leaq 0x68(%rsp), %rbp
xorpd %xmm0, %xmm0
movsd %xmm0, 0x40(%rsp)
cmpl 0x34(%rsp), %ebx
je 0x97cc
movq 0xd0(%rsp), %rdi
movq 0xd8(%rsp), %rsi
leaq 0x130(%rsp), %rdx
callq 0x7710
movq 0xd0(%rsp), %rax
movq 0xa0(%r14), %rcx
movq 0x100(%r14), %rdx
movq 0xe8(%rsp), %rsi
movq 0x118(%rsp), %rdi
xorl %r8d, %r8d
movq 0x98(%r14), %r9
cmpq %r9, %r8
jae 0x95fa
movq (%rax,%r8,8), %r9
movq (%rcx,%r8,8), %r10
movq (%rcx,%r9,8), %r11
movq %r11, (%rcx,%r8,8)
movq %r10, (%rcx,%r9,8)
movsd (%rsi,%r8,8), %xmm0
movsd (%rsi,%r9,8), %xmm1
movsd %xmm1, (%rsi,%r8,8)
movsd %xmm0, (%rsi,%r9,8)
movsd (%rdx,%r8,8), %xmm0
movsd (%rdx,%r9,8), %xmm1
movsd %xmm1, (%rdx,%r8,8)
movsd %xmm0, (%rdx,%r9,8)
movsd (%rdi,%r8,8), %xmm0
movsd (%rdi,%r9,8), %xmm1
movsd %xmm1, (%rdi,%r8,8)
movsd %xmm0, (%rdi,%r9,8)
incq %r8
jmp 0x958d
movq %rbx, 0xa0(%rsp)
xorpd %xmm0, %xmm0
movsd %xmm0, 0x80(%rsp)
xorl %r15d, %r15d
cmpq %r9, %r15
jae 0x973f
movq 0x118(%rsp), %rax
movsd (%rax,%r15,8), %xmm1
xorpd %xmm0, %xmm0
ucomisd %xmm1, %xmm0
jae 0x9730
movsd %xmm1, 0x20(%rsp)
movq 0xa0(%r14), %rax
movq (%rax,%r15,8), %r12
movq 0x10(%rsp), %rax
movq (%rax), %rax
movslq %r12d, %rcx
movq (%rax,%rcx,8), %rbx
movq 0x48(%rsp), %rax
movq (%rax), %rax
movsd (%rax,%r15,8), %xmm0
movsd %xmm0, 0x8(%rsp)
movq (%rbx), %rax
movq %rbx, %rdi
movq %rbp, %rsi
leaq 0xb0(%rsp), %rdx
callq *0x8(%rax)
movsd 0x8(%rsp), %xmm1
addsd %xmm0, %xmm1
movsd (%r13,%r12,8), %xmm0
movq 0xe8(%rsp), %rax
addsd (%rax,%r15,8), %xmm0
movsd 0x20(%rsp), %xmm2
divsd %xmm2, %xmm1
movsd %xmm0, 0x8(%rsp)
subsd %xmm1, %xmm0
movapd 0x50(%rsp), %xmm1
divsd %xmm2, %xmm1
callq 0x7350
movapd %xmm0, %xmm1
subsd 0x8(%rsp), %xmm1
ucomisd 0x2a5d(%rip), %xmm1     # 0xc130
jne 0x96d7
jnp 0x9730
subsd (%r13,%r12,8), %xmm0
movq 0xe8(%rsp), %rax
movsd %xmm0, (%rax,%r15,8)
movq (%rbx), %rax
movq %rbx, %rdi
movq %rbp, %rsi
movsd %xmm1, 0x8(%rsp)
movapd %xmm1, %xmm0
callq *0x18(%rax)
movsd 0x8(%rsp), %xmm1
mulsd %xmm1, %xmm1
movsd 0x20(%rsp), %xmm0
mulsd %xmm0, %xmm1
mulsd %xmm0, %xmm1
movsd 0x80(%rsp), %xmm0
addsd %xmm1, %xmm0
movsd %xmm0, 0x80(%rsp)
incq %r15
movq 0x98(%r14), %r9
jmp 0x9612
cmpb $0x0, 0xd9(%r14)
je 0x979d
leaq 0xb0(%rsp), %rdi
movq %rbp, %rsi
callq 0x7510
movq 0xa0(%rsp), %rbx
addsd 0x110(%rsp), %xmm0
xorpd 0x28ee(%rip), %xmm0     # 0xc060
divsd 0x108(%rsp), %xmm0
movq %rbp, %rdi
movapd %xmm0, 0x20(%rsp)
callq 0x7170
movsd 0x40(%rsp), %xmm0
addsd 0x20(%rsp), %xmm0
movsd %xmm0, 0x40(%rsp)
jmp 0x97a5
movq 0xa0(%rsp), %rbx
leal 0x1(%rbx), %eax
testl %ebx, %ebx
movl %eax, %ebx
je 0x953d
movsd 0x90(%rsp), %xmm0
ucomisd 0x80(%rsp), %xmm0
movl %eax, %ebx
jbe 0x953d
movaps 0x288d(%rip), %xmm0     # 0xc060
xorps 0x50(%rsp), %xmm0
movaps %xmm0, 0x80(%rsp)
leaq 0x78(%r14), %rbx
xorl %r12d, %r12d
movsd 0x29a1(%rip), %xmm0     # 0xc190
leaq 0x68(%rsp), %rbp
xorpd %xmm1, %xmm1
movq 0x100(%rsp), %r15
cmpl $0x14, %r12d
movsd %xmm0, 0x8(%rsp)
je 0x9948
subsd %xmm1, %xmm0
movapd %xmm0, %xmm1
mulsd 0x40(%rsp), %xmm1
movq 0x98(%rsp), %rax
addsd (%rax), %xmm1
movsd %xmm1, (%rax)
movq 0x98(%r14), %rax
movq 0xa0(%r14), %rcx
xorpd %xmm1, %xmm1
movapd %xmm1, 0x20(%rsp)
movq 0xe8(%rsp), %rdx
xorl %esi, %esi
movapd 0x80(%rsp), %xmm2
cmpq %rsi, %rax
je 0x98be
movq (%rcx,%rsi,8), %rdi
movsd (%rdx,%rsi,8), %xmm1
mulsd %xmm0, %xmm1
addsd (%r13,%rdi,8), %xmm1
movsd %xmm1, (%r13,%rdi,8)
xorpd %xmm3, %xmm3
ucomisd %xmm1, %xmm3
jbe 0x988a
movsd (%rdx,%rsi,8), %xmm1
jmp 0x98a5
ucomisd %xmm3, %xmm1
movsd (%rdx,%rsi,8), %xmm1
jbe 0x989d
mulsd 0x50(%rsp), %xmm1
jmp 0x98a9
andpd 0x27ab(%rip), %xmm1     # 0xc050
mulsd %xmm2, %xmm1
movapd 0x20(%rsp), %xmm3
addsd %xmm1, %xmm3
movapd %xmm3, 0x20(%rsp)
incq %rsi
jmp 0x9859
movq 0x68(%rsp), %rax
movq 0xa8(%rsp), %rcx
movq (%rcx), %rcx
xorl %edx, %edx
cmpq %rdx, %r15
je 0x98ed
movsd (%rax,%rdx,8), %xmm1
mulsd %xmm0, %xmm1
addsd (%rcx,%rdx,8), %xmm1
movsd %xmm1, (%rcx,%rdx,8)
incq %rdx
jmp 0x98d0
movq 0x38(%rsp), %rdi
movq (%rdi), %rax
movq 0xa8(%rsp), %rsi
movq %r14, %rdx
movq %rbx, %rcx
movq 0x10(%rsp), %r8
callq *0x10(%rax)
movq %rbp, %rdi
movq %r14, %rsi
callq 0x7510
movapd 0x20(%rsp), %xmm1
addsd %xmm0, %xmm1
xorpd %xmm0, %xmm0
ucomisd %xmm1, %xmm0
movsd 0x8(%rsp), %xmm2
ja 0x9948
movapd %xmm2, %xmm0
mulsd 0x28b4(%rip), %xmm0     # 0xc1f0
incl %r12d
movapd %xmm2, %xmm1
jmp 0x9800
movq %r14, %rdi
movq 0x98(%rsp), %rsi
movq 0x38(%rsp), %rdx
movq 0x10(%rsp), %rcx
callq 0x7860
movsd 0x8(%rsp), %xmm1
ucomisd 0x2820(%rip), %xmm1     # 0xc190
jne 0x9974
jnp 0x99a5
movq 0x68(%rsp), %rax
movq 0x70(%rsp), %rcx
subq %rax, %rcx
sarq $0x3, %rcx
xorl %edx, %edx
cmpq %rdx, %rcx
je 0x99a5
movsd (%rax,%rdx,8), %xmm1
mulsd 0x8(%rsp), %xmm1
addsd %xmm0, %xmm1
movsd %xmm1, (%rax,%rdx,8)
incq %rdx
jmp 0x9987
xorl %r15d, %r15d
leaq 0x68(%rsp), %rbx
leaq 0xb0(%rsp), %r12
cmpq 0x98(%r14), %r15
jae 0x9a4e
movq 0xa0(%r14), %rax
movslq (%rax,%r15,8), %rax
movq 0x10(%rsp), %rcx
movq (%rcx), %rcx
movq (%rcx,%rax,8), %r13
movq (%r13), %rax
movq %r13, %rdi
movq %r14, %rsi
callq *(%rax)
movsd %xmm0, 0x20(%rsp)
movq 0x48(%rsp), %rax
movq (%rax), %rax
movsd (%rax,%r15,8), %xmm0
movsd %xmm0, 0x8(%rsp)
movq (%r13), %rax
movq %r13, %rdi
movq %rbx, %rsi
movq %r12, %rdx
callq *0x8(%rax)
movsd 0x8(%rsp), %xmm1
addsd %xmm0, %xmm1
movq 0x100(%r14), %rax
movsd 0x20(%rsp), %xmm0
movsd %xmm0, (%rax,%r15,8)
subsd %xmm1, %xmm0
mulsd %xmm0, %xmm0
addsd 0xf8(%r14), %xmm0
movsd %xmm0, 0xf8(%r14)
incq %r15
jmp 0x99b5
movq 0x48(%rsp), %rdi
callq 0x7580
movapd 0x50(%rsp), %xmm2
movapd %xmm2, %xmm1
divsd %xmm0, %xmm1
cmpltsd %xmm0, %xmm2
andpd %xmm2, %xmm1
movsd 0x2719(%rip), %xmm0     # 0xc190
andnpd %xmm0, %xmm2
orpd %xmm1, %xmm2
movapd %xmm2, 0x50(%rsp)
leaq 0x118(%rsp), %rdi
callq 0x77a0
leaq 0xb0(%rsp), %rdi
callq 0x77a0
leaq 0xd0(%rsp), %rdi
callq 0x70b0
leaq 0x68(%rsp), %rdi
callq 0x77a0
leaq 0xe8(%rsp), %rdi
callq 0x77a0
movaps 0x50(%rsp), %xmm0
addq $0x14b8, %rsp           # imm = 0x14B8
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
jmp 0x9b0d
jmp 0x9b0d
jmp 0x9b0d
jmp 0x9b0d
jmp 0x9b0d
jmp 0x9b0d
jmp 0x9ae8
movq %rax, %rbx
jmp 0x9b1d
jmp 0x9b02
movq %rax, %rbx
jmp 0x9b41
jmp 0x9b0d
jmp 0x9b0d
jmp 0x9b0d
jmp 0x9b0d
jmp 0x9b0d
jmp 0x9b0d
jmp 0x9b0d
movq %rax, %rbx
jmp 0x9b2a
jmp 0x9b0d
jmp 0x9b0d
jmp 0x9b0d
movq %rax, %rbx
leaq 0x118(%rsp), %rdi
callq 0x77a0
leaq 0xb0(%rsp), %rdi
callq 0x77a0
leaq 0xd0(%rsp), %rdi
callq 0x70b0
leaq 0x68(%rsp), %rdi
callq 0x77a0
leaq 0xe8(%rsp), %rdi
callq 0x77a0
movq %rbx, %rdi
callq 0x77e0