// File: /workspace/github_cpp_makefile_star2_O3/tomfunkhouser[P]gaps/apps/pfm2pfm/pfm2pfm.cpp
// Function: FlattenGrid(gaps::R2Grid*, double, double)
void 
FlattenGrid(R2Grid *grid, RNScalar delta, RNScalar sigma)
{
  // Allocate array of grid value wrappers
  GridValue **gvs = new GridValue * [ grid->NEntries() ];
  for (int i = 0; i < grid->NEntries(); i++) gvs[i] = NULL;

  // Build priority queue of grid values
  int x, y;
  GridValue tmp;
  RNHeap<GridValue *> heap(&tmp, &tmp.delta, &tmp.heap_ptr);
  for (int index = 0; index < grid->NEntries(); index++) {
    RNScalar value = grid->GridValue(index);
    if (value != R2_GRID_UNKNOWN_VALUE) {
      grid->IndexToIndices(index, x, y);
      RNScalar target_value = grid->GridValue(x, y, sigma);
      if (target_value != R2_GRID_UNKNOWN_VALUE) {
        RNScalar d = target_value - value;
        GridValue *gv = new GridValue();
        gv->index = index;
        gv->delta = d;
        gv->heap_ptr = NULL;
        gvs[index] = gv;
        heap.Push(gv);
      }
    }
  }

  // Iteratively adjust grid values from largest negative step to smallest
  while (!heap.IsEmpty()) {
    // Get grid entry with highest delta
    GridValue *gv = heap.Peek();
    if (gv->delta > -delta) break;
    RNScalar value = grid->GridValue(gv->index);
    assert(value != R2_GRID_UNKNOWN_VALUE);

    // Update grid entry
    grid->SetGridValue(gv->index, value + gv->delta);

    // Update heap deltas for entry and all neighbors
    int x, y, index;
    grid->IndexToIndices(gv->index, x, y);
    // printf("%d %d %.6f\n", x, y, gv->delta);
    for (int dy = -1; dy <= 1; dy++) {
      for (int dx = -1; dx <= 1; dx++) {
        int i = x + dx;
        if ((i < 0) || (i >= grid->XResolution())) continue;
        int j = y + dy;
        if ((j < 0) || (j >= grid->YResolution())) continue;
        grid->IndicesToIndex(i, j, index);
        if (!gvs[index]) continue;
        RNScalar current_value = grid->GridValue(i, j);
        RNScalar target_value = grid->GridValue(i, j, sigma);
        assert(current_value != R2_GRID_UNKNOWN_VALUE);
        assert(target_value != R2_GRID_UNKNOWN_VALUE);
        gvs[index]->delta = target_value - current_value;
        heap.Update(gvs[index]);
      }
    }
  }

  // Delete memory
  for (int i = 0; i < grid->NEntries(); i++) { 
    if (gvs[i]) delete gvs[i];
  }
}


void 
Poisson(R2Grid *grid, R2Grid *dx_grid, R2Grid *dy_grid)
{
  // Create system of equations
  int n = grid->NEntries();
  int nx = grid->XResolution();
  RNSystemOfEquations equations(n);
  
  // Add inertia equations
  RNScalar inertia_weight = 1.0E-3;
  for (int i = 0; i < n; i++) {
    RNScalar d = grid->GridValue(i);
    RNPolynomial *f = new RNPolynomial(1.0, i, 1.0);
    RNAlgebraic *e = new RNAlgebraic(RN_SUBTRACT_OPERATION, f, d);
    e->Multiply(inertia_weight);
    equations.InsertEquation(e);
  }

  // Add dx equations
  RNScalar dx_weight = 1.0;
  for (int iy = 1; iy < grid->YResolution()-1; iy++) {
    for (int ix = 1; ix < grid->XResolution()-1; ix++) {
      RNScalar dx = dx_grid->GridValue(ix, iy);
      RNPolynomial *e = new RNPolynomial();
      e->AddTerm(-0.25, (iy-1)*nx+(ix-1), 1.0);
      e->AddTerm(-0.50, (iy)*nx+(ix-1), 1.0);
      e->AddTerm(-0.25, (iy+1)*nx+(ix-1), 1.0);
      e->AddTerm( 0.25, (iy-1)*nx+(ix+1), 1.0);
      e->AddTerm( 0.50, (iy)*nx+(ix+1), 1.0);
      e->AddTerm( 0.25, (iy+1)*nx+(ix+1), 1.0);
      e->Subtract(dx);
      e->Multiply(dx_weight);
      equations.InsertEquation(e);
    }
  }
  
  // Add dy equations
  RNScalar dy_weight = 1.0;
  for (int iy = 1; iy < grid->YResolution()-1; iy++) {
    for (int ix = 1; ix < grid->XResolution()-1; ix++) {
      RNScalar dy = dy_grid->GridValue(ix, iy);
      RNPolynomial *e = new RNPolynomial();
      e->AddTerm(-0.25, (iy-1)*nx+(ix-1), 1.0);
      e->AddTerm(-0.50, (iy-1)*nx+(ix), 1.0);
      e->AddTerm(-0.25, (iy-1)*nx+(ix+1), 1.0);
      e->AddTerm( 0.25, (iy+1)*nx+(ix-1), 1.0);
      e->AddTerm( 0.50, (iy+1)*nx+(ix), 1.0);
      e->AddTerm( 0.25, (iy+1)*nx+(ix+1), 1.0);
      e->Subtract(dy);
      e->Multiply(dy_weight);
      equations.InsertEquation(e);
    }
  }
  
  // Initialize variables
  double *x = new double [ n ];
  for (int i = 0; i < n; i++) x[i] = grid->GridValue(i);

  // Solve system of equations
  if (equations.NEquations() >= n) {
    if (!equations.Minimize(x, RN_CSPARSE_SOLVER, 1E-3)) {
      RNFail("Unable to minimize system of equations\n");
      delete [] x;
      return;
    }
  }

  // Copy solution into grid
  for (int i = 0; i < n; i++) grid->SetGridValue(i, x[i]);

  // Delete variables
  delete [] x;
}


struct LineSegment {
  LineSegment(R2Point p1, R2Point p2, RNScalar score) : p1(p1), p2(p2), score(score) {};
  R2Point p1, p2;
  RNScalar score;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0x98, %rsp
movsd %xmm1, 0x20(%rsp)
movapd %xmm0, 0x30(%rsp)
movq %rdi, %rbx
movslq 0xcc(%rdi), %r15
leaq (,%r15,8), %r12
testq %r15, %r15
movq $-0x1, %rdi
cmovnsq %r12, %rdi
callq 0x4030
movabsq $0x1000000008, %r14     # imm = 0x1000000008
testq %r15, %r15
jle 0x5281
movq %rax, %r13
xorl %r15d, %r15d
movq %rax, %rdi
xorl %esi, %esi
movq %r12, %rdx
callq 0x4160
xorpd %xmm0, %xmm0
leaq 0x60(%rsp), %r12
movapd %xmm0, (%r12)
movupd %xmm0, 0x18(%r12)
movq $0x0, 0x28(%r12)
movl $0x1, 0x30(%r12)
movq %r14, 0x10(%r12)
movq 0xb8(%rbx), %rax
movsd (%rax,%r15,8), %xmm0
movsd %xmm0, 0x10(%rsp)
ucomisd 0x53f93(%rip), %xmm0    # 0x59008
jne 0x5079
jnp 0x50ef
movl %r15d, %eax
cltd
idivl 0xc8(%rbx)
xorps %xmm0, %xmm0
cvtsi2sd %edx, %xmm0
xorps %xmm1, %xmm1
cvtsi2sd %eax, %xmm1
movq %rbx, %rdi
movsd 0x20(%rsp), %xmm2
callq 0xf70c
movsd %xmm0, 0x8(%rsp)
ucomisd 0x53f5b(%rip), %xmm0    # 0x59008
jne 0x50b1
jnp 0x50ef
movl $0x18, %edi
callq 0x4280
movsd 0x8(%rsp), %xmm1
subsd 0x10(%rsp), %xmm1
xorpd %xmm0, %xmm0
movapd %xmm0, (%rax)
movl %r15d, (%rax)
movsd %xmm1, 0x8(%rax)
movq $0x0, 0x10(%rax)
movq %rax, (%r13,%r15,8)
movq %r12, %rdi
movq %rax, %rsi
callq 0x7c94
incq %r15
movslq 0xcc(%rbx), %rax
cmpq %rax, %r15
jl 0x505a
cmpl $0x0, 0x68(%rsp)
je 0x5253
movq %r13, 0x28(%rsp)
movapd 0x30(%rsp), %xmm0
xorpd 0x53f40(%rip), %xmm0    # 0x59060
movapd %xmm0, 0x30(%rsp)
movq 0x60(%rsp), %rax
movq (%rax), %rax
movsd 0x8(%rax), %xmm0
ucomisd 0x30(%rsp), %xmm0
ja 0x5248
movslq (%rax), %rax
movq 0xb8(%rbx), %rcx
addsd (%rcx,%rax,8), %xmm0
movsd %xmm0, (%rcx,%rax,8)
cltd
idivl 0xc8(%rbx)
movq %rax, 0x50(%rsp)
leal -0x1(%rax), %ecx
decl %edx
movslq %edx, %rax
movq %rax, 0x48(%rsp)
movl $0xffffffff, %r12d      # imm = 0xFFFFFFFF
movq 0x50(%rsp), %rax
leal (%r12,%rax), %r15d
xorps %xmm0, %xmm0
cvtsi2sd %r15d, %xmm0
movsd %xmm0, 0x58(%rsp)
movl $0x3, %r14d
movl %ecx, 0x1c(%rsp)
movslq %ecx, %rax
movq %rax, 0x8(%rsp)
movq 0x48(%rsp), %rbp
testl %ebp, %ebp
js 0x521e
cmpl 0xc0(%rbx), %ebp
jge 0x521e
testl %r15d, %r15d
js 0x521e
cmpl 0xc4(%rbx), %r15d
jge 0x521e
movslq 0xc8(%rbx), %rax
imulq 0x8(%rsp), %rax
addq %rbp, %rax
movq 0x28(%rsp), %rcx
movq (%rcx,%rax,8), %r13
testq %r13, %r13
je 0x521e
movq 0xb8(%rbx), %rcx
movsd (%rcx,%rax,8), %xmm0
movsd %xmm0, 0x10(%rsp)
xorps %xmm0, %xmm0
cvtsi2sd %ebp, %xmm0
movq %rbx, %rdi
movsd 0x58(%rsp), %xmm1
movsd 0x20(%rsp), %xmm2
callq 0xf70c
subsd 0x10(%rsp), %xmm0
movsd %xmm0, 0x8(%r13)
leaq 0x60(%rsp), %rdi
movq %r13, %rsi
callq 0x7da8
incq %rbp
decl %r14d
jne 0x51a0
incl %r12d
movl 0x1c(%rsp), %ecx
incl %ecx
cmpl $0x2, %r12d
jne 0x5172
cmpl $0x0, 0x68(%rsp)
jne 0x5126
movl 0xcc(%rbx), %eax
movq 0x28(%rsp), %r13
testl %eax, %eax
jle 0x52ad
xorl %r14d, %r14d
movq (%r13,%r14,8), %rdi
testq %rdi, %rdi
je 0x5274
movl $0x18, %esi
callq 0x4290
movl 0xcc(%rbx), %eax
incq %r14
movslq %eax, %rcx
cmpq %rcx, %r14
jl 0x525a
jmp 0x52ad
xorpd %xmm0, %xmm0
movapd %xmm0, 0x60(%rsp)
movupd %xmm0, 0x78(%rsp)
movq $0x0, 0x88(%rsp)
movl $0x1, 0x90(%rsp)
movq %r14, 0x70(%rsp)
movq 0x60(%rsp), %rdi
testq %rdi, %rdi
je 0x52bc
callq 0x4360
addq $0x98, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
jmp 0x52d2
jmp 0x52d2
movq %rax, %rbx
movq 0x60(%rsp), %rdi
testq %rdi, %rdi
je 0x52e4
callq 0x4360
movq %rbx, %rdi
callq 0x44c0