Module: Rjq::MathFunctions
- Defined in:
- lib/rjq/math_functions.rb
Constant Summary collapse
- CANDIDATE_LIBRARIES =
[ '/usr/lib/libSystem.B.dylib', 'libm.so.6', 'libm.so', 'libm.dylib' ].freeze
- NATIVE_SIGNATURES =
{ fma: 'double fma(double, double, double)', remainder: 'double remainder(double, double)', scalb: 'double scalb(double, double)', scalbln: 'double scalbln(double, long)' }.freeze
Class Method Summary collapse
- .bessel(name, *values) ⇒ Object
- .bessel_available? ⇒ Boolean
- .bessel_library ⇒ Object
- .build_bessel_library(library) ⇒ Object
- .build_native_library(library, signature) ⇒ Object
- .c_long_exponent(value) ⇒ Object
- .fma(left, right, addend) ⇒ Object
- .load_bessel_library ⇒ Object
- .native_available?(name) ⇒ Boolean
- .native_library(name) ⇒ Object
- .portable_scale(value, exponent) ⇒ Object
- .remainder(left, right) ⇒ Object
- .scalb(value, exponent) ⇒ Object
- .scalbln(value, exponent) ⇒ Object
- .signed_max(value) ⇒ Object
Class Method Details
.bessel(name, *values) ⇒ Object
22 23 24 25 26 27 28 |
# File 'lib/rjq/math_functions.rb', line 22 def bessel(name, *values) if %w[jn yn].include?(name) bessel_library.public_send(name, values.fetch(0).to_i, values.fetch(1).to_f) else bessel_library.public_send(name, values.fetch(0).to_f) end end |
.bessel_available? ⇒ Boolean
136 137 138 139 140 141 |
# File 'lib/rjq/math_functions.rb', line 136 def bessel_available? bessel_library true rescue Rjq::RuntimeError false end |
.bessel_library ⇒ Object
132 133 134 |
# File 'lib/rjq/math_functions.rb', line 132 def bessel_library @bessel_library ||= load_bessel_library end |
.build_bessel_library(library) ⇒ Object
154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/rjq/math_functions.rb', line 154 def build_bessel_library(library) Module.new do extend Fiddle::Importer dlload library extern 'double j0(double)' extern 'double j1(double)' extern 'double y0(double)' extern 'double y1(double)' extern 'double jn(int, double)' extern 'double yn(int, double)' end end |
.build_native_library(library, signature) ⇒ Object
96 97 98 99 100 101 102 103 |
# File 'lib/rjq/math_functions.rb', line 96 def build_native_library(library, signature) Module.new do extend Fiddle::Importer dlload library extern signature end end |
.c_long_exponent(value) ⇒ Object
105 106 107 108 109 110 111 112 113 |
# File 'lib/rjq/math_functions.rb', line 105 def c_long_exponent(value) bits = Fiddle::SIZEOF_LONG * 8 minimum = -(1 << (bits - 1)) maximum = (1 << (bits - 1)) - 1 return 0 if value.nan? return value.positive? ? maximum : minimum if value.infinite? [[value.to_i, minimum].max, maximum].min end |
.fma(left, right, addend) ⇒ Object
30 31 32 33 34 35 |
# File 'lib/rjq/math_functions.rb', line 30 def fma(left, right, addend) library = native_library(:fma) return library.fma(left.to_f, right.to_f, addend.to_f) if library raise Rjq::RuntimeError, 'native fused multiply-add is not available on this platform' end |
.load_bessel_library ⇒ Object
143 144 145 146 147 148 149 150 151 152 |
# File 'lib/rjq/math_functions.rb', line 143 def load_bessel_library errors = [] CANDIDATE_LIBRARIES.each do |library| return build_bessel_library(library) rescue Fiddle::DLError => e errors << "#{library}: #{e.}" end raise Rjq::RuntimeError, "C math library with Bessel functions is not available (#{errors.join('; ')})" end |
.native_available?(name) ⇒ Boolean
80 81 82 |
# File 'lib/rjq/math_functions.rb', line 80 def native_available?(name) !native_library(name).nil? end |
.native_library(name) ⇒ Object
84 85 86 87 88 89 90 91 92 93 94 |
# File 'lib/rjq/math_functions.rb', line 84 def native_library(name) @native_libraries ||= {} return @native_libraries[name] if @native_libraries.key?(name) signature = NATIVE_SIGNATURES.fetch(name) @native_libraries[name] = CANDIDATE_LIBRARIES.lazy.filter_map do |library| build_native_library(library, signature) rescue Fiddle::DLError nil end.first end |
.portable_scale(value, exponent) ⇒ Object
115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/rjq/math_functions.rb', line 115 def portable_scale(value, exponent) return value if value.zero? return signed_max(value) if value.infinite? _, value_exponent = Math.frexp(value.abs) target_exponent = value_exponent + exponent return signed_max(value) if target_exponent > 1024 return value.negative? ? -0.0 : 0.0 if target_exponent < -1074 result = Math.ldexp(value, exponent) result.infinite? ? signed_max(value) : result end |
.remainder(left, right) ⇒ Object
37 38 39 40 41 42 |
# File 'lib/rjq/math_functions.rb', line 37 def remainder(left, right) library = native_library(:remainder) return library.remainder(left.to_f, right.to_f) if library raise Rjq::RuntimeError, 'native IEEE remainder is not available on this platform' end |
.scalb(value, exponent) ⇒ Object
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/rjq/math_functions.rb', line 44 def scalb(value, exponent) library = native_library(:scalb) if library result = library.scalb(value.to_f, exponent.to_f) return signed_max(value.to_f) if result.infinite? return result end exponent = exponent.to_f return Float::NAN if exponent.nan? return portable_scale(value.to_f, exponent.positive? ? 10_000 : -10_000) if exponent.infinite? portable_scale(value.to_f, exponent.to_i) end |
.scalbln(value, exponent) ⇒ Object
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/rjq/math_functions.rb', line 60 def scalbln(value, exponent) exponent = exponent.to_f if RUBY_PLATFORM.include?('linux') && !exponent.finite? # jq's Linux build passes non-finite values through a C long # conversion, which yields zero on the supported libc targets. return 0.0 end integral_exponent = c_long_exponent(exponent) library = native_library(:scalbln) if library result = library.scalbln(value.to_f, integral_exponent) return signed_max(value.to_f) if result.infinite? return result end portable_scale(value.to_f, integral_exponent) end |
.signed_max(value) ⇒ Object
128 129 130 |
# File 'lib/rjq/math_functions.rb', line 128 def signed_max(value) value.negative? ? -Float::MAX : Float::MAX end |