; docformat = 'rst' ;+ ; Returns true if all rows of a matrix are unique. ; ; :categories: ; Linear algebra ; ; :author: ; Mats Löfdahl, Institute for Solar Physics, mats@astro.su.se. ; ; :params: ; M : in, type=array ; 2D matrix ; ; :keywords: ; ; duplicateindx : out, optional ; ; An array of row indices corresponding to duplicate rows. ; (Excluding the first occurence.) ; ; uniqindx : out, optional ; ; An array of row indices corresponding to unique rows. ; ; Muniq : out, optional ; ; A version of M with duplicate rows removed. ; ; OP : in, optional, type=string, default="'##'" ; ; The assumed matrix multiplication operator, "#" or "##". This ; specifies your matrix conventions, (row,column) or (column,row). ; The default is (column,row), the convention that goes with using ; the ## operator for matrix multiplication. ; ; COLUMN : in, optional, type=boolean, default=FALSE ; ; Set this if you want to operate on columns instead. ; ; :returns: ; Boolean, true if there are only unique rows, false otherwise. ; ; :examples: ; IDL> print, 'Matrix M: ', M ; ; IDL> if ~UniqRows(M,Muniq=Muniq) then print, 'Matrix M with duplicate rows removed: ', Muniq ; ; :history: ; 2012-03-23: Written by Mats Löfdahl, Institute for Solar Physics. ; ; 2012-11-21 : Switched to docformat 'rst'. ; ; 2013-01-25 : Added the OP keyword and logic for taking care of ; scalars and one-row matrices. Also added keyword ; duplicateindx. ; ; 2013-01-29 : Added the COLUMN keyword. ;- function UniqRows, M $ , DUPLICATEINDX = duplicateindx $ , UNIQINDX = uniqindx $ , MUNIQ = Muniq $ , OP = op $ , COLUMN = column if n_elements(op) eq 0 then op = '##' ; Default ## if keyword_set(column) then begin ;; Easiest way to implement operation on columns is to switch the ;; assumed operator. So from here on, whenever it says "rows", ;; think "columns". if op eq "#" then op = "##" else op = "#" endif if op eq '#' then begin Nrows = (size(M, /dim))[0] endif else begin if size(M, /n_dim) lt 2 then begin Nrows = 1 endif else begin Nrows = (size(M, /dim))[1] endelse endelse ;; print, 'Number of rows:', Nrows ;; If M is a scalar or has just a single row, there are by ;; definition no duplicate rows. So it makes some sense to return ;; TRUE in this case. if Nrows lt 2 then return, 1 ;; We can return as soon as we find a duplicate row if we don't have ;; to return uniqindx or Muniq. FastExit = ~(arg_present(uniqindx) or $ arg_present(Muniq) or $ arg_present(duplicateindx)) if ~FastExit then duplicateindx = lonarr(Nrows) k = 0 ; Initialize the number of duplicates for i = 0, Nrows-2 do begin ; Check all rows for j = i+1, Nrows-1 do begin ; Compare with higher index rows ; print, i, j if op eq '#' then begin equalrows = array_equal(M[i, *], M[j, *]) endif else begin equalrows = array_equal(M[*, i], M[*, j]) endelse if equalrows then begin if FastExit then begin ;; Early exit if possible return, 0 endif else begin duplicateindx[k] = j k += 1 ; Increment the number of duplicates endelse endif endfor endfor if k eq 0 then return, 1 ; All unique ;; If we got this far, there are duplicates and we need to return ;; either Muniq, duplicateindx, or uniqindx. duplicateindx = duplicateindx[0:k-1] uniqindx = SetDifference(indgen(Nrows), duplicateindx) if arg_present(Muniq) then begin if op eq '#' then begin Muniq = M[uniqindx, *] endif else begin Muniq = M[*, uniqindx] endelse endif return, 0 ; All not unique end