├── VERSION ├── LICENSE ├── .gitignore ├── lib ├── ruby-svd.rb ├── lsa.rb └── svd_matrix.rb ├── ext ├── extconf.rb ├── svd.c ├── svd.h └── nrutil.h ├── .document ├── pkg ├── ruby-svd-0.1.0.gem ├── ruby-svd-0.2.0.gem ├── ruby-svd-0.2.1.gem ├── ruby-svd-0.3.0.gem ├── ruby-svd-0.4.0.gem └── ruby-svd-0.4.5.gem ├── README.markdown ├── ruby-svd.gemspec └── Rakefile /VERSION: -------------------------------------------------------------------------------- 1 | 0.5.1 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Public Domain 2 | 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Makefile 2 | *.so 3 | *.bundle 4 | .DS_Store 5 | pkg/* 6 | -------------------------------------------------------------------------------- /lib/ruby-svd.rb: -------------------------------------------------------------------------------- 1 | require 'svd' 2 | require 'svd_matrix' 3 | require 'lsa' 4 | -------------------------------------------------------------------------------- /ext/extconf.rb: -------------------------------------------------------------------------------- 1 | # file: extconf.rb 2 | require 'mkmf' 3 | create_makefile('svd') 4 | -------------------------------------------------------------------------------- /.document: -------------------------------------------------------------------------------- 1 | README.rdoc 2 | lib/**/*.rb 3 | bin/* 4 | features/**/*.feature 5 | LICENSE 6 | -------------------------------------------------------------------------------- /pkg/ruby-svd-0.1.0.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willcannings/Ruby-SVD/HEAD/pkg/ruby-svd-0.1.0.gem -------------------------------------------------------------------------------- /pkg/ruby-svd-0.2.0.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willcannings/Ruby-SVD/HEAD/pkg/ruby-svd-0.2.0.gem -------------------------------------------------------------------------------- /pkg/ruby-svd-0.2.1.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willcannings/Ruby-SVD/HEAD/pkg/ruby-svd-0.2.1.gem -------------------------------------------------------------------------------- /pkg/ruby-svd-0.3.0.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willcannings/Ruby-SVD/HEAD/pkg/ruby-svd-0.3.0.gem -------------------------------------------------------------------------------- /pkg/ruby-svd-0.4.0.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willcannings/Ruby-SVD/HEAD/pkg/ruby-svd-0.4.0.gem -------------------------------------------------------------------------------- /pkg/ruby-svd-0.4.5.gem: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/willcannings/Ruby-SVD/HEAD/pkg/ruby-svd-0.4.5.gem -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Ruby SVD 2 | ======== 3 | Singular Value Decomposition for Ruby with no dependency on GSL or LAPACK. 4 | 5 | About 6 | ----- 7 | Ruby SVD provides an interface to the Numerical Recipies C implementation of an SVD matrix decomposer. It also includes an extension to the native Ruby Matrix class with a simple LSA 8 | method (decomposes the matrix, transposes matrix V, diagonalises the S array into a matrix, then removes all but the two leading terms in S to compress the original matrix to two dimensions). 9 | 10 | Sample Usage 11 | ------------ 12 | ```ruby 13 | require 'ruby-svd' 14 | 15 | tdm = SVDMatrix.new(4, 2) 16 | tdm.set_row(0, [1,0]) 17 | tdm.set_row(1, [1,0]) 18 | tdm.set_row(2, [0,1]) 19 | tdm.set_row(3, [0,1]) 20 | 21 | puts "== Term document matrix:" 22 | p tdm 23 | 24 | puts "\n== Decomposing matrix:" 25 | lsa = LSA.new(tdm) 26 | p lsa 27 | 28 | puts "\n== Classifying new column vector: [1, 0.5, 0, 0.5]" 29 | puts "Format is [column, similarity]" 30 | ranks = lsa.classify_vector([1,0.5,0,0.5]) 31 | p ranks 32 | 33 | sorted_ranks = ranks.sort_by(&:last).reverse 34 | puts "\n== Vector most similar to column #{sorted_ranks.first[0]}" 35 | p tdm.column(sorted_ranks.first[0]) 36 | ``` 37 | -------------------------------------------------------------------------------- /ruby-svd.gemspec: -------------------------------------------------------------------------------- 1 | # Generated by jeweler 2 | # DO NOT EDIT THIS FILE DIRECTLY 3 | # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' 4 | # -*- encoding: utf-8 -*- 5 | 6 | Gem::Specification.new do |s| 7 | s.name = %q{ruby-svd} 8 | s.version = "0.5.1" 9 | 10 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 11 | s.authors = ["Will Cannings"] 12 | s.date = %q{2011-05-30} 13 | s.description = %q{Singular Value Decomposition with no dependency on GSL or LAPACK} 14 | s.email = %q{me@willcannings.com} 15 | s.extensions = ["ext/extconf.rb"] 16 | s.extra_rdoc_files = [ 17 | "LICENSE", 18 | "README.markdown" 19 | ] 20 | s.files = [ 21 | "ext/extconf.rb", 22 | "ext/nrutil.h", 23 | "ext/svd.c", 24 | "ext/svd.h", 25 | "lib/lsa.rb", 26 | "lib/ruby-svd.rb", 27 | "lib/svd_matrix.rb" 28 | ] 29 | s.homepage = %q{http://github.com/willcannings/ruby-svd} 30 | s.require_paths = ["lib", "ext"] 31 | s.rubygems_version = %q{1.6.2} 32 | s.summary = %q{SVD for Ruby} 33 | 34 | if s.respond_to? :specification_version then 35 | s.specification_version = 3 36 | 37 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then 38 | else 39 | end 40 | else 41 | end 42 | end 43 | 44 | -------------------------------------------------------------------------------- /lib/lsa.rb: -------------------------------------------------------------------------------- 1 | require 'svd_matrix' 2 | 3 | class LSA 4 | attr_accessor :u, :s, :v 5 | 6 | def initialize(matrix) 7 | @u, @s, @v = matrix.decompose(2) 8 | end 9 | 10 | def inspect 11 | "U:\n#{@u.inspect}\n\nS:\n#{@s.inspect}\n\nV:\n#{@v.inspect}" 12 | end 13 | 14 | # Return a distance (cosine similarity) between a new vector, 15 | # and all the clusters (columns) used in the original matrix. 16 | # Returns a sorted list of indexes and distances, 17 | def classify_vector(values) 18 | raise "Unsupported vector length" unless values.size == @u.row_size || values.size == @v.row_size 19 | vector = Matrix.row_vector(values) 20 | mult_matrix = (values.size == @u.row_size ? @u : @v) 21 | comp_matrix = (values.size == @u.row_size ? @v : @u) 22 | 23 | position = vector * mult_matrix * @s.inverse 24 | x = position[0,0] 25 | y = position[0,1] 26 | results = [] 27 | 28 | comp_matrix.row_size.times do |index| 29 | results << [index, cosine_similarity(x, y, comp_matrix[index, 0], comp_matrix[index, 1])] 30 | end 31 | 32 | results.sort {|a, b| b[1] <=> a[1]} 33 | end 34 | 35 | # Determines the cosine similarity between two 2D points 36 | def cosine_similarity(x1, y1, x2, y2) 37 | dp = (x1 * x2) + (y1 * y2) 38 | mag1 = Math.sqrt((x1 ** 2) + (y1 ** 2)) 39 | mag2 = Math.sqrt((x2 ** 2) + (y2 ** 2)) 40 | return 0 if mag1 == 0 || mag2 == 0 41 | return (dp / (mag1 * mag2)) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'rake' 3 | 4 | begin 5 | require 'jeweler' 6 | Jeweler::Tasks.new do |gem| 7 | gem.name = "ruby-svd" 8 | gem.summary = "SVD for Ruby" 9 | gem.description = "Singular Value Decomposition with no dependency on GSL or LAPACK" 10 | gem.email = "me@willcannings.com" 11 | gem.homepage = "http://github.com/willcannings/ruby-svd" 12 | gem.authors = ["Will Cannings"] 13 | gem.files = ["lib/*", "ext/*"] 14 | gem.require_paths = ['lib', 'ext'] 15 | # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings 16 | end 17 | Jeweler::GemcutterTasks.new 18 | rescue LoadError 19 | puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler" 20 | end 21 | 22 | require 'rake/testtask' 23 | Rake::TestTask.new(:test) do |test| 24 | test.libs << 'lib' << 'test' 25 | test.pattern = 'test/**/test_*.rb' 26 | test.verbose = true 27 | end 28 | 29 | begin 30 | require 'rcov/rcovtask' 31 | Rcov::RcovTask.new do |test| 32 | test.libs << 'test' 33 | test.pattern = 'test/**/test_*.rb' 34 | test.verbose = true 35 | end 36 | rescue LoadError 37 | task :rcov do 38 | abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov" 39 | end 40 | end 41 | 42 | task :test => :check_dependencies 43 | 44 | task :default => :test 45 | 46 | require 'rake/rdoctask' 47 | Rake::RDocTask.new do |rdoc| 48 | version = File.exist?('VERSION') ? File.read('VERSION') : "" 49 | 50 | rdoc.rdoc_dir = 'rdoc' 51 | rdoc.title = "ruby-svd #{version}" 52 | rdoc.rdoc_files.include('README*') 53 | rdoc.rdoc_files.include('lib/**/*.rb') 54 | end 55 | -------------------------------------------------------------------------------- /ext/svd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "svd.h" 4 | 5 | VALUE decompose(VALUE module, VALUE matrix_ruby, VALUE m_ruby, VALUE n_ruby) { 6 | int m = NUM2INT(m_ruby); 7 | int n = NUM2INT(n_ruby); 8 | float **u = matrix(1, m, 1, n); 9 | float **v = matrix(1, m, 1, n); 10 | float *w = vector(1, n); 11 | VALUE *matrix_values = RARRAY_PTR(matrix_ruby); 12 | int offset = 0; 13 | int i, j; 14 | 15 | /* output arrays */ 16 | VALUE u_output = rb_ary_new(); 17 | VALUE v_output = rb_ary_new(); 18 | VALUE w_output = rb_ary_new(); 19 | VALUE output = rb_ary_new(); 20 | 21 | /* precondition */ 22 | if((m*n) != RARRAY_LEN(matrix_ruby)) { 23 | rb_raise(rb_eRangeError, "Size of the array is not equal to m * n"); 24 | return; 25 | } 26 | 27 | /* convert to u matrix */ 28 | for(i = 1; i <= m; i++) { 29 | for(j = 1; j <= n; j++) { 30 | offset = ((i-1)*n) + (j-1); 31 | u[i][j] = (float) NUM2DBL(matrix_values[offset]); 32 | } 33 | } 34 | 35 | /* perform SVD */ 36 | svdcmp(u, m, n, w, v); 37 | 38 | /* create w output array */ 39 | for(i = 1; i <= n; i++) 40 | rb_ary_push(w_output, rb_float_new(w[i])); 41 | 42 | /* create u arrays */ 43 | for(i = 1; i <= m; i++) { 44 | for(j = 1; j <= n; j++) { 45 | rb_ary_push(u_output, rb_float_new(u[i][j])); 46 | } 47 | } 48 | 49 | /* create v arrays */ 50 | for(i = 1; i <= n; i++) { 51 | for(j = 1; j <= n; j++) { 52 | rb_ary_push(v_output, rb_float_new(v[i][j])); 53 | } 54 | } 55 | 56 | rb_ary_push(output, u_output); 57 | rb_ary_push(output, w_output); 58 | rb_ary_push(output, v_output); 59 | return output; 60 | } 61 | 62 | void Init_svd() 63 | { 64 | VALUE module = rb_define_module("SVD"); 65 | rb_define_module_function(module, "decompose", decompose, 3); 66 | } 67 | -------------------------------------------------------------------------------- /lib/svd_matrix.rb: -------------------------------------------------------------------------------- 1 | require 'mathn' 2 | require 'svd' 3 | 4 | class SVDMatrix < Matrix 5 | public_class_method :new 6 | 7 | # Create a new SVD Matrix with m rows, n columns 8 | def initialize(m, n) 9 | @rows = Array.new(m) 10 | @column_size = n 11 | m.times {|i| @rows[i] = Array.new(n)} 12 | end 13 | 14 | # Set the value of the cell i, j 15 | def []=(i, j, val) 16 | @rows[i][j] = val 17 | end 18 | 19 | # Set the value of a row to an array 20 | def set_row(i, row) 21 | @rows[i] = row 22 | end 23 | 24 | # Nicely formatted inspect string for the matrix 25 | def inspect 26 | @rows.collect {|row| row.inspect}.join("\n") 27 | end 28 | 29 | # Perform SVD and decompose the matrix into three matrices: 30 | # U, W, and V. You can choose to reduce the dimensionality of 31 | # the data by setting a number of diagonal cells to 0. For 32 | # example, reduce_dimentions_to = 2 will set a 4x4 W 33 | # matrix into: 34 | # [NUM, 0, 0, 0] 35 | # [0, NUM, 0, 0] 36 | # [ 0, 0, 0, 0 ] 37 | # [ 0, 0, 0, 0 ] 38 | def decompose(reduce_dimensions_to = nil) 39 | input_array = [] 40 | @rows.each {|row| input_array += row} 41 | u_array, w_array, v_array = SVD.decompose(input_array, row_size, column_size) 42 | 43 | # recompose U matrix 44 | u = SVDMatrix.new(row_size, reduce_dimensions_to || column_size) 45 | row_size.times {|i| u.set_row(i, u_array.slice!(0, column_size)[0...(reduce_dimensions_to || column_size)])} 46 | 47 | # recompose V matrix 48 | v = SVDMatrix.new(column_size, reduce_dimensions_to || column_size) 49 | column_size.times {|i| v.set_row(i, v_array.slice!(0, column_size)[0...(reduce_dimensions_to || column_size)])} 50 | 51 | # diagonalise W array as a matrix 52 | if reduce_dimensions_to 53 | w_array = w_array[0...reduce_dimensions_to] 54 | end 55 | w = Matrix.diagonal(*w_array) 56 | 57 | [u, w, v] 58 | end 59 | 60 | # Reduce the number of dimensions of the data to dimensions. 61 | # Returns a back a recombined matrix (conceptually the original 62 | # matrix dimensionally reduced). For example Latent Semantic 63 | # Analysis uses 2 dimensions, and commonly tf-idf cell data. 64 | # The recombined matrix, and the 3 decomposed matrices are 65 | # returned. 66 | def reduce_dimensions(dimensions = 2) 67 | u, w, v = self.decompose(dimensions) 68 | [(u * w * v.transpose), u, w, v] 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /ext/svd.h: -------------------------------------------------------------------------------- 1 | /* svd.h */ 2 | /* Singular Value Decomposition for solving linear algebraic equations */ 3 | #include 4 | #include 5 | #include 6 | #include "nrutil.h" 7 | 8 | float pythag(float a, float b) 9 | { 10 | float absa,absb; 11 | absa=fabs(a); 12 | absb=fabs(b); 13 | if (absa > absb) return absa*sqrt(1.0+SQR(absb/absa)); 14 | else return (absb == 0.0 ? 0.0 : absb*sqrt(1.0+SQR(absa/absb))); 15 | } 16 | 17 | void svdcmp(float **a, int m, int n, float w[], float **v) 18 | { 19 | float pythag(float a, float b); 20 | int flag,i,its,j,jj,k,l,nm; 21 | float anorm,c,f,g,h,s,scale,x,y,z,*rv1; 22 | 23 | rv1=vector(1,n); 24 | g=scale=anorm=0.0; 25 | for (i=1;i<=n;i++) { 26 | l=i+1; 27 | rv1[i]=scale*g; 28 | g=s=scale=0.0; 29 | if (i <= m) { 30 | for (k=i;k<=m;k++) scale += fabs(a[k][i]); 31 | if (scale) { 32 | for (k=i;k<=m;k++) { 33 | a[k][i] /= scale; 34 | s += a[k][i]*a[k][i]; 35 | } 36 | f=a[i][i]; 37 | g = -SIGN(sqrt(s),f); 38 | h=f*g-s; 39 | a[i][i]=f-g; 40 | for (j=l;j<=n;j++) { 41 | for (s=0.0,k=i;k<=m;k++) s += a[k][i]*a[k][j]; 42 | f=s/h; 43 | for (k=i;k<=m;k++) a[k][j] += f*a[k][i]; 44 | } 45 | for (k=i;k<=m;k++) a[k][i] *= scale; 46 | } 47 | } 48 | w[i]=scale *g; 49 | g=s=scale=0.0; 50 | if (i <= m && i != n) { 51 | for (k=l;k<=n;k++) scale += fabs(a[i][k]); 52 | if (scale) { 53 | for (k=l;k<=n;k++) { 54 | a[i][k] /= scale; 55 | s += a[i][k]*a[i][k]; 56 | } 57 | f=a[i][l]; 58 | g = -SIGN(sqrt(s),f); 59 | h=f*g-s; 60 | a[i][l]=f-g; 61 | for (k=l;k<=n;k++) rv1[k]=a[i][k]/h; 62 | for (j=l;j<=m;j++) { 63 | for (s=0.0,k=l;k<=n;k++) s += a[j][k]*a[i][k]; 64 | for (k=l;k<=n;k++) a[j][k] += s*rv1[k]; 65 | } 66 | for (k=l;k<=n;k++) a[i][k] *= scale; 67 | } 68 | } 69 | anorm=FMAX(anorm,(fabs(w[i])+fabs(rv1[i]))); 70 | } 71 | for (i=n;i>=1;i--) { 72 | if (i < n) { 73 | if (g) { 74 | for (j=l;j<=n;j++) 75 | v[j][i]=(a[i][j]/a[i][l])/g; 76 | for (j=l;j<=n;j++) { 77 | for (s=0.0,k=l;k<=n;k++) s += a[i][k]*v[k][j]; 78 | for (k=l;k<=n;k++) v[k][j] += s*v[k][i]; 79 | } 80 | } 81 | for (j=l;j<=n;j++) v[i][j]=v[j][i]=0.0; 82 | } 83 | v[i][i]=1.0; 84 | g=rv1[i]; 85 | l=i; 86 | } 87 | for (i=IMIN(m,n);i>=1;i--) { 88 | l=i+1; 89 | g=w[i]; 90 | for (j=l;j<=n;j++) a[i][j]=0.0; 91 | if (g) { 92 | g=1.0/g; 93 | for (j=l;j<=n;j++) { 94 | for (s=0.0,k=l;k<=m;k++) s += a[k][i]*a[k][j]; 95 | f=(s/a[i][i])*g; 96 | for (k=i;k<=m;k++) a[k][j] += f*a[k][i]; 97 | } 98 | for (j=i;j<=m;j++) a[j][i] *= g; 99 | } else for (j=i;j<=m;j++) a[j][i]=0.0; 100 | ++a[i][i]; 101 | } 102 | for (k=n;k>=1;k--) { 103 | for (its=1;its<=30;its++) { 104 | flag=1; 105 | for (l=k;l>=1;l--) { 106 | nm=l-1; 107 | if ((float)(fabs(rv1[l])+anorm) == anorm) { 108 | flag=0; 109 | break; 110 | } 111 | if ((float)(fabs(w[nm])+anorm) == anorm) break; 112 | } 113 | if (flag) { 114 | c=0.0; 115 | s=1.0; 116 | for (i=l;i<=k;i++) { 117 | f=s*rv1[i]; 118 | rv1[i]=c*rv1[i]; 119 | if ((float)(fabs(f)+anorm) == anorm) break; 120 | g=w[i]; 121 | h=pythag(f,g); 122 | w[i]=h; 123 | h=1.0/h; 124 | c=g*h; 125 | s = -f*h; 126 | for (j=1;j<=m;j++) { 127 | y=a[j][nm]; 128 | z=a[j][i]; 129 | a[j][nm]=y*c+z*s; 130 | a[j][i]=z*c-y*s; 131 | } 132 | } 133 | } 134 | z=w[k]; 135 | if (l == k) { 136 | if (z < 0.0) { 137 | w[k] = -z; 138 | for (j=1;j<=n;j++) v[j][k] = -v[j][k]; 139 | } 140 | break; 141 | } 142 | if (its == 30) nrerror("no convergence in 30 svdcmp iterations"); 143 | x=w[l]; 144 | nm=k-1; 145 | y=w[nm]; 146 | g=rv1[nm]; 147 | h=rv1[k]; 148 | f=((y-z)*(y+z)+(g-h)*(g+h))/(2.0*h*y); 149 | g=pythag(f,1.0); 150 | f=((x-z)*(x+z)+h*((y/(f+SIGN(g,f)))-h))/x; 151 | c=s=1.0; 152 | for (j=l;j<=nm;j++) { 153 | i=j+1; 154 | g=rv1[i]; 155 | y=w[i]; 156 | h=s*g; 157 | g=c*g; 158 | z=pythag(f,h); 159 | rv1[j]=z; 160 | c=f/z; 161 | s=h/z; 162 | f=x*c+g*s; 163 | g = g*c-x*s; 164 | h=y*s; 165 | y *= c; 166 | for (jj=1;jj<=n;jj++) { 167 | x=v[jj][j]; 168 | z=v[jj][i]; 169 | v[jj][j]=x*c+z*s; 170 | v[jj][i]=z*c-x*s; 171 | } 172 | z=pythag(f,h); 173 | w[j]=z; 174 | if (z) { 175 | z=1.0/z; 176 | c=f*z; 177 | s=h*z; 178 | } 179 | f=c*g+s*y; 180 | x=c*y-s*g; 181 | for (jj=1;jj<=m;jj++) { 182 | y=a[jj][j]; 183 | z=a[jj][i]; 184 | a[jj][j]=y*c+z*s; 185 | a[jj][i]=z*c-y*s; 186 | } 187 | } 188 | rv1[l]=0.0; 189 | rv1[k]=f; 190 | w[k]=x; 191 | } 192 | } 193 | free_vector(rv1,1,n); 194 | } 195 | -------------------------------------------------------------------------------- /ext/nrutil.h: -------------------------------------------------------------------------------- 1 | /* nrutil.h */ 2 | #include 3 | #include 4 | #include 5 | #define NR_END 1 6 | #define FREE_ARG char* 7 | 8 | 9 | #ifndef _NR_UTILS_H_ 10 | #define _NR_UTILS_H_ 11 | 12 | static float sqrarg; 13 | #define SQR(a) ((sqrarg = (a)) == 0.0 ? 0.0 : sqrarg*sqrarg) 14 | 15 | static double dsqrarg; 16 | #define DSQR(a) ((dsqrarg = (a)) == 0.0 ? 0.0 : dsqrarg*dsqrarg) 17 | 18 | static double dmaxarg1, dmaxarg2; 19 | #define DMAX(a, b) (dmaxarg1 = (a), dmaxarg2 = (b), (dmaxarg1) > (dmaxarg2) ? (dmaxarg1) : (dmaxarg2)) 20 | 21 | static double dminarg1, dminarg2; 22 | #define DMIN(a, b) (dminarg1 = (a), dminarg2 = (b), (dminarg1) < (dminarg2) ? (dminarg1) : (dminarg2)) 23 | 24 | static float maxarg1, maxarg2; 25 | #define FMAX(a, b) (maxarg1 = (a), maxarg2 = (b), (maxarg1) > (maxarg2) ? (maxarg1) : (maxarg2)) 26 | 27 | static float minarg1, minarg2; 28 | #define FMIN(a, b) (minarg1 = (a), minarg2 = (b), (minarg1) < (minarg2) ? (minarg1) : (minarg2)) 29 | 30 | static long lmaxarg1, lmaxarg2; 31 | #define LMAX(a, b) (lmaxarg1 = (a), lmaxarg2 = (b), (lmaxarg1) > (lmaxarg2) ? (lmaxarg1) : (lmaxarg2)) 32 | 33 | static long lminarg1, lminarg2; 34 | #define LMIN(a, b) (lminarg1 = (a), lminarg2 = (b), (lminarg1) < (lminarg2) ? (lminarg1) : (lminarg2)) 35 | 36 | static int imaxarg1, imaxarg2; 37 | #define IMAX(a, b) (imaxarg1 = (a), imaxarg2 = (b), (imaxarg1) > (imaxarg2) ? (imaxarg1) : (imaxarg2)) 38 | 39 | static int iminarg1, iminarg2; 40 | #define IMIN(a, b) (iminarg1 = (a), iminarg2 = (b), (iminarg1) < (iminarg2) ? (iminarg1) : (iminarg2)) 41 | 42 | #define SIGN(a, b) ((b) >= 0.0 ? fabs(a) : -fabs(a)) 43 | 44 | 45 | /* prototype declaration */ 46 | 47 | #if defined(__STDC__) || defined(ANSI) || defined(NRANSI) /* ANSI */ 48 | 49 | void nrerror(char error_test[]); 50 | float *vector(long nl, long nh); 51 | int *ivector(long nl, long nh); 52 | unsigned char *cvector(long nl, long nh); 53 | unsigned long *lvector(long nl, long nh); 54 | double *dvector(long nl, long nh); 55 | float **matrix(long nrl, long nrh, long ncl, long nch); 56 | double **dmatrix(long nrl, long nrh, long ncl, long nch); 57 | int **imatrix(long nrl, long nrh, long ncl, long nch); 58 | float **submatrix(float **a, long oldrl, long oldrh, 59 | long oldcl, long oldch, long newrl, long newcl); 60 | float **convert_matrix(float *a, long nrl, long nrh, long ncl, long nch); 61 | float ***f3tensor(long nrl, long nrh, long ncl, long nch, 62 | long ndl, long ndh); 63 | void free_vector(float *v, long nl, long nh); 64 | void free_ivector(int *v, long nl, long nh); 65 | void free_cvector(unsigned char *v, long nl, long nh); 66 | void free_lvector(unsigned long *v, long nl, long nh); 67 | void free_dvector(double *v, long nl, long nh); 68 | void free_matrix(float **m, long nrl, long nrh, long ncl, long nch); 69 | void free_dmatrix(double **m, long nrl, long nrh, long ncl, long nch); 70 | void free_imatrix(int **m, long nrl, long nrh, long ncl, long nch); 71 | void free_submatrix(float **b, long nrl, long nrh, long ncl, long nch); 72 | void free_convert_matrix(float **b, long nrl, long nrh, 73 | long ncl, long nch); 74 | void free_f3tensor(float ***t, long nrl, long nrh, long ncl, long nch, 75 | long ndl, long ndh); 76 | 77 | #else /* ANSI */ 78 | /* traditional -K&R */ 79 | 80 | void nrerror(); 81 | float *vector(); 82 | 83 | #endif /* ANSI */ 84 | 85 | #endif /* _NR_UTILS_H_ */ 86 | 87 | 88 | /* function definition */ 89 | 90 | 91 | void nrerror(char error_text[]) 92 | /* Numerical Recipes standard error handler */ 93 | { 94 | fprintf(stderr, "Numerical Recipes run-time error...\n"); 95 | fprintf(stderr, "%s\n",error_text); 96 | fprintf(stderr, "...now exiting to system...\n"); 97 | exit(1); 98 | } 99 | 100 | float *vector(long nl, long nh) 101 | /* allocate a float vector with subscript range v[nl..nr] */ 102 | { 103 | float *v; 104 | 105 | v = (float *)malloc((size_t)((nh - nl + 1 + NR_END) * sizeof(float))); 106 | if (!v) nrerror("allocation failure in vector()"); 107 | return v - nl + NR_END; 108 | } 109 | 110 | int *ivector(long nl, long nh) 111 | /* allocate an int vector with subscript range v[nl..nr] */ 112 | { 113 | int *v; 114 | 115 | v = (int *)malloc((size_t)((nh - nl + 1 + NR_END)*sizeof(int))); 116 | if (!v) nrerror("allocation failure in ivector()"); 117 | return v - nl + NR_END; 118 | } 119 | 120 | unsigned char *cvector(long nl, long nh) 121 | /* allocate an unsigned char vector with subscript range v[nl..nr] */ 122 | { 123 | unsigned char *v; 124 | 125 | v = (unsigned char *)malloc((size_t)((nh - nl + 1 + NR_END) 126 | * sizeof(unsigned char))); 127 | if (!v) nrerror("allocation failure in cvector()"); 128 | return v - nl + NR_END; 129 | } 130 | 131 | unsigned long *lvector(long nl, long nh) 132 | /* allocate an unsigned long vector with subscript range v[nl..nr] */ 133 | { 134 | unsigned long *v; 135 | 136 | v = (unsigned long *)malloc((size_t)((nh - nl + 1 + NR_END) * sizeof(long))); 137 | if (!v) nrerror("allocation failure in lvector()"); 138 | return v - nl + NR_END; 139 | } 140 | 141 | double *dvector(long nl, long nh) 142 | /* allocate a double vector with subscript range v[nl..nr] */ 143 | { 144 | double *v; 145 | 146 | v = (double *)malloc((size_t)((nh - nl + 1 + NR_END) * sizeof(double))); 147 | if (!v) nrerror("allocation failure in dvector()"); 148 | return v - nl + NR_END; 149 | } 150 | 151 | float **matrix(long nrl, long nrh, long ncl, long nch) 152 | /* allocate a float matrix with subscript range m[nrl..nrh][ncl, nch] */ 153 | { 154 | long i, nrow = nrh - nrl + 1, ncol = nch - ncl + 1; 155 | float **m; 156 | 157 | /* allocate pointers to rows */ 158 | m = (float **)malloc((size_t)((nrow + NR_END) * sizeof(float *))); 159 | if (!m) nrerror("allocation failure 1 in matrix()"); 160 | m += NR_END; 161 | m -= nrl; 162 | 163 | /* allocate rows and set pointers to them */ 164 | m[nrl] = (float *)malloc((size_t)((nrow * ncol + NR_END) * sizeof(float))); 165 | if (!m[nrl]) nrerror("allocation failure 2 in matrix()"); 166 | m[nrl] += NR_END; 167 | m[nrl] -= ncl; 168 | 169 | for (i = nrl + 1; i <= nrh; i++) m[i] = m[i-1] + ncol; 170 | 171 | /* return pointer to array of pointers to rows */ 172 | return m; 173 | } 174 | 175 | double **dmatrix(long nrl, long nrh, long ncl, long nch) 176 | /* allocate a double matrix with subscript range m[nrl..nrh][ncl, nch] */ 177 | { 178 | long i, nrow = nrh - nrl + 1, ncol = nch - ncl + 1; 179 | double **m; 180 | 181 | /* allocate pointers to rows */ 182 | m = (double **)malloc((size_t)((nrow + NR_END) * sizeof(double *))); 183 | if (!m) nrerror("allocation failure 1 in matrix()"); 184 | m += NR_END; 185 | m -= nrl; 186 | 187 | /* allocate rows and set pointers to them */ 188 | m[nrl] = (double *)malloc((size_t)((nrow * ncol + NR_END) * sizeof(double))); 189 | if (!m[nrl]) nrerror("allocation failure 2 in matrix()"); 190 | m[nrl] += NR_END; 191 | m[nrl] -= ncl; 192 | 193 | for (i = nrl + 1; i <= nrh; i++) m[i] = m[i-1] + ncol; 194 | 195 | /* return pointer to array of pointers to rows */ 196 | return m; 197 | } 198 | 199 | int **imatrix(long nrl, long nrh, long ncl, long nch) 200 | /* allocate an int matrix with subscript range m[nrl..nrh][ncl, nch] */ 201 | { 202 | long i, nrow = nrh - nrl + 1, ncol = nch - ncl + 1; 203 | int **m; 204 | 205 | /* allocate pointers to rows */ 206 | m = (int **)malloc((size_t)((nrow + NR_END) * sizeof(int *))); 207 | if (!m) nrerror("allocation failure 1 in matrix()"); 208 | m += NR_END; 209 | m -= nrl; 210 | 211 | /* allocate rows and set pointers to them */ 212 | m[nrl] = (int *)malloc((size_t)((nrow * ncol + NR_END) * sizeof(int))); 213 | if (!m[nrl]) nrerror("allocation failure 2 in matrix()"); 214 | m[nrl] += NR_END; 215 | m[nrl] -= ncl; 216 | 217 | for (i = nrl + 1; i <= nrh; i++) m[i] = m[i-1] + ncol; 218 | 219 | /* return pointer to array of pointers to rows */ 220 | return m; 221 | } 222 | 223 | float **submatrix(float **a, long oldrl, long oldrh, long oldcl, long oldch, 224 | long newrl, long newcl) 225 | /* point a submatrix [newrl..][newcl..] 226 | to a[oldrl..oldrh][oldcl..oldch] */ 227 | 228 | { 229 | long i, j, nrow = oldrh - oldrl + 1, ncol = oldcl - newcl; 230 | float **m; 231 | 232 | /* allocate array of pointers to rows */ 233 | m = (float **)malloc((size_t)((nrow + NR_END) * sizeof(float *))); 234 | if (!m) nrerror("allocation failure in submatrix()"); 235 | m += NR_END; 236 | m -= newrl; 237 | 238 | /* set pointers to rows */ 239 | for (i = oldrl, j = newrl; i <= oldrh; i++, j++) m[j] = a[i] + ncol; 240 | 241 | /* return pointer to array of pointers to rows */ 242 | return m; 243 | } 244 | 245 | float **convert_matrix(float *a, long nrl, long nrh, long ncl, long nch) 246 | /* allocate a float matrix m[nrl..nrh][ncl..nch] that points to the matrix 247 | declared in the standard C manner as a[nrow][ncol], 248 | where nrow = nrh - nrl + 1 and ncol = nch - ncl + 1. The routine should 249 | be called with the address &a[0][0] as the first argument. */ 250 | { 251 | long i, j, nrow = nrh -nrl + 1, ncol = nch - ncl + 1; 252 | float **m; 253 | 254 | /* allocate pointers to rows */ 255 | m = (float **)malloc((size_t)((nrow + NR_END) * sizeof(float *))); 256 | if (!m) nrerror("allocation failure in convert_matrix()"); 257 | m += NR_END; 258 | m -= nrl; 259 | 260 | /* set pointers to rows */ 261 | m[nrl] = a - ncl; 262 | for (i = 1, j = nrl + 1; i < nrow; i++, j++) m[j] = m[j - 1] + ncol; 263 | 264 | /* return pointer to array of pointers to rows */ 265 | return m; 266 | } 267 | 268 | float ***f3tensor(long nrl, long nrh, long ncl, long nch, long ndl, long ndh) 269 | /* allocate a float 3tensor with range t[nrl..nrh][ncl..nch][ndl..ndh] */ 270 | { 271 | long i, j, nrow = nrh - nrl + 1, ncol = nch - ncl + 1, ndep = ndh - ndl + 1; 272 | float ***t; 273 | 274 | /* allocate pointers to pointers to rows */ 275 | t = (float ***)malloc((size_t)((nrow + NR_END) * sizeof(float **))); 276 | if (!t) nrerror("allocation failure 1 in f3tensor()"); 277 | t += NR_END; 278 | t -= nrl; 279 | 280 | /* allocate pointers to rows and set pointers to them */ 281 | t[nrl] = (float **)malloc((size_t)((nrow * ncol + NR_END) 282 | * sizeof(float *))); 283 | if (!t[nrl]) nrerror("allocation failure 2 in f3tensor()"); 284 | t[nrl] += NR_END; 285 | t[nrl] -= ncl; 286 | 287 | /* allocate rows and set pointers to them */ 288 | t[nrl][ncl] = (float *)malloc((size_t)((nrow * ncol * ndep + NR_END) 289 | * sizeof(float))); 290 | if (!t[nrl][ncl]) nrerror("allocation failure 3 in f3tensor()"); 291 | t[nrl][ncl] += NR_END; 292 | t[nrl][ncl] -= ndl; 293 | 294 | for (j = ncl + 1; j <= nch; j++) t[nrl][j] = t[nrl][j - 1] + ndep; 295 | for (i = nrl + 1; i <= nrh; i++) { 296 | t[i] = t[i - 1] + ncol; 297 | t[i][ncl] = t[i - 1][ncl] + ncol * ndep; 298 | for ( j = ncl + 1; j <= nch; j++) t[i][j] = t[i][j - 1] + ndep; 299 | } 300 | 301 | /* return pointer to array of pointers to rows */ 302 | return t; 303 | } 304 | 305 | void free_vector(float *v, long nl, long nh) 306 | /* free a float vector allocated with vector() */ 307 | { 308 | free((FREE_ARG)(v + nl - NR_END)); 309 | } 310 | 311 | void free_ivector(int *v, long nl, long nh) 312 | /* free an int vector allocated with ivector() */ 313 | { 314 | free((FREE_ARG)(v + nl - NR_END)); 315 | } 316 | 317 | void free_cvector(unsigned char *v, long nl, long nh) 318 | /* free an unsigned char vector allocated with cvector() */ 319 | { 320 | free((FREE_ARG)(v + nl - NR_END)); 321 | } 322 | 323 | void free_lvector(unsigned long *v, long nl, long nh) 324 | /* free an unsigned long vector allocated with lvector() */ 325 | { 326 | free((FREE_ARG)(v + nl - NR_END)); 327 | } 328 | 329 | void free_dvector(double *v, long nl, long nh) 330 | /* free a double vector allocated with dvector() */ 331 | { 332 | free((FREE_ARG)(v + nl - NR_END)); 333 | } 334 | 335 | void free_matrix(float **m, long nrl, long nrh, long ncl, long nch) 336 | /* free a float matrix allocated by matrix() */ 337 | { 338 | free((FREE_ARG)(m[nrl] + ncl - NR_END)); 339 | free((FREE_ARG)(m + nrl - NR_END)); 340 | } 341 | 342 | void free_dmatrix(double **m, long nrl, long nrh, long ncl, long nch) 343 | /* free a double matrix allocated by dmatrix() */ 344 | { 345 | free((FREE_ARG)(m[nrl] + ncl - NR_END)); 346 | free((FREE_ARG)(m + nrl - NR_END)); 347 | } 348 | 349 | void free_imatrix(int **m, long nrl, long nrh, long ncl, long nch) 350 | /* free an int matrix allocated by imatrix() */ 351 | { 352 | free((FREE_ARG)(m[nrl] + ncl - NR_END)); 353 | free((FREE_ARG)(m + nrl - NR_END)); 354 | } 355 | 356 | void free_submatirx(float **b, long nrl, long nrh, long ncl, long nch) 357 | /* free a submatirx allocated by submatirx() */ 358 | { 359 | free((FREE_ARG)(b + nrl - NR_END)); 360 | } 361 | 362 | void free_convert_matirx(float **b, long nrl, long nrh, long ncl, long nch) 363 | /* free a matirx allocated by convert_matirx() */ 364 | { 365 | free((FREE_ARG)(b + nrl - NR_END)); 366 | } 367 | 368 | void free_f3tensor(float ***t, long nrl, long nrh, long ncl, long nch, 369 | long ndl, long ndh) 370 | /* free a float f3tensor allocated by f3tensor() */ 371 | { 372 | free((FREE_ARG)(t[nrl][ncl] + ndl - NR_END)); 373 | free((FREE_ARG)(t[nrl] + ncl - NR_END)); 374 | free((FREE_ARG)(t + nrl - NR_END)); 375 | } 376 | 377 | --------------------------------------------------------------------------------