// File: tomfunkhouser[P]gaps/pkgs/RNBasics/RNSvd.cpp
// Function: gaps::bidiagonal_svd(double*, int, int, double*, double*)
void bidiagonal_svd(RNScalar *b,int m,int n,RNScalar *u,RNScalar *v)
{
    RNScalar anorm, t;
    int p, q, i, j, z, iter, min=MIN(m,n);

    /* use the max of the sum of each diagonal element and the
    ** superdiagonal element above it as a norm for the array.
    ** The use of this norm comes from the Numerical Recipes (Press et al)
    ** code.  squawk!
    */

    // CHUCKR: this is where the array access if fouling up
    //for (anorm=REF2D(b,m,n,0,0), i=1; i<n; i++) {
    for (anorm = REF2D(b,m,n,0,0), i=1 ; i < min ; i++) 
    {
	    t = ((RNScalar) (fabs(REF2D(b,m,n,i,i)) + fabs(REF2D(b,m,n,i-1,i))));

	    if (t > anorm) 
            anorm = t;
    }

    /* Each iteration of this loop will zero out the superdiagonal
    ** element above b[q][q] -- i.e. b[q][q] will be a singular value.
    */
    for (q = min-1; q>=0; q--) {

	/* Each iteration will make the superdiagonal elements smaller,
	** till they drop below numerical noise.  It ought to converge
	** by 30 tries.  squawk!  (Increased to 100 tries by funk)
	*/
        const int max_iter = 100;
	for (iter=0; iter<max_iter; iter++) {
	    
	    /* Find a block that has a zero on the diagonal or superdiagonal.
	    ** That is, we are breaking up b into three submatrices,
	    ** b11, b22, b33, such that b33 is diagonal and b22 has no zeros 
	    ** on the superdiagonal.
	    ** b33 goes from q+1 to n-1 -- it's the part we already did
	    ** b22 goes from p through q -- it's a bidiagonal block
	    */

	    /* sweep back till we reach a numerically 0 superdiagonal element */
	    for (p = q; p>0; p--) if (REF2D(b,m,n,p-1,p) + anorm == anorm) {
		REF2D(b,m,n,p-1,p) = 0;
		break;
	    }

	    /* if b22 is 1x1, i.e. there is a 0 above b[q,q], then
	    ** b[q,q] is the singular value.  Go on to the next
	    ** singular value.  (But first, make sure it's
	    ** positive.  If it's negative, negate it and the
	    ** corresponding row of v)
	    */

	    if (p==q) {
		if (REF2D(b,m,n,q,q) < 0) {
		    REF2D(b,m,n,q,q) *= -1;
		    for (j=0; j<n; j++) REF2D(v,min,n,q,j) *= -1;
		}
		break;
	    }

	    /* check for zero on the diagonal */
	    for (z= -1, i=q; i>=p; i--) if (REF2D(b,m,n,i,i)+anorm==anorm) {
		z = i;
		break;
	    }
	    if (z >= 0) {
		/* get rid of zero on the diagonl.  where is it? */
		if (z == q)
		    /* lower-right corner.  clr element above it */
		    clr_bot_supdiag_elt(b,0,z,v,m,n,min);
		else
		    /* in the middle.  clr elt to its right */
		    clr_top_supdiag_elt(b,z,q,u,m,n,min);
	    }
	    else {
		/* b22 has no zeroes on the diagonal or superdiagonal.
		** Do magic rotation, leaving glop in the uppermost
		** subdiagonal element
		*/
		golub_kahn_svd_rot(b,p,q,v,m,n,min);
		/* get rid of the glop in the uppermost subdiagonal */
		clr_top_subdiag_elt(b,p,q,u,v,m,n,min);
	    }
	}
#if 0
	if (iter>=max_iter)
	    (void) fprintf(stderr,"svd: didn't converge after %d iterations\n", max_iter);
#endif
    }
}

// Assembly:
subq $0x68, %rsp
movq %rdi, 0x60(%rsp)
movl %esi, 0x5c(%rsp)
movl %edx, 0x58(%rsp)
movq %rcx, 0x50(%rsp)
movq %r8, 0x48(%rsp)
movl 0x5c(%rsp), %eax
cmpl 0x58(%rsp), %eax
jge 0xd3a6f
movl 0x5c(%rsp), %eax
movl %eax, 0x14(%rsp)
jmp 0xd3a77
movl 0x58(%rsp), %eax
movl %eax, 0x14(%rsp)
movl 0x14(%rsp), %eax
movl %eax, 0x1c(%rsp)
movq 0x60(%rsp), %rax
imull $0x0, 0x58(%rsp), %ecx
addl $0x0, %ecx
movslq %ecx, %rcx
movsd (%rax,%rcx,8), %xmm0
movsd %xmm0, 0x40(%rsp)
movl $0x1, 0x2c(%rsp)
movl 0x2c(%rsp), %eax
cmpl 0x1c(%rsp), %eax
jge 0xd3b35
movq 0x60(%rsp), %rax
movl 0x58(%rsp), %ecx
imull 0x2c(%rsp), %ecx
addl 0x2c(%rsp), %ecx
movslq %ecx, %rcx
movsd (%rax,%rcx,8), %xmm0
movaps 0x5f79f(%rip), %xmm1    # 0x133270
pand %xmm1, %xmm0
movq 0x60(%rsp), %rax
movl 0x58(%rsp), %ecx
movl 0x2c(%rsp), %edx
subl $0x1, %edx
imull %edx, %ecx
addl 0x2c(%rsp), %ecx
movslq %ecx, %rcx
movsd (%rax,%rcx,8), %xmm1
movaps 0x5f775(%rip), %xmm2    # 0x133270
pand %xmm2, %xmm1
addsd %xmm1, %xmm0
movsd %xmm0, 0x38(%rsp)
movsd 0x38(%rsp), %xmm0
ucomisd 0x40(%rsp), %xmm0
jbe 0xd3b23
movsd 0x38(%rsp), %xmm0
movsd %xmm0, 0x40(%rsp)
jmp 0xd3b25
movl 0x2c(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x2c(%rsp)
jmp 0xd3aa2
movl 0x1c(%rsp), %eax
subl $0x1, %eax
movl %eax, 0x30(%rsp)
cmpl $0x0, 0x30(%rsp)
jl 0xd3db6
movl $0x64, 0x18(%rsp)
movl $0x0, 0x20(%rsp)
cmpl $0x64, 0x20(%rsp)
jge 0xd3da4
movl 0x30(%rsp), %eax
movl %eax, 0x34(%rsp)
cmpl $0x0, 0x34(%rsp)
jle 0xd3bd7
movq 0x60(%rsp), %rax
movl 0x58(%rsp), %ecx
movl 0x34(%rsp), %edx
subl $0x1, %edx
imull %edx, %ecx
addl 0x34(%rsp), %ecx
movslq %ecx, %rcx
movsd (%rax,%rcx,8), %xmm0
addsd 0x40(%rsp), %xmm0
ucomisd 0x40(%rsp), %xmm0
jne 0xd3bc8
jp 0xd3bc8
movq 0x60(%rsp), %rax
movl 0x58(%rsp), %ecx
movl 0x34(%rsp), %edx
subl $0x1, %edx
imull %edx, %ecx
addl 0x34(%rsp), %ecx
movslq %ecx, %rcx
xorps %xmm0, %xmm0
movsd %xmm0, (%rax,%rcx,8)
jmp 0xd3bd7
jmp 0xd3bca
movl 0x34(%rsp), %eax
addl $-0x1, %eax
movl %eax, 0x34(%rsp)
jmp 0xd3b6e
movl 0x34(%rsp), %eax
cmpl 0x30(%rsp), %eax
jne 0xd3c78
movq 0x60(%rsp), %rax
movl 0x58(%rsp), %ecx
imull 0x30(%rsp), %ecx
addl 0x30(%rsp), %ecx
movslq %ecx, %rcx
xorps %xmm0, %xmm0
ucomisd (%rax,%rcx,8), %xmm0
jbe 0xd3c73
movq 0x60(%rsp), %rax
movl 0x58(%rsp), %ecx
imull 0x30(%rsp), %ecx
addl 0x30(%rsp), %ecx
movslq %ecx, %rcx
movsd 0x5f3f7(%rip), %xmm0    # 0x133018
mulsd (%rax,%rcx,8), %xmm0
movsd %xmm0, (%rax,%rcx,8)
movl $0x0, 0x28(%rsp)
movl 0x28(%rsp), %eax
cmpl 0x58(%rsp), %eax
jge 0xd3c71
movq 0x48(%rsp), %rax
movl 0x58(%rsp), %ecx
imull 0x30(%rsp), %ecx
addl 0x28(%rsp), %ecx
movslq %ecx, %rcx
movsd 0x5f3be(%rip), %xmm0    # 0x133018
mulsd (%rax,%rcx,8), %xmm0
movsd %xmm0, (%rax,%rcx,8)
movl 0x28(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x28(%rsp)
jmp 0xd3c33
jmp 0xd3c73
jmp 0xd3da4
movl $0xffffffff, 0x24(%rsp) # imm = 0xFFFFFFFF
movl 0x30(%rsp), %eax
movl %eax, 0x2c(%rsp)
movl 0x2c(%rsp), %eax
cmpl 0x34(%rsp), %eax
jl 0xd3cd5
movq 0x60(%rsp), %rax
movl 0x58(%rsp), %ecx
imull 0x2c(%rsp), %ecx
addl 0x2c(%rsp), %ecx
movslq %ecx, %rcx
movsd (%rax,%rcx,8), %xmm0
addsd 0x40(%rsp), %xmm0
ucomisd 0x40(%rsp), %xmm0
jne 0xd3cc6
jp 0xd3cc6
movl 0x2c(%rsp), %eax
movl %eax, 0x24(%rsp)
jmp 0xd3cd5
jmp 0xd3cc8
movl 0x2c(%rsp), %eax
addl $-0x1, %eax
movl %eax, 0x2c(%rsp)
jmp 0xd3c88
cmpl $0x0, 0x24(%rsp)
jl 0xd3d38
movl 0x24(%rsp), %eax
cmpl 0x30(%rsp), %eax
jne 0xd3d0e
movq 0x60(%rsp), %rdi
movl 0x24(%rsp), %edx
movq 0x48(%rsp), %rcx
movl 0x5c(%rsp), %r8d
movl 0x58(%rsp), %r9d
movl 0x1c(%rsp), %eax
xorl %esi, %esi
movl %eax, (%rsp)
callq 0xd2e50
jmp 0xd3d36
movq 0x60(%rsp), %rdi
movl 0x24(%rsp), %esi
movl 0x30(%rsp), %edx
movq 0x50(%rsp), %rcx
movl 0x5c(%rsp), %r8d
movl 0x58(%rsp), %r9d
movl 0x1c(%rsp), %eax
movl %eax, (%rsp)
callq 0xd2ca0
jmp 0xd3d92
movq 0x60(%rsp), %rdi
movl 0x34(%rsp), %esi
movl 0x30(%rsp), %edx
movq 0x48(%rsp), %rcx
movl 0x5c(%rsp), %r8d
movl 0x58(%rsp), %r9d
movl 0x1c(%rsp), %eax
movl %eax, (%rsp)
callq 0xd32f0
movq 0x60(%rsp), %rdi
movl 0x34(%rsp), %esi
movl 0x30(%rsp), %edx
movq 0x50(%rsp), %rcx
movq 0x48(%rsp), %r8
movl 0x5c(%rsp), %r9d
movl 0x58(%rsp), %r10d
movl 0x1c(%rsp), %eax
movl %r10d, (%rsp)
movl %eax, 0x8(%rsp)
callq 0xd3000
jmp 0xd3d94
movl 0x20(%rsp), %eax
addl $0x1, %eax
movl %eax, 0x20(%rsp)
jmp 0xd3b5b
jmp 0xd3da6
movl 0x30(%rsp), %eax
addl $-0x1, %eax
movl %eax, 0x30(%rsp)
jmp 0xd3b40
addq $0x68, %rsp
retq
nopl (%rax,%rax)