begin
  require "rubygems"
rescue LoadError
end

require "numru/fftw3/version"
require "numru/fftw3/fftw3"

if NumRu::FFTW3::SUPPORT_BIGMEM
  if Object.const_defined?(:NArray)
    raise "Incompatibility found. The loaded NumRu::FFTW3 was compiled to " +
          "use NumRu::NArray, but here NArray has already been loaded."
  end
  require("numru/narray");
else
  if ( RUBY_VERSION>="1.9" ? NumRu.const_defined?(:NArray,false) :
       NumRu.const_defined?(:NArray) )
    raise "Incompatibility found. The loaded NumRu::FFTW3 was compiled to " +
          "use NArray, but here NumRu::NArray has already been loaded."
  end
  require("narray");
end

module NumRu

  # Ruby wrapper of FFTW3, a fast discrete Fourier transform library. http://www.fftw.org
  # 
  # ==Features
  # 
  # * Uses NArray (https://github.com/masa16/narray). (Also it supports
  #   NumRu::NArray as well, if this library is compiled to use it).
  # * Multi-dimensional complex and real FFT.
  # * Supports both double and single float transforms.
  # 
  # ==How to use
  # 
  # Copy and paste the following code line-by-line using irb.
  # Or you can run it by saving it in a file fftw3test.rb (say) 
  # and executing "ruby fftw3test.rb".
  # 
  #   require "numru/fftw3"
  #   include NumRu
  # 
  #   na = NArray.float(8,6)   # float -> will be coerced to complex
  #   na[1,1]=1
  # 
  #   # <example 1: complex FFT on all dimensions >
  # 
  #   fc = FFTW3.fft(na, FFTW3::FORWARD)/na.length  # forward 2D FFT and normalization
  #   nc = FFTW3.fft(fc, FFTW3::BACKWARD)       # backward 2D FFT (complex) --> 
  #   nb = nc.real              # should be equal to na except round errors  
  #   p (nb - na).abs.max       # => 8.970743058303247e-17 (sufficiently small)
  # 
  #   # <example 2: complex FFT on all dimensions >
  #   # Same as example 1 but using more user-friendly wrapper of FFTW3.fft
  #   
  #   fc = FFTW3.fft_fw(na)     # forward 2D FFT and normalization
  #   nc = FFTW3.fft_bk(fc)     # backward 2D FFT (complex) --> 
  #   nb = nc.real              # should be equal to na except round errors  
  #   p (nb - na).abs.max       # => 8.970743058303247e-17 (sufficiently small)
  # 
  #   # <example 3: complex FFT along a dimension >
  #   fc = FFTW3.fft_fw(na, 0)  # forward 1D FFT along the first dim
  #   nc = FFTW3.fft_bk(fc, 0)  # backward 1D FFT along the first dim
  #   p (nc.real - na).abs.max  # => 1.1102230246251565e-16 (sufficiently small)
  # 
  #   # <example 4: complex FFT along a dimension >
  #   fc = FFTW3.fft_fw(na, 1)  # forward 1D FFT along the second dim
  # 
  #   # <example 5: real FFT along a dimension >
  # 
  #   fc = FFTW3.fft_r2r(na, FFTW3::RODFT00, 0)  # not normalized sine transform along the 1st dim
  #   len = 2*(na.shape[0]+1)    # this is the supposed length of this transformation
  #   nc = FFTW3.fft_r2r(fc/len, FFTW3::RODFT00, 0)  # forward==backward transformation
  #   p (nc-na).abs.max          # => 2.220446049250313e-16 (sufficiently small)
  # 
  #   # <example 5b: real FFT on all dimensions >
  # 
  #   fc = FFTW3.fft_r2r(na, FFTW3::REDFT11)   # unnormalized cosine transform
  #   len = 4*na.length          # from (2*na.shape[0]) * (2*na.shape[1])
  #   nc = FFTW3.fft_r2r(fc/len, FFTW3::REDFT11)   # forward==backward transformation
  #   p (nc-na).abs.max          # => 6.228190483314256e-17 (sufficiently small)
  #   
  # See the FFTW3 manual for the kinds of supported real FFTs. See Ch.2 of
  # http://www.fftw.org/fftw3_doc/ (http://www.fftw.org/fftw3.pdf).
  # Virtually all kinds are supported!
  # 
  module FFTW3

    module_function

    # Forward complex FFT with normalization
    # 
    # This calls FFW3.fft(na, FFW3::FORWARD, *dims) and normalizes the result
    # by dividing by the length
    # 
    def fft_fw(na, *dims)
      fc = fft(na, FORWARD, *dims)
      if dims.length == 0
        len = na.total
      else
        len = 1
        shape = na.shape
        dims.each{|d| len *= shape[d]}
      end
      fc / len
    end

    # Backward complex FFT
    # 
    # This method simply calls FFW3.fft(na, FFW3::BACKWARD, *dims)
    # 
    def fft_bk(na, *dims)
      fft(na, BACKWARD, *dims)
    end
  end
end
