hornetseye-fftw3 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ hornetseye-fftw3
2
+ ================
3
+
4
+ **Author**: Jan Wedekind
5
+ **Copyright**: 2011
6
+ **License**: GPL
7
+
8
+ Synopsis
9
+ --------
10
+
11
+ This Ruby extension provides bindings for the FFTW3 library.
12
+
13
+ Installation
14
+ ------------
15
+
16
+ *hornetseye-fftw3* requires the FFTW3 libraries. If you are running Debian or (K)ubuntu, you can install them like this:
17
+
18
+ $ sudo aptitude install libfftw3-dev
19
+
20
+ To install this Ruby extension, use the following command:
21
+
22
+ $ sudo gem install hornetseye-fftw3
23
+
24
+ Alternatively you can build and install the Ruby extension from source as follows:
25
+
26
+ $ rake
27
+ $ sudo rake install
28
+
data/Rakefile ADDED
@@ -0,0 +1,183 @@
1
+ #!/usr/bin/env ruby
2
+ require 'date'
3
+ require 'rake/clean'
4
+ require 'rake/testtask'
5
+ require 'rake/packagetask'
6
+ require 'rake/loaders/makefile'
7
+ require 'rbconfig'
8
+ require 'tempfile'
9
+
10
+ PKG_NAME = 'hornetseye-fftw3'
11
+ PKG_VERSION = '0.1.0'
12
+ CFG = RbConfig::CONFIG
13
+ CXX = ENV[ 'CXX' ] || 'g++'
14
+ RB_FILES = FileList[ 'lib/**/*.rb' ]
15
+ CC_FILES = FileList[ 'ext/*.cc' ]
16
+ HH_FILES = FileList[ 'ext/*.hh' ] + FileList[ 'ext/*.tcc' ]
17
+ TC_FILES = FileList[ 'test/tc_*.rb' ]
18
+ TS_FILES = FileList[ 'test/ts_*.rb' ]
19
+ SO_FILE = "ext/#{PKG_NAME.tr '\-', '_'}.#{CFG[ 'DLEXT' ]}"
20
+ PKG_FILES = [ 'Rakefile', 'README.md', 'COPYING', '.document' ] +
21
+ RB_FILES + CC_FILES + HH_FILES + TS_FILES + TC_FILES
22
+ BIN_FILES = [ 'README.md', 'COPYING', '.document', SO_FILE ] +
23
+ RB_FILES + TS_FILES + TC_FILES
24
+ SUMMARY = %q{Fourier transforms}
25
+ DESCRIPTION = %q{This Ruby extension provides bindings for the FFTW3 library.}
26
+ AUTHOR = %q{Jan Wedekind}
27
+ EMAIL = %q{jan@wedesoft.de}
28
+ HOMEPAGE = %q{http://wedesoft.github.com/hornetseye-fftw3/}
29
+
30
+ OBJ = CC_FILES.ext 'o'
31
+ $CXXFLAGS = "-DNDEBUG -DHAVE_CONFIG_H -D__STDC_CONSTANT_MACROS #{CFG[ 'CPPFLAGS' ]} #{CFG[ 'CFLAGS' ]}"
32
+ if CFG[ 'rubyhdrdir' ]
33
+ $CXXFLAGS = "#{$CXXFLAGS} -I#{CFG[ 'rubyhdrdir' ]} " +
34
+ "-I#{CFG[ 'rubyhdrdir' ]}/#{CFG[ 'arch' ]}"
35
+ else
36
+ $CXXFLAGS = "#{$CXXFLAGS} -I#{CFG[ 'archdir' ]}"
37
+ end
38
+ $LIBRUBYARG = "-L#{CFG[ 'libdir' ]} #{CFG[ 'LIBRUBYARG' ]} #{CFG[ 'LDFLAGS' ]} " +
39
+ "#{CFG[ 'SOLIBS' ]} #{CFG[ 'DLDLIBS' ]}"
40
+ $SITELIBDIR = CFG[ 'sitelibdir' ]
41
+ $SITEARCHDIR = CFG[ 'sitearchdir' ]
42
+ $LDSHARED = CFG[ 'LDSHARED' ][ CFG[ 'LDSHARED' ].index( ' ' ) .. -1 ]
43
+
44
+ task :default => :all
45
+
46
+ desc 'Compile Ruby extension (default)'
47
+ task :all => [ SO_FILE ]
48
+
49
+ file SO_FILE => OBJ do |t|
50
+ sh "#{CXX} -shared -o #{t.name} #{OBJ} -lfftw3 -lfftw3f #{$LIBRUBYARG}"
51
+ end
52
+
53
+ task :test => [ SO_FILE ]
54
+
55
+ desc 'Install Ruby extension'
56
+ task :install => :all do
57
+ verbose true do
58
+ for f in RB_FILES do
59
+ FileUtils.mkdir_p "#{$SITELIBDIR}/#{File.dirname f.gsub( /^lib\//, '' )}"
60
+ FileUtils.cp_r f, "#{$SITELIBDIR}/#{f.gsub( /^lib\//, '' )}"
61
+ end
62
+ FileUtils.mkdir_p $SITEARCHDIR
63
+ FileUtils.cp SO_FILE, "#{$SITEARCHDIR}/#{File.basename SO_FILE}"
64
+ end
65
+ end
66
+
67
+ desc 'Uninstall Ruby extension'
68
+ task :uninstall do
69
+ verbose true do
70
+ for f in RB_FILES do
71
+ FileUtils.rm_f "#{$SITELIBDIR}/#{f.gsub /^lib\//, ''}"
72
+ end
73
+ FileUtils.rm_f "#{$SITEARCHDIR}/#{File.basename SO_FILE}"
74
+ end
75
+ end
76
+
77
+ Rake::TestTask.new do |t|
78
+ t.libs << 'ext'
79
+ t.test_files = TC_FILES
80
+ end
81
+
82
+ begin
83
+ require 'yard'
84
+ YARD::Rake::YardocTask.new :yard do |y|
85
+ y.files << RB_FILES
86
+ end
87
+ rescue LoadError
88
+ STDERR.puts 'Please install \'yard\' if you want to generate documentation'
89
+ end
90
+
91
+ Rake::PackageTask.new PKG_NAME, PKG_VERSION do |p|
92
+ p.need_tar = true
93
+ p.package_files = PKG_FILES
94
+ end
95
+
96
+ begin
97
+ require 'rubygems'
98
+ require 'rubygems/builder'
99
+ $SPEC = Gem::Specification.new do |s|
100
+ s.name = PKG_NAME
101
+ s.version = PKG_VERSION
102
+ s.platform = Gem::Platform::RUBY
103
+ s.date = Date.today.to_s
104
+ s.summary = SUMMARY
105
+ s.description = DESCRIPTION
106
+ s.author = AUTHOR
107
+ s.email = EMAIL
108
+ s.homepage = HOMEPAGE
109
+ s.files = PKG_FILES
110
+ s.test_files = TC_FILES
111
+ s.require_paths = [ 'lib', 'ext' ]
112
+ s.rubyforge_project = %q{hornetseye}
113
+ s.extensions = %w{Rakefile}
114
+ s.has_rdoc = 'yard'
115
+ s.extra_rdoc_files = []
116
+ s.rdoc_options = %w{--no-private}
117
+ s.add_dependency %<malloc>, [ '~> 1.1' ]
118
+ s.add_dependency %<multiarray>, [ '~> 0.20' ]
119
+ s.add_development_dependency %q{rake}
120
+ end
121
+ GEM_SOURCE = "#{PKG_NAME}-#{PKG_VERSION}.gem"
122
+ $BINSPEC = Gem::Specification.new do |s|
123
+ s.name = PKG_NAME
124
+ s.version = PKG_VERSION
125
+ s.platform = Gem::Platform::CURRENT
126
+ s.date = Date.today.to_s
127
+ s.summary = SUMMARY
128
+ s.description = DESCRIPTION
129
+ s.author = AUTHOR
130
+ s.email = EMAIL
131
+ s.homepage = HOMEPAGE
132
+ s.files = BIN_FILES
133
+ s.test_files = TC_FILES
134
+ s.require_paths = [ 'lib', 'ext' ]
135
+ s.rubyforge_project = %q{hornetseye}
136
+ s.has_rdoc = 'yard'
137
+ s.extra_rdoc_files = []
138
+ s.rdoc_options = %w{--no-private}
139
+ s.add_dependency %<malloc>, [ '~> 1.1' ]
140
+ s.add_dependency %<multiarray>, [ '~> 0.20' ]
141
+ end
142
+ GEM_BINARY = "#{PKG_NAME}-#{PKG_VERSION}-#{$BINSPEC.platform}.gem"
143
+ desc "Build the gem file #{GEM_SOURCE}"
144
+ task :gem => [ "pkg/#{GEM_SOURCE}" ]
145
+ file "pkg/#{GEM_SOURCE}" => [ 'pkg' ] + $SPEC.files do
146
+ when_writing 'Creating GEM' do
147
+ Gem::Builder.new( $SPEC ).build
148
+ verbose true do
149
+ FileUtils.mv GEM_SOURCE, "pkg/#{GEM_SOURCE}"
150
+ end
151
+ end
152
+ end
153
+ desc "Build the gem file #{GEM_BINARY}"
154
+ task :gem_binary => [ "pkg/#{GEM_BINARY}" ]
155
+ file "pkg/#{GEM_BINARY}" => [ 'pkg' ] + $BINSPEC.files do
156
+ when_writing 'Creating binary GEM' do
157
+ Gem::Builder.new( $BINSPEC ).build
158
+ verbose true do
159
+ FileUtils.mv GEM_BINARY, "pkg/#{GEM_BINARY}"
160
+ end
161
+ end
162
+ end
163
+ rescue LoadError
164
+ STDERR.puts 'Please install \'rubygems\' if you want to create Gem packages'
165
+ end
166
+
167
+ rule '.o' => '.cc' do |t|
168
+ sh "#{CXX} #{$CXXFLAGS} -c -o #{t.name} #{t.source}"
169
+ end
170
+
171
+ file ".depends.mf" do |t|
172
+ sh "g++ -MM #{CC_FILES.join ' '} | " +
173
+ "sed -e :a -e N -e 's/\\n/\\$/g' -e ta | " +
174
+ "sed -e 's/ *\\\\\\$ */ /g' -e 's/\\$/\\n/g' | sed -e 's/^/ext\\//' > #{t.name}"
175
+ end
176
+ CC_FILES.each do |t|
177
+ file t.ext(".o") => t
178
+ end
179
+ import ".depends.mf"
180
+
181
+ CLEAN.include 'ext/*.o'
182
+ CLOBBER.include SO_FILE, 'doc', '.yardoc', '.depends.mf'
183
+
data/ext/error.hh ADDED
@@ -0,0 +1,50 @@
1
+ /* HornetsEye - Computer Vision with Ruby
2
+ Copyright (C) 2006, 2007, 2008, 2009, 2010 Jan Wedekind
3
+
4
+ This program is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU General Public License as published by
6
+ the Free Software Foundation, either version 3 of the License, or
7
+ (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU General Public License for more details.
13
+
14
+ You should have received a copy of the GNU General Public License
15
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
+ #ifndef ERROR_HH
17
+ #define ERROR_HH
18
+
19
+ #include <exception>
20
+ #include <sstream>
21
+
22
+ class Error: public std::exception
23
+ {
24
+ public:
25
+ Error(void) {}
26
+ Error( Error &e ): std::exception( e )
27
+ { m_message << e.m_message.str(); }
28
+ virtual ~Error(void) throw() {}
29
+ template< typename T >
30
+ std::ostream &operator<<( const T &t )
31
+ { m_message << t; return m_message; }
32
+ std::ostream &operator<<( std::ostream& (*__pf)(std::ostream&) )
33
+ { (*__pf)( m_message ); return m_message; }
34
+ virtual const char* what(void) const throw() {
35
+ temp = m_message.str();
36
+ return temp.c_str();
37
+ }
38
+ protected:
39
+ std::ostringstream m_message;
40
+ mutable std::string temp;
41
+ };
42
+
43
+ #define ERRORMACRO( condition, class, params, message ) \
44
+ if ( !( condition ) ) { \
45
+ class _e params; \
46
+ _e << message; \
47
+ throw _e; \
48
+ };
49
+
50
+ #endif
data/ext/init.cc ADDED
@@ -0,0 +1,38 @@
1
+ /* HornetsEye - Computer Vision with Ruby
2
+ Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Jan Wedekind
3
+
4
+ This program is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU General Public License as published by
6
+ the Free Software Foundation, either version 3 of the License, or
7
+ (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU General Public License for more details.
13
+
14
+ You should have received a copy of the GNU General Public License
15
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
+ #include "rubyinc.hh"
17
+ #include "node.hh"
18
+
19
+ #ifdef WIN32
20
+ #define DLLEXPORT __declspec(dllexport)
21
+ #define DLLLOCAL
22
+ #else
23
+ #define DLLEXPORT __attribute__ ((visibility("default")))
24
+ #define DLLLOCAL __attribute__ ((visibility("hidden")))
25
+ #endif
26
+
27
+ extern "C" DLLEXPORT void Init_hornetseye_fftw3(void);
28
+
29
+ extern "C" {
30
+
31
+ void Init_hornetseye_fftw3(void)
32
+ {
33
+ VALUE rbHornetseye = rb_define_module( "Hornetseye" );
34
+ Node::registerRubyClass( rbHornetseye );
35
+ rb_require( "hornetseye_fftw3_ext.rb" );
36
+ }
37
+
38
+ }
data/ext/node.cc ADDED
@@ -0,0 +1,223 @@
1
+ /* HornetsEye - Computer Vision with Ruby
2
+ Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Jan Wedekind
3
+
4
+ This program is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU General Public License as published by
6
+ the Free Software Foundation, either version 3 of the License, or
7
+ (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU General Public License for more details.
13
+
14
+ You should have received a copy of the GNU General Public License
15
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
+ #include <iostream>
17
+
18
+ #include <boost/shared_array.hpp>
19
+ #include <fftw3.h>
20
+ #include "error.hh"
21
+ #include "node.hh"
22
+
23
+ using namespace boost;
24
+ using namespace std;
25
+
26
+ VALUE Node::cRubyClass = Qnil;
27
+
28
+ VALUE Node::mModule = Qnil;
29
+
30
+ VALUE Node::registerRubyClass( VALUE module )
31
+ {
32
+ mModule = module;
33
+ cRubyClass = rb_define_class_under( module, "Node", rb_cObject );
34
+ rb_define_method( cRubyClass, "fft", RUBY_METHOD_FUNC( wrapFFT ), 1 );
35
+ rb_define_method( cRubyClass, "rfft", RUBY_METHOD_FUNC( wrapRFFT ), 1 );
36
+ return cRubyClass;
37
+ }
38
+
39
+ VALUE Node::wrapFFT( VALUE rbSelf, VALUE rbForward )
40
+ {
41
+ VALUE rbRetVal = Qnil;
42
+ try {
43
+ int sign = rbForward == Qtrue ? FFTW_FORWARD : FFTW_BACKWARD;
44
+ VALUE rbMalloc = rb_funcall( rbSelf, rb_intern( "memory" ), 0 );
45
+ VALUE rbTypecode = rb_funcall( rbSelf, rb_intern( "typecode" ), 0 );
46
+ VALUE rbSize = rb_funcall( rbSelf, rb_intern( "size" ), 0 );
47
+ int size = NUM2INT( rbSize );
48
+ VALUE rbShape = rb_funcall( rbSelf, rb_intern( "shape" ), 0 );
49
+ int rank = RARRAY_LEN( rbShape );
50
+ shared_array< int > n( new int[ rank ] );
51
+ for ( int i=0; i<rank; i++ )
52
+ n[ rank - 1 - i ] = NUM2INT( RARRAY_PTR( rbShape )[i] );
53
+ VALUE rbDest = Qnil;
54
+ if ( rb_funcall( rbTypecode, rb_intern( "==" ), 1,
55
+ rb_const_get( mModule, rb_intern( "SCOMPLEX" ) ) ) == Qtrue ) {
56
+ assert( sizeof( fftwf_complex ) == sizeof( float ) * 2 );
57
+ fftwf_complex *source;
58
+ Data_Get_Struct( rbMalloc, fftwf_complex, source );
59
+ fftwf_complex *dest = ALLOC_N( fftwf_complex, size );
60
+ rbDest = Data_Wrap_Struct( rb_const_get( mModule, rb_intern( "Malloc" ) ), 0, xfree,
61
+ (void *)dest );
62
+ rb_ivar_set( rbDest, rb_intern( "@size" ), INT2NUM( size * sizeof( fftwf_complex ) ) );
63
+ fftwf_plan plan =
64
+ fftwf_plan_dft( rank, n.get(), source, dest, sign,
65
+ FFTW_ESTIMATE | FFTW_PRESERVE_INPUT );
66
+ ERRORMACRO( plan != NULL, Error, , "Error creating FFTW plan" );
67
+ fftwf_execute( plan );
68
+ fftwf_destroy_plan( plan );
69
+ } else if ( rb_funcall( rbTypecode, rb_intern( "==" ), 1,
70
+ rb_const_get( mModule, rb_intern( "DCOMPLEX" ) ) ) == Qtrue ) {
71
+ assert( sizeof( fftw_complex ) == sizeof( double ) * 2 );
72
+ fftw_complex *source;
73
+ Data_Get_Struct( rbMalloc, fftw_complex, source );
74
+ fftw_complex *dest = ALLOC_N( fftw_complex, size );
75
+ rbDest = Data_Wrap_Struct( rb_const_get( mModule, rb_intern( "Malloc" ) ), 0, xfree,
76
+ (void *)dest );
77
+ rb_ivar_set( rbDest, rb_intern( "@size" ), INT2NUM( size * sizeof( fftw_complex ) ) );
78
+ fftw_plan plan =
79
+ fftw_plan_dft( rank, n.get(), source, dest, sign,
80
+ FFTW_ESTIMATE | FFTW_PRESERVE_INPUT );
81
+ ERRORMACRO( plan != NULL, Error, , "Error creating FFTW plan" );
82
+ fftw_execute( plan );
83
+ fftw_destroy_plan( plan );
84
+ } else {
85
+ ERRORMACRO( false, Error, ,
86
+ "This datatype is not supported for the selected transform" );
87
+ };
88
+ rbRetVal = rb_funcall2( rb_funcall( rb_const_get( mModule, rb_intern( "Sequence" ) ),
89
+ rb_intern( "import" ), 3,
90
+ rbTypecode, rbDest, rbSize ),
91
+ rb_intern( "reshape" ), rank, RARRAY_PTR(rbShape) );
92
+ } catch ( std::exception &e ) {
93
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
94
+ };
95
+ return rbRetVal;
96
+ }
97
+
98
+ VALUE Node::wrapRFFT( VALUE rbSelf, VALUE rbForward )
99
+ {
100
+ VALUE rbRetVal = Qnil;
101
+ try {
102
+ int sign = rbForward == Qtrue ? FFTW_FORWARD : FFTW_BACKWARD;
103
+ VALUE rbMalloc = rb_funcall( rbSelf, rb_intern( "memory" ), 0 );
104
+ VALUE rbTypecode = rb_funcall( rbSelf, rb_intern( "typecode" ), 0 );
105
+ VALUE rbInSize = rb_funcall( rbSelf, rb_intern( "size" ), 0 );
106
+ int inSize = NUM2INT( rbInSize );
107
+ VALUE rbShape = rb_funcall( rbSelf, rb_intern( "shape" ), 0 );
108
+ int rank = RARRAY_LEN( rbShape );
109
+ shared_array< int > n( new int[ rank ] );
110
+ for ( int i=0; i<rank; i++ )
111
+ n[ rank - 1 - i ] = NUM2INT( RARRAY_PTR( rbShape )[i] );
112
+ VALUE rbDest = Qnil;
113
+ VALUE rbRetType = Qnil;
114
+ int size = 0;
115
+ if ( rank > 0 ) {
116
+ if ( sign == FFTW_FORWARD ) {
117
+ ERRORMACRO( n[ rank - 1 ] % 2 == 0, Error, ,
118
+ "First dimension of array must be even for discrete "
119
+ "fourier transform of real data" );
120
+ int half = n[ rank - 1 ] / 2 + 1;
121
+ size = inSize / n[ rank - 1 ] * half;
122
+ RARRAY_PTR(rbShape)[0] = INT2NUM(half);
123
+ } else {
124
+ int twice = ( n[ rank - 1 ] - 1 ) * 2;
125
+ size = inSize / n[ rank - 1 ] * twice;
126
+ RARRAY_PTR(rbShape)[0] = INT2NUM(twice);
127
+ n[ rank - 1 ] = twice;
128
+ };
129
+ };
130
+ if ( ( rb_funcall( rbTypecode, rb_intern( "==" ), 1,
131
+ rb_const_get( mModule,
132
+ rb_intern( "SFLOAT" ) ) ) ==
133
+ Qtrue &&
134
+ sign == FFTW_FORWARD ) ||
135
+ ( rb_funcall( rbTypecode, rb_intern( "==" ), 1,
136
+ rb_const_get( mModule,
137
+ rb_intern( "SCOMPLEX" ) ) ) ==
138
+ Qtrue &&
139
+ sign == FFTW_BACKWARD ) ) {
140
+ assert( sizeof( fftwf_complex ) == sizeof( float ) * 2 );
141
+ fftwf_complex *source;
142
+ Data_Get_Struct( rbMalloc, fftwf_complex, source );
143
+ void *dest;
144
+ fftwf_plan plan;
145
+ shared_array< fftwf_complex > copy;
146
+ int retTypeSize;
147
+ if ( sign == FFTW_FORWARD ) {
148
+ dest = ALLOC_N( fftwf_complex, size );
149
+ retTypeSize = sizeof( fftwf_complex );
150
+ plan = fftwf_plan_dft_r2c( rank, n.get(), (float *)source,
151
+ (fftwf_complex *)dest,
152
+ FFTW_ESTIMATE | FFTW_PRESERVE_INPUT );
153
+ rbRetType = rb_const_get( mModule, rb_intern( "SCOMPLEX" ) );
154
+ } else {
155
+ dest = ALLOC_N( float, size );
156
+ retTypeSize = sizeof( float );
157
+ // FFTW_PRESERVE_INPUT not supported in this case.
158
+ copy = shared_array< fftwf_complex >( new fftwf_complex[ inSize ] );
159
+ memcpy( copy.get(), source, inSize * sizeof( fftwf_complex ) );
160
+ plan = fftwf_plan_dft_c2r( rank, n.get(), (fftwf_complex *)copy.get(),
161
+ (float *)dest, FFTW_ESTIMATE );
162
+ rbRetType = rb_const_get( mModule, rb_intern( "SFLOAT" ) );
163
+ };
164
+ rbDest = Data_Wrap_Struct( rb_const_get( mModule, rb_intern( "Malloc" ) ), 0, xfree,
165
+ (void *)dest );
166
+ rb_ivar_set( rbDest, rb_intern( "@size" ), INT2NUM( size * retTypeSize ) );
167
+ ERRORMACRO( plan != NULL, Error, , "Error creating FFTW plan" );
168
+ fftwf_execute( plan );
169
+ fftwf_destroy_plan( plan );
170
+ } else if ( ( rb_funcall( rbTypecode, rb_intern( "==" ), 1,
171
+ rb_const_get( mModule,
172
+ rb_intern( "DFLOAT" ) ) ) ==
173
+ Qtrue &&
174
+ sign == FFTW_FORWARD ) ||
175
+ ( rb_funcall( rbTypecode, rb_intern( "==" ), 1,
176
+ rb_const_get( mModule,
177
+ rb_intern( "DCOMPLEX" ) ) ) ==
178
+ Qtrue &&
179
+ sign == FFTW_BACKWARD ) ) {
180
+ assert( sizeof( fftw_complex ) == sizeof( double ) * 2 );
181
+ fftw_complex *source;
182
+ Data_Get_Struct( rbMalloc, fftw_complex, source );
183
+ void *dest;
184
+ fftw_plan plan;
185
+ shared_array< fftw_complex > copy;
186
+ int retTypeSize;
187
+ if ( sign == FFTW_FORWARD ) {
188
+ dest = ALLOC_N( fftw_complex, size );
189
+ retTypeSize = sizeof( fftw_complex );
190
+ plan = fftw_plan_dft_r2c( rank, n.get(), (double *)source,
191
+ (fftw_complex *)dest,
192
+ FFTW_ESTIMATE | FFTW_PRESERVE_INPUT );
193
+ rbRetType = rb_const_get( mModule, rb_intern( "DCOMPLEX" ) );
194
+ } else {
195
+ dest = ALLOC_N( double, size );
196
+ retTypeSize = sizeof( double );
197
+ // FFTW_PRESERVE_INPUT not supported in this case.
198
+ copy = shared_array< fftw_complex >( new fftw_complex[ inSize ] );
199
+ memcpy( copy.get(), source, inSize * sizeof( fftw_complex ) );
200
+ plan = fftw_plan_dft_c2r( rank, n.get(), (fftw_complex *)copy.get(),
201
+ (double *)dest, FFTW_ESTIMATE );
202
+ rbRetType = rb_const_get( mModule, rb_intern( "DFLOAT" ) );
203
+ };
204
+ rbDest = Data_Wrap_Struct( rb_const_get( mModule, rb_intern( "Malloc" ) ), 0, xfree,
205
+ (void *)dest );
206
+ rb_ivar_set( rbDest, rb_intern( "@size" ), INT2NUM( size * retTypeSize ) );
207
+ ERRORMACRO( plan != NULL, Error, , "Error creating FFTW plan" );
208
+ fftw_execute( plan );
209
+ fftw_destroy_plan( plan );
210
+ } else {
211
+ ERRORMACRO( false, Error, ,
212
+ "This datatype is not supported for the selected transform" );
213
+ };
214
+ rbRetVal = rb_funcall2( rb_funcall( rb_const_get( mModule, rb_intern( "Sequence" ) ),
215
+ rb_intern( "import" ), 3,
216
+ rbRetType, rbDest, INT2NUM( size ) ),
217
+ rb_intern( "reshape" ), rank, RARRAY_PTR(rbShape) );
218
+ } catch ( std::exception &e ) {
219
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
220
+ };
221
+ return rbRetVal;
222
+ }
223
+