subroutine mmread(iunit,rep,field,symm,rows,cols,nnz,nnzmax, * indx,jndx,ival,rval,cval) ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc c c This routine will read data from a matrix market formatted file. c The data may be either sparse coordinate format, or dense array format. c c The unit iunit must be open, and the file will be rewound on return. c c 20-Sept-96 Karin A. Remington, NIST ACMD (karin@cam.nist.gov) c 18-Oct-96 Change in routine name to match C and Matlab routines. c 30-Oct-96 Bug fixes in mmio.f: c -looping for comment lines c -fixed non-ansi zero stringlength c -incorrect size calculation for skew-symmetric arrays c Other changes in mmio.f: c -added integer value parameter to calling sequences c -enforced proper count in size info line c -added routine to count words in string (countwd) c (Thanks to G.P.Leendetse and H.Oudshoom for their review c of the initial version and suggested fixes.) c 15-Oct-08 fixed illegal attempt of mimicking "do while" construct c by redifing limits inside loop. (lines 443-450) c (Thanks to Geraldo Veiga for his comments.) c ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc c c Arguments: c c name type in/out description c --------------------------------------------------------------- c c iunit integer in Unit identifier for the file c containing the data to be read. c Must be open prior to call. c Will be rewound on return. c c rep character*10 out Matrix Market 'representation' c indicator. On return: c c coordinate (for sparse data) c array (for dense data) c elemental (to be added) c c field character*7 out Matrix Market 'field'. On return: c c real c complex c integer c pattern c c symm character*19 out Matrix Market 'field'. On return: c c symmetric c hermitian c skew-symmetric c general c c rows integer out Number of rows in matrix. c c cols integer out Number of columns in matrix. c c nnz integer out Number of nonzero entries required to c store matrix. c c nnzmax integer in Maximum dimension of data arrays. c c indx integer(nnz)out Row indices for coordinate format. c Undefined for array format. c c jndx integer(nnz)out Column indices for coordinate format. c Undefined for array format. c c ival integer(nnz) out Integer data (if applicable, see 'field') c c rval double(nnz) out Real data (if applicable, see 'field') c c cval complex(nnz)out Complex data (if applicable, see 'field') c ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc c c Declarations: c integer ival(*) double precision rval(*) complex cval(*) double precision rpart,ipart integer indx(*) integer jndx(*) integer i, rows, cols, nnz, nnzreq, nnzmax, iunit integer count character mmhead*15 character mmtype*6 character rep*10 character field*7 character symm*19 character tmp1*1024 character tmp2*2 c c Read header line and check validity: c read (iunit,end=1000,fmt=5) tmp1 5 format(1024A) call getwd(mmhead,tmp1,1024,1,next,count) if ( count .eq. 0 ) go to 5000 call getwd(mmtype,tmp1,1024,next,next,count) if ( count .eq. 0 ) go to 5000 call getwd(rep,tmp1,1024,next,next,count) if ( count .eq. 0 ) go to 5000 call getwd(field,tmp1,1024,next,next,count) if ( count .eq. 0 ) go to 5000 call getwd(symm,tmp1,1024,next,next,count) if ( count .eq. 0 ) go to 5000 if ( mmhead .ne. '%%MatrixMarket' ) go to 5000 c c Convert type code to lower case for easier comparisons: c call lowerc(mmtype,1,6) if ( mmtype .ne. 'matrix' ) then print *,'Invalid matrix type: ',mmtype print *,'This reader only understands type ''matrix''.' stop else call lowerc(rep,1,10) call lowerc(field,1,7) call lowerc(symm,1,19) endif c c Test input qualifiers: c if (rep .ne. 'coordinate' .and. rep .ne. 'array' ) * go to 6000 if (rep .eq. 'coordinate' .and. field .ne. 'integer' .and. * field .ne. 'real' .and. field .ne. 'complex' .and. * field .ne. 'pattern') go to 7000 if (rep .eq. 'array' .and. field .ne. 'integer' .and. * field .ne. 'real' .and. field .ne. 'complex' ) go to 8000 if (symm .ne. 'general' .and. symm .ne. 'symmetric' .and. * symm .ne. 'hermitian' .and. symm .ne. 'skew-symmetric') * go to 9000 c c Read through comment lines, ignoring content: c read (iunit,end=2000,fmt=200) tmp2 200 format(1a) 10 continue if ( tmp2(1:1) .ne. '%' ) then go to 20 endif read (iunit,end=2000,fmt=200) tmp2 go to 10 20 continue c c Just read a non-comment. c Now, back up a line, and read for first int, and back up c again. This will set pointer to just before apparent size c info line. c Before continuing with free form input, count the number of c words on the size info line to ensure there is the right amount c of info (2 words for array matrices, 3 for coordinate matrices). c backspace (iunit) read (iunit,end=1000,fmt=5) tmp1 call countwd(tmp1,1024,1,count) if ( rep .eq. 'array' .and. count .ne. 2 ) go to 3000 if ( rep .eq. 'coordinate' .and. count .ne. 3 ) go to 3500 c c Correct number of words are present, now back up and read them. c backspace (iunit) c if ( rep .eq. 'coordinate' ) then c c Read matrix in sparse coordinate format c read (iunit,fmt=*) rows,cols,nnz c c Check to ensure adequate storage is available c if ( nnz .gt. nnzmax ) then print *,'insufficent array lengths for matrix of ',nnz, * ' nonzeros.' print *,'resize nnzmax to at least ',nnz,'. (currently ', * nnzmax,')' stop endif c c Read data according to data type (real,integer,complex, or pattern) c if ( field .eq. 'integer' ) then do 30 i=1,nnz read (iunit,fmt=*,end=4000) indx(i),jndx(i),ival(i) 30 continue elseif ( field .eq. 'real' ) then do 35 i=1,nnz read (iunit,fmt=*,end=4000) indx(i),jndx(i),rval(i) 35 continue elseif ( field .eq. 'complex' ) then do 40 i=1,nnz read (iunit,fmt=*,end=4000) indx(i),jndx(i),rpart,ipart cval(i) = cmplx(rpart,ipart) 40 continue elseif ( field .eq. 'pattern' ) then do 50 i=1,nnz read (iunit,fmt=*,end=4000) indx(i),jndx(i) 50 continue else print *,'''',field,''' data type not recognized.' stop endif rewind(iunit) return c elseif ( rep .eq. 'array' ) then c c Read matrix in dense column-oriented array format c read (iunit,fmt=*) rows,cols c c Check to ensure adequate storage is available c if ( symm .eq. 'symmetric' .or. symm .eq. 'hermitian' ) then nnzreq = (rows*cols - rows)/2 + rows nnz = nnzreq elseif ( symm .eq. 'skew-symmetric' ) then nnzreq = (rows*cols - rows)/2 nnz = nnzreq else nnzreq = rows*cols nnz = nnzreq endif if ( nnzreq .gt. nnzmax ) then print *,'insufficent array length for ',rows, ' by ', * cols,' dense ',symm,' matrix.' print *,'resize nnzmax to at least ',nnzreq,'. (currently ', * nnzmax,')' stop endif c c Read data according to data type (real,integer,complex, or pattern) c if ( field .eq. 'integer' ) then do 60 i=1,nnzreq read (iunit,fmt=*,end=4000) ival(i) 60 continue elseif ( field .eq. 'real' ) then do 65 i=1,nnzreq read (iunit,fmt=*,end=4000) rval(i) 65 continue elseif ( field .eq. 'complex' ) then do 70 i=1,nnzreq read (iunit,fmt=*,end=4000) rpart,ipart cval(i) = cmplx(rpart,ipart) 70 continue else print *,'''pattern'' data not consistent with type ''array''' stop endif rewind(iunit) return else print *,'''',rep,''' representation not recognized.' print *, 'Recognized representations:' print *, ' array' print *, ' coordinate' stop endif c c Various error conditions: c 1000 print *,'Premature end-of-file.' print *,'No lines found.' stop 2000 print *,'Premature end-of-file.' print *,'No data lines found.' stop 3000 print *,'Size info inconsistent with representation.' print *,'Array matrices need exactly 2 size descriptors.' print *, count,' were found.' stop 3500 print *,'Size info inconsistent with representation.' print *,'Coordinate matrices need exactly 3 size descriptors.' print *, count,' were found.' stop 4000 print *,'Premature end-of-file.' print *,'Check that the data file contains ',nnz, * ' lines of i,j,[val] data.' print *,'(it appears there are only ',i,' such lines.)' stop 5000 print *,'Invalid matrix header: ',tmp1 print *,'Correct header format:' print *,'%%MatrixMarket type representation field symmetry' print * print *,'Check specification and try again.' 6000 print *,'''',rep,''' representation not recognized.' print *, 'Recognized representations:' print *, ' array' print *, ' coordinate' stop 7000 print *,'''',field,''' field is not recognized.' print *, 'Recognized fields:' print *, ' real' print *, ' complex' print *, ' integer' print *, ' pattern' stop 8000 print *,'''',field,''' arrays are not recognized.' print *, 'Recognized fields:' print *, ' real' print *, ' complex' print *, ' integer' stop 9000 print *,'''',symm,''' symmetry is not recognized.' print *, 'Recognized symmetries:' print *, ' general' print *, ' symmetric' print *, ' hermitian' print *, ' skew-symmetric' stop cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc end cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc c End of subroutine mmread cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc subroutine mminfo(iunit,rep,field,symm,rows,cols,nnz) ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc c c This routine will read header information from a Matrix Market c formatted file. c c The unit iunit must be open, and the file will be rewound on return. c c 20-Sept-96 Karin A. Remington, NIST ACMD (karin@cam.nist.gov) c 18-Oct-96 Change in routine name to match C and Matlab routines. c 30-Oct-96 Bug fixes in mmio.f: c -looping for comment lines c -fixed non-ansi zero stringlength c -incorrect size calculation for skew-symmetric arrays c Other changes in mmio.f: c -added integer value parameter to calling sequences c -enforced proper count in size info line c -added routine to count words in string (countwd) c (Thanks to G.P.Leendetse and H.Oudshoom for their review c of the initial version and suggested fixes.) ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc c c Arguments: c c name type in/out description c --------------------------------------------------------------- c c iunit integer in Unit identifier for the open file c containing the data to be read. c c rep character*10 out Matrix Market 'representation' c indicator. On return: c c coordinate (for sparse data) c array (for dense data) c elemental (to be added) c c field character*7 out Matrix Market 'field'. On return: c c real c complex c integer c pattern c c symm character*19 out Matrix Market 'field'. On return: c c symmetric c hermitian c skew-symmetric c general c c rows integer out Number of rows in matrix. c c cols integer out Number of columns in matrix. c c nnz integer out Number of nonzero entries required to store c the matrix. c ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc c c Declarations: c integer i, rows, cols, nnz, iunit integer count character mmhead*14 character mmtype*6 character rep*10 character field*7 character symm*19 character tmp1*1024 character tmp2*2 c c Read header line and check validity: c read (iunit,end=1000,fmt=5) tmp1 5 format(1024A) c c Parse words from header line: c call getwd(mmhead,tmp1,1024,1,next,count) if ( count .eq. 0 ) go to 5000 call getwd(mmtype,tmp1,1024,next,next,count) if ( count .eq. 0 ) go to 5000 call getwd(rep,tmp1,1024,next,next,count) if ( count .eq. 0 ) go to 5000 call getwd(field,tmp1,1024,next,next,count) if ( count .eq. 0 ) go to 5000 call getwd(symm,tmp1,1024,next,next,count) if ( count .eq. 0 ) go to 5000 if ( mmhead .ne. '%%MatrixMarket' ) go to 5000 c c Convert type code to upper case for easier comparisons: c call lowerc(mmtype,1,6) if ( mmtype .ne. 'matrix' ) then print *,'Invalid matrix type: ',mmtype print *,'This reader only understands type ''matrix''.' stop else call lowerc(rep,1,10) call lowerc(field,1,7) call lowerc(symm,1,19) endif c c Test input qualifiers: c if (rep .ne. 'coordinate' .and. rep .ne. 'array' ) * go to 6000 if (rep .eq. 'coordinate' .and. field .ne. 'integer' .and. * field .ne. 'real' .and. field .ne. 'complex' .and. * field .ne. 'pattern') go to 7000 if (rep .eq. 'array' .and. field .ne. 'integer' .and. * field .ne. 'real' .and. field .ne. 'complex' ) go to 8000 if (symm .ne. 'general' .and. symm .ne. 'symmetric' .and. * symm .ne. 'hermitian' .and. symm .ne. 'skew-symmetric') * go to 9000 c c Read through comment lines, ignoring content: c read (iunit,end=2000,fmt=200) tmp2 200 format(1a) 10 continue if ( tmp2(1:1) .ne. '%' ) then go to 20 endif read (iunit,end=2000,fmt=200) tmp2 go to 10 20 continue c c Just read a non-comment. c Now, back up a line, and read for first int, and back up c again. This will set pointer to just before apparent size c info line. c Before continuing with free form input, count the number of c words on the size info line to ensure there is the right amount c of info (2 words for array matrices, 3 for coordinate matrices). c backspace (iunit) read (iunit,end=1000,fmt=5) tmp1 call countwd(tmp1,1024,1,count) if ( rep .eq. 'array' .and. count .ne. 2 ) go to 3000 if ( rep .eq. 'coordinate' .and. count .ne. 3 ) go to 3500 c c Correct number of words are present, now back up and read them. c backspace (iunit) c if ( rep .eq. 'coordinate' ) then c c Read matrix in sparse coordinate format c read (iunit,fmt=*) rows,cols,nnz c c Rewind before returning c rewind(iunit) return c elseif ( rep .eq. 'array' ) then c c Read matrix in dense column-oriented array format c read (iunit,fmt=*) rows,cols if ( symm .eq. 'symmetric' .or. symm .eq. 'hermitian' ) then nnz = (rows*cols - rows)/2 + rows elseif ( symm .eq. 'skew-symmetric' ) then nnz = (rows*cols - rows)/2 else nnz = rows*cols endif c c Rewind before returning c rewind(iunit) return else print *,'''',rep,''' representation not recognized.' print *, 'Recognized representations:' print *, ' array' print *, ' coordinate' stop endif c c Various error conditions: c 1000 print *,'Premature end-of-file.' print *,'No lines found.' stop 2000 print *,'Premature end-of-file.' print *,'No data found.' stop 3000 print *,'Size info inconsistent with representation.' print *,'Array matrices need exactly 2 size descriptors.' print *, count,' were found.' stop 3500 print *,'Size info inconsistent with representation.' print *,'Coordinate matrices need exactly 3 size descriptors.' print *, count,' were found.' stop 5000 print *,'Invalid matrix header: ',tmp1 print *,'Correct header format:' print *,'%%MatrixMarket type representation field symmetry' print * print *,'Check specification and try again.' stop 6000 print *,'''',rep,''' representation not recognized.' print *, 'Recognized representations:' print *, ' array' print *, ' coordinate' stop 7000 print *,'''',field,''' field is not recognized.' print *, 'Recognized fields:' print *, ' real' print *, ' complex' print *, ' integer' print *, ' pattern' stop 8000 print *,'''',field,''' arrays are not recognized.' print *, 'Recognized fields:' print *, ' real' print *, ' complex' print *, ' integer' stop 9000 print *,'''',symm,''' symmetry is not recognized.' print *, 'Recognized symmetries:' print *, ' general' print *, ' symmetric' print *, ' hermitian' print *, ' skew-symmetric' stop cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc end cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc c End of subroutine mmread cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc subroutine mmwrite(ounit,rep,field,symm,rows,cols,nnz, * indx,jndx,ival,rval,cval) ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc c c This routine will write data to a matrix market formatted file. c The data may be either sparse coordinate format, or dense array format. c c The unit ounit must be open. c c 20-Sept-96 Karin A. Remington, NIST ACMD (karin@cam.nist.gov) c 18-Oct-96 Change in routine name to match C and Matlab routines. c 30-Oct-96 Bug fixes in mmio.f: c -looping for comment lines c -fixed non-ansi zero stringlength c -incorrect size calculation for skew-symmetric arrays c Other changes in mmio.f: c -added integer value parameter to calling sequences c -enforced proper count in size info line c -added routine to count words in string (countwd) c (Thanks to G.P.Leendetse and H.Oudshoom for their review c of the initial version and suggested fixes.) ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc c c Arguments: c c name type in/out description c --------------------------------------------------------------- c c ounit integer in Unit identifier for the file c to which the data will be written. c Must be open prior to call. c c rep character* in Matrix Market 'representation' c indicator. Valid inputs: c c coordinate (for sparse data) c array (for dense data) c *elemental* (to be added) c c field character* in Matrix Market 'field'. Valid inputs: c c real c complex c integer c pattern (not valid for dense arrays) c c symm character* in Matrix Market 'field'. Valid inputs: c c symmetric c hermitian c skew-symmetric c general c c rows integer in Number of rows in matrix. c c cols integer in Number of columns in matrix. c c nnz integer in Number of nonzero entries in matrix. c (rows*cols for array matrices) c c indx integer(nnz)in Row indices for coordinate format. c Undefined for array format. c c jndx integer(nnz)in Column indices for coordinate format. c Undefined for array format. c c ival integer(nnz) in Integer data (if applicable, see 'field') c c rval double(nnz) in Real data (if applicable, see 'field') c c cval complex(nnz)in Complex data (if applicable, see 'field') c ccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc c c Declarations: c integer ival(*) double precision rval(*) complex cval(*) integer indx(*) integer jndx(*) integer i, rows, cols, nnz, nnzreq, ounit character*(*)rep,field,symm c c Test input qualifiers: c if (rep .ne. 'coordinate' .and. rep .ne. 'array' ) * go to 1000 if (rep .eq. 'coordinate' .and. field .ne. 'integer' .and. * field .ne. 'real' .and. field .ne. 'complex' .and. * field .ne. 'pattern') go to 2000 if (rep .eq. 'array' .and. field .ne. 'integer' .and. * field .ne. 'real' .and. field .ne. 'complex' ) go to 3000 if (symm .ne. 'general' .and. symm .ne. 'symmetric' .and. * symm .ne. 'hermitian' .and. symm .ne. 'skew-symmetric') * go to 4000 c c Write header line: c write(unit=ounit,fmt=5)rep,' ',field,' ',symm 5 format('%%MatrixMarket matrix ',11A,1A,8A,1A,20A) c c Write size information: c if ( rep .eq. 'coordinate' ) then nnzreq=nnz write(unit=ounit,fmt=*) rows,cols,nnz if ( field .eq. 'integer' ) then do 10 i=1,nnzreq write(unit=ounit,fmt=*)indx(i),jndx(i),ival(i) 10 continue elseif ( field .eq. 'real' ) then do 20 i=1,nnzreq write(unit=ounit,fmt=*)indx(i),jndx(i),rval(i) 20 continue elseif ( field .eq. 'complex' ) then do 30 i=1,nnzreq write(unit=ounit,fmt=*)indx(i),jndx(i), * real(cval(i)),aimag(cval(i)) 30 continue else c field .eq. 'pattern' do 40 i=1,nnzreq write(unit=ounit,fmt=*)indx(i),jndx(i) 40 continue endif else c rep .eq. 'array' if ( symm .eq. 'general' ) then nnzreq = rows*cols elseif ( symm .eq. 'symmetric' .or. * symm .eq. 'hermitian' ) then nnzreq = (rows*cols - rows)/2 + rows else c symm .eq. 'skew-symmetric' nnzreq = (rows*cols - rows)/2 endif write(unit=ounit,fmt=*)rows,cols if ( field .eq. 'integer' ) then do 50 i=1,nnzreq write(unit=ounit,fmt=*)ival(i) 50 continue elseif ( field .eq. 'real' ) then do 60 i=1,nnzreq write(unit=ounit,fmt=*)rval(i) 60 continue else c field .eq. 'complex' do 70 i=1,nnzreq write(unit=ounit,fmt=*)real(cval(i)),aimag(cval(i)) 70 continue endif endif return c c Various errors c 1000 print *,'''',rep,''' representation not recognized.' print *, 'Recognized representations:' print *, ' array' print *, ' coordinate' stop 2000 print *,'''',field,''' field is not recognized.' print *, 'Recognized fields:' print *, ' real' print *, ' complex' print *, ' integer' print *, ' pattern' stop 3000 print *,'''',field,''' arrays are not recognized.' print *, 'Recognized fields:' print *, ' real' print *, ' complex' print *, ' integer' stop 4000 print *,'''',symm,''' symmetry is not recognized.' print *, 'Recognized symmetries:' print *, ' general' print *, ' symmetric' print *, ' hermitian' print *, ' skew-symmetric' stop cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc end cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc c End of subroutine mmwrite cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc subroutine lowerc(string,pos,len) cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc c Convert uppercase letters to lowercase letters in string with c starting position pos and length len. cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc integer pos, len character*(*) string character*26 lcase, ucase save lcase,ucase data lcase/'abcdefghijklmnopqrstuvwxyz'/ data ucase/'ABCDEFGHIJKLMNOPQRSTUVWXYZ'/ do 10 i=pos,len k = index(ucase,string(i:i)) if (k.ne.0) string(i:i) = lcase(k:k) 10 continue return end subroutine getwd(word,string,slen,start,next,wlen) cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc c Getwd extracts the first word from string starting c at position start. On return, next is the position c of the blank which terminates the word in string. c If the found word is longer than the allocated space c for the word in the calling program, the word will be c truncated to fit. c Count is set to the length of the word found. c c 30-Oct-96 Bug fix: fixed non-ansi zero stringlength cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc integer slen, start, next, begin, space, wlen character*(*) word character*(*) string begin = start do 5 i=start,slen space = index(string(i:slen),' ') if ( space .gt. 1) then next = i+space-1 go to 100 endif begin=begin+1 5 continue 100 continue wlen=next-begin if ( wlen .le. 0 ) then wlen = 0 word = ' ' return endif word=string(begin:begin+wlen) return end subroutine countwd(string,slen,start,count) cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc c Countwd counts the number of words in string starting c at position start. On return, count is the number of words. c 30-Oct-96 Routine added cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc character*(*) string integer slen, start, next, wordlength, count character tmp2*2 count = 0 next = 1 10 call getwd(tmp2,string,1024,next,next,wordlength) if ( wordlength .gt. 0 ) then count = count + 1 go to 10 endif return end