These notes, and also files mylib.dll, mysrc.f, myfun.R, and Makefile and instructions for installing Cygwin, can all be found at http://www.stats.bris.ac.uk/~peter/R/ --- |1| --- Put the file mylib.dll in a suitable folder on your PC, say c:\Home\Research\Expt (assumed below) --- |2| --- Type the following in R on Windows: ---------------- setwd('c:/Home/Research/Expt') dyn.load('mylib.dll') myfun<-function(a,js) { m<-nrow(a); n<-ncol(a) z<-.Fortran('mysub',a=as.double(a),as.integer(m), as.integer(m),as.integer(n), as.integer(js),integer(m),double(m)) matrix(z$a,m,n) } a<-matrix(sample(20),4,5) a myfun(a,3) a[order(a[,3]),] ---------------- See, it works! The function takes a numerical matrix, and reorders the rows so that the js'th column is in non-decreasing order. You'll probably want to save myfun for future use (in .R (text) or .RData (binary) form). --- |3| --- What is the role of this peculiar ".Fortran" function? It provides a connection between the argument list of the mysub subroutine, which we will see below begins: subroutine mysub(a,lda,m,n,js,na,w) real*8 a(lda,n),w(m) integer na(m) and the R session. Note that .Fortran has 8 arguments in this case - the name 'mysub' followed by one argument for each of the formal parameters of mysub. Note the care given to ensuring that each actual argument has the correct 'storage mode' (double, integer, etc.) before being passed to Fortran: input parameters are coerced to the right mode, while for others space has to be declared. The .Fortran function returns the (output) values of all of its arguments as a list; it is useful to give names to those components we are interested in. --- |4| --- Add a touch of elegance, convenience and portability: insert the following as the first lines of the function myfun - then you don't need to do the "dyn.load('mylib.dll')" ever again. This function should now work on other platforms (although you will need the appropriate library file to load). ---------------- if(!is.loaded(symbol.For('mysub'))) { lib<-paste('mylib',.Platform$dynlib.ext,sep='') dyn.load(lib) cat('...',lib,'loaded\n') } ---------------- --- |5| --- An example in C (from the Writing R extensions manual): C function: void convolve(double *a, int *na, double *b, int *nb, double *ab) { int i, j, nab = *na + *nb - 1; for(i = 0; i < nab; i++) ab[i] = 0.0; for(i = 0; i < *na; i++) for(j = 0; j < *nb; j++) ab[i + j] += a[i] * b[j]; } R wrapper: conv <- function(a, b) { .C("convolve", as.double(a), as.integer(length(a)), as.double(b), as.integer(length(b)), ab = double(length(a) + length(b) - 1))$ab } --- |6| --- Recompiling the Fortran (or C) source: For this you need Cygwin installed on your PC. (see my notes at http://www.stats.bris.ac.uk/~peter/R/; Full details at www.cygwin.com) (i) Put the following in file Makefile, in the same directory: ---------------- FC = g77 CC = gcc FFLAGS = -Wall -O2 mylib.dll: mysrc.o gcc -shared -mno-cygwin -o mylib.dll mysrc.o \ -L/cygdrive/c/"Program Files"/R/Rw1062/bin -lR ---------------- (it's important that it is a 'tab' character before gcc on the 5th line, not spaces; also make sure the path for R is correct for your PC, including the version number) (ii) Put this in file mysrc.f: ---------------- subroutine mysub(a,lda,m,n,js,na,w) real*8 a(lda,n),w(m) integer na(m) c.. sorts matrix a, keeping rows together, c so that column j is in increasing order do i = 1,m na(i) = i end do call sort(na,1,m,a(1,js),m) do j = 1,n do i = 1,m w(i) = a(na(i),j) end do do i = 1,m a(i,j) = w(i) end do end do return end c------------------------------------- subroutine sort(na,llo,lhi,q,nq) real*8 q,ql dimension na(lhi),q(nq) c c sorts array na(l), l = llo to lhi c according to values of q(na(l)) c so if llo = 1 and na(l) = l on entry, c obtain na(l) = index of l-th smallest q on exit. c if(llo.ge.lhi) return mesh = lhi-llo+1 1 mesh = (mesh+1)/3 lst = llo+mesh do 3 l1 = lst,lhi nal = na(l1) ql = q(nal) lnow = l1 do 2 l2 = lst,l1,mesh lnext = lnow-mesh nan = na(lnext) if(ql.ge.q(nan)) go to 3 na(lnow) = nan 2 lnow = lnext 3 na(lnow) = nal if(mesh.gt.1) go to 1 return end ---------------- (iii) Then type "make" in a Cygwin shell window: you should get something like ...Research/Expt $ make g77 -Wall -O2 -c mysrc.f -o mysrc.o gcc -shared -mno-cygwin -o mylib.dll mysrc.o \ -L/cygdrive/c/"Program Files"/R/Rw1062/bin -lR ...Research/Expt $ Now you can repeat step 2. NOTES (a) The four names 'mysub', 'mysrc', 'mylib' and 'myfun' used above can of course be replaced by names of your choice. Furthermore, these names need not be different, in fact it will often be sensible to make them all the same. Different names are used here merely to clarify the different objects involved, and to help match one reference to another. The Fortran routine mysub is in source file mysrc.f, which is compiled into mylib.dll, called by R wrapper function myfun(). (b) Use of C from R is similar to use of Fortran. (c) Compare the sources for mysub and myfun carefully to see the way that you must force R to use the correct storage mode for the parameters passed to the Fortran code, and how to deal with parameters that are inputs, outputs (or both), or just workspace. (d) Care is needed for other storage modes than integer or double, or for structures more complicated than a vector (matrices and other arrays are easy enough, but lists...). The full story is in Section 4 of the 'Writing R extensions' manual (probably in pdf form in the Help menu of R on your PC). (e) You should not use ordinary input or output from the Fortran or C code. See the same manual for how to do input or output if you must. Similarly avoid using 'stop' - always return control to R. Care is needed if you need to access other libraries from within the Fortran or C code. (f) You can use multiple source files (and mix Fortran and C) - just adapt the Makefile accordingly (list all the .o files where it says 'mysrc.o'). All subroutines in the resulting .dll library can of course be called from R. (g) In Linux, everything is similar - the extension for shared library files becomes .so not .dll . For other Unix systems you will probably need to change FC and CC in the Makefile.