c=======================================================================
c=== RBio/RBwrite mexFunction ==========================================
c=======================================================================

c RBio: a MATLAB toolbox for reading and writing sparse matrices in
c Rutherford/Boeing format.
c Copyright (c) 2006, Timothy A. Davis, Univ. Florida.  Version 1.0.


c-----------------------------------------------------------------------
c RBwrite mexFunction: write a sparse matrix to a Rutherford/Boeing file
c-----------------------------------------------------------------------
c
c   mtype = RBwrite (filename, A, Z, title, key)
c
c   A: a sparse matrix (no explicit zero entries)
c   Z: binary pattern of explicit zero entries to include in the
c	Rutherford/Boeing file.  This always has the same size as A,
c	and is always sparse.  Not used if [ ], or if nnz(Z) is 0.
c   title: title of Rutherford/Boeing file, up to 72 characters
c   key: the name of the matrix, up to 8 characters
c
c   Z is optional.  RBwrite (filename, A) uses a default 
c   title and key, and does not include any explicit zeros.
c   RBwrite (filname, A, 'title...', 'key') uses the given title and
c   key, with no Z matrix.
c
c   A must be sparse.  Z must be empty, or sparse.
c
c   mtype is a 3-character string with the file-type used
c   mtype(1):  r: 0 (real), p: 1 (pattern), c: 2 (complex),
c	       i: 3 (ineger)
c   mtype(2):  r: -1 (rectangular), u: 0 (unsymmetric), s: 1 symmetric,
c	       h: 2 (Hermitian), z: 3 (skew symmetric)
c   mtype(3):  a: assembled matrix
c-----------------------------------------------------------------------

	subroutine mexfunction (nargout, pargout, nargin, pargin)
	integer
     $	    pargout (*), pargin (*)
	integer nargout, nargin

c	----------------------------------------------------------------
c	MATLAB functions
c	----------------------------------------------------------------

	integer mxIsChar, mxClassIDFromClassName,
     $	    mxIsClass, mxIsSparse, mxIsComplex

	integer
     $	    mxGetM, mxGetN, mxGetJc, mxGetIr, mxGetPr, mxGetPi,
     $	    mxGetString, mxGetData, mxCreateNumericMatrix,
     $	    mxCreateString

c	----------------------------------------------------------------
c	local variables
c	----------------------------------------------------------------

	integer
     $	    nrow, ncol, nnz, mkind, cp, info, zmin, zmax,
     $	    skind, cmplex, wmat, cpmat, ww, kmin, kmax,
     $	    Ap, Ai, Ax, Az, Zp, Zi, Zx, Zz, znz, zrow, zcol, i,
     $	    mzkind, szkind, totcrd, ptrcrd, indcrd, valcrd,
     $	    w, valn, valn2, indn, nnz2, ptrn, i1, ititle, vals
	integer iclass
	character title*72, key*8, mtype*3, ptrfmt*20, indfmt*20,
     $	    valfmt*20, filename*1024, fmt2*20, ztype*3
	double precision t
	logical doZ, l1, is_int

c	----------------------------------------------------------------
c	check inputs
c	----------------------------------------------------------------

	if (nargin .lt. 2 .or. nargin .gt. 5. .or. nargout .gt. 2 .or.
     $	    mxIsChar (pargin (1)) .ne. 1) then
	    call mexErrMsgTxt
     $	    ('[m s] = RBwrite (filename, A, Z, title, key)')
	endif

c	----------------------------------------------------------------
c	get filename
c	----------------------------------------------------------------

	if (mxGetString (pargin (1), filename, 1024) .ne. 0) then
	    call mexErrMsgTxt ('filename too long')
	endif
	close (unit = 7)
	open (unit = 7, file = filename, err = 998)

c	----------------------------------------------------------------
c	get A
c	----------------------------------------------------------------

	if (mxIsClass (pargin (2), 'double') .ne. 1 .or.
     $	    mxIsSparse (pargin (2)) .ne. 1) then
	    call mexErrMsgTxt ('A must be sparse and double')
	endif
	cmplex = mxIsComplex (pargin (2))
	Ap = mxGetJc (pargin (2))
	Ai = mxGetIr (pargin (2))
	Ax = mxGetPr (pargin (2))
	Az = mxGetPi (pargin (2))
	nrow = mxGetM (pargin (2))
	ncol = mxGetN (pargin (2))

c	----------------------------------------------------------------
c	get title and key
c	----------------------------------------------------------------

	do 5 i = 1, 72
	    title (i:i) = ' '
5	continue
	key = '        '

	ititle = 99
	do 15 i = 3, nargin
	    if (mxIsChar (pargin (i)) .eq. 1) then
		if (ititle .eq. 99) then
c		    get the title, up to 72 characters long
		    i1 = mxGetString (pargin (i), title, 72)
		    ititle = i
		else
c		    get the key, up to 8 characters long
		    i1 = mxGetString (pargin (i), key, 8)
		endif
	    endif
15	continue

c	place a marker in the title, so we know that the
c	Rutherford/Boeing file was generated by the RBwrite mexFunction.

	title (72:72) = '|'

c	----------------------------------------------------------------
c	get Z, if present
c	----------------------------------------------------------------

	if (nargin .ge. 3 .and. ititle .gt. 3) then

	    zrow = mxGetM (pargin (3))
	    zcol = mxGetN (pargin (3))

	    if (zrow .eq. 0 .or. zcol .eq. 0) then

c		--------------------------------------------------------
c		Z matrix is empty
c		--------------------------------------------------------

		doZ = .false.

	    else

c		--------------------------------------------------------
c		get the Z matrix
c		--------------------------------------------------------

		if (mxIsClass (pargin (3), 'double') .ne. 1 .or.
     $		    mxIsSparse (pargin (3)) .ne. 1 .or.
     $		    mxIsComplex (pargin (3)) .ne. 0 .or.
     $		    zrow .ne. nrow .or. zcol .ne. ncol) then
		    call mexErrMsgTxt
     $		  ('Z must be sparse, double, real, and same size as A')
		endif
		Zp = mxGetJc (pargin (3))
		Zi = mxGetIr (pargin (3))
		Zx = mxGetPr (pargin (3))
		Zz = mxGetPi (pargin (3))
		doZ = .true.

	    endif

	else

c	    ------------------------------------------------------------
c	    no Z matrix is present
c	    ------------------------------------------------------------

	    doZ = .false.

	endif

c	----------------------------------------------------------------
c	get workspace
c	----------------------------------------------------------------

	call RBint (iclass)
	wmat  = mxCreateNumericMatrix (max (nrow, ncol)+1, 1, iclass, 0)
	cpmat = mxCreateNumericMatrix (max (nrow, ncol)+1, 1, iclass, 0)
	w = mxGetData (wmat)
	cp = mxGetData (cpmat)

c	----------------------------------------------------------------
c	determine the matrix type (RSA, RUA, etc)
c	----------------------------------------------------------------

c	find the symmetry of A (mkind, skind), and nnz(A)
	call RBkind (nrow, ncol, %val(Ap), %val(Ai), %val(Ax),
     $	    %val(Az), cmplex, mkind, skind, mtype, nnz, %val(cp),
     $	    kmin, kmax)

	if (doZ) then
c	    find the symmetry of Z and find nnz(Z)
	    call RBkind (nrow, ncol, %val(Zp), %val(Zi), %val(Zx),
     $		%val(Zz), 0, mzkind, szkind, ztype, znz, %val(cp),
     $		zmin, zmax)
	    if (znz .eq. 0) then
c		ignore the Z matrix
		doZ = .false.
	    elseif (szkind .le. 0) then
c		if Z is unsymmetric, then A+Z is unsymmetric too
		skind = szkind
	    endif
	endif

	pargout (1) = mxCreateString (mtype)

c	----------------------------------------------------------------
c	determine the required precision
c	----------------------------------------------------------------

	indfmt = '                '
	valfmt = '                    '
	is_int = mkind .eq. 3
	ww = 1
	if (mkind .ne. 1) then
	    call RBformat (nnz, %val (Ax), ww, valfmt, valn, is_int,
     $		kmin, kmax)
	    if (cmplex .eq. 1) then
		call RBformat (nnz, %val (Az), ww, valfmt, valn, is_int,
     $		kmin, kmax)
	    endif
	endif

c	----------------------------------------------------------------
c	determine the number of entries in the matrix A+Z
c	----------------------------------------------------------------

	call RBwrite (1, nrow, ncol, skind, cmplex, doZ, %val(Ap),
     $	    %val(Ai), %val(Ax), %val(Az), %val(Zp), %val(Zi), 0,
     $	    indfmt, indn, valfmt, valn, nnz2, %val(w), %val(cp))

	if (nnz2 .eq. 0) then
	    call mexErrMsgTxt ('empty matrices not handled')
	endif

c	determine pointer format.  ncol+1 integers, largest is nnz2+1
	call RBiformat (1, nnz2+1, ptrfmt, ptrn, i)
	call RBcards (ncol+1, ptrn, ptrcrd)

c	determine row index format.  nnz2 integers, largest is nrow
	call RBiformat (1, nrow, indfmt, indn, i)
	call RBcards (nnz2, indn, indcrd)

c	determine how many lines for the numerical values
	if (mkind .eq. 0 .or. mkind .eq. 3) then
c	    real or integer
	    vals = 1
	elseif (mkind .eq. 1) then
c	    pattern
	    vals = 0
	else
c	    complex
	    vals = 2
	endif
	call RBcards (vals*nnz2, valn, valcrd)

c	----------------------------------------------------------------
c	determine total number of cards
c	----------------------------------------------------------------

	totcrd = ptrcrd + indcrd + valcrd

c	----------------------------------------------------------------
c	write the header
c	----------------------------------------------------------------

        write (unit = 7, fmt = 10, err = 999)
     $          title, key,
     $          totcrd, ptrcrd, indcrd, valcrd,
     $          mtype, nrow, ncol, nnz2, 0,
     $          ptrfmt, indfmt, valfmt
10      format (a72, a8 / 4i14 / a3, 11x, 4i14 / 2a16, a20)

c	----------------------------------------------------------------
c	write the pointers
c	----------------------------------------------------------------

	call RBiflush (ptrfmt, %val (cp), ncol+1)

c	----------------------------------------------------------------
c	write the row indices
c	----------------------------------------------------------------

	call RBwrite (2, nrow, ncol, skind, cmplex, doZ, %val(Ap),
     $	    %val(Ai), %val(Ax), %val(Az), %val(Zp), %val(Zi), mkind,
     $	    indfmt, indn, valfmt, valn, nnz2, %val(w), %val(cp))

c	----------------------------------------------------------------
c	write the numerical values
c	----------------------------------------------------------------

	if (mkind .ne. 1) then
	    call RBwrite (3, nrow, ncol, skind, cmplex, doZ, %val(Ap),
     $		%val(Ai), %val(Ax), %val(Az), %val(Zp), %val(Zi), mkind,
     $		indfmt, indn, valfmt, valn, nnz2, %val(w), %val(cp))
	endif

c	----------------------------------------------------------------
c	free workspace and return
c	----------------------------------------------------------------

	close (unit = 7)
	call mxDestroyArray (%val (cpmat))
	return
998	call mexErrMsgTxt ('error openning file')
999	call mexErrMsgTxt ('error writing file')
	end
