File: log_utilities.rb

package info (click to toggle)
ruby-distribution 0.7.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 572 kB
  • ctags: 370
  • sloc: ruby: 4,270; makefile: 5
file content (80 lines) | stat: -rw-r--r-- 2,726 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# Added by John O. Woods, SciRuby project.
# Derived from GSL-1.9 source files in the specfunc/ dir.

module Distribution
  module MathExtension
    # Various logarithm shortcuts, adapted from GSL-1.9.
    module Log
      C1 = -1.quo(2)
      C2 =  1.quo(3)
      C3 = -1.quo(4)
      C4 =  1.quo(5)
      C5 = -1.quo(6)
      C6 =  1.quo(7)
      C7 = -1.quo(8)
      C8 =  1.quo(9)
      C9 = -1.quo(10)
      class << self

        # gsl_log1p from GSL-1.9 sys/log1p.c
        # log for very small x
        def log1p x
          # in C, this is volatile double y.
          # Not sure how to reproduce that in Ruby.
          y = 1+x
          Math.log(y) - ((y-1)-x).quo(y) # cancel errors with IEEE arithmetic
        end

        # \log(1+x) for x > -1
        # gsl_sf_log_1plusx_e
        def log_1plusx(x, with_error = false)
          raise(ArgumentError, "Range error: x must be > -1") if x <= -1

          if x.abs < Math::ROOT6_FLOAT_EPSILON
            result = x * (1.0 + x * (C1 + x*(C2 + x*(C3 + x*(C4 + x*begin
                       C5 + x*(C6 + x*(C7 + x*(C8 + x*C9))) # formerly t = this
            end)))))
            return with_error ? [result, Float::EPSILON * result.abs] : result
          elsif x.abs < 0.5
            c = ChebyshevSeries.evaluate(:lopx, (8*x + 1).quo(2*x+4), with_error)
            return with_error ? [x * c.first, x * c.last] : x*c
          else
            result = Math.log(1+x)
            return with_error ? [result, Float::EPSILON*result.abs] : result
          end
        end


        # \log(1+x)-x for x > -1
        # gsl_sf_log_1plusx_mx_e
        def log_1plusx_minusx x, with_error = false
          raise(ArgumentError, "Range error: x must be > -1") if x <= -1

          if x.abs < Math::ROOT5_FLOAT_EPSILON
            result = x*x * (C1 + x*(C2 + x*(C3 + x*(C4 + x*begin
                       C5 + x*(C6 + x*(C7 + x*(C8 + x*C9))) # formerly t = this
            end))))
            return with_error ? [result, Float::EPSILON * result.abs] : result
          elsif x.abs < 0.5
            c = ChebyshevSeries.evaluate(:lopxmx, (8*x + 1).quo(2*x+4), with_error)
            return with_error ? [x*x * c.first, x*x * c.last] : x*x*c
          else
            lterm = Math.log(1.0+x)
            error = Float::EPSILON * (lterm.abs + x.abs) if with_error
            result = lterm - x
            return with_error ? [result, error] : result
          end
        end

      protected

        # Abstracted from other log helper functions in GSL-1.9.
        def x_less_than_root_epsilon x, with_error
          result  = square_x ? x*x : x

          with_error ? [result, Float::EPSILON * result.abs] : result
        end
      end
    end
  end
end