math_lgamma.odin 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. package math
  2. // The original C code and the long comment below are
  3. // from FreeBSD's /usr/src/lib/msun/src/e_lgamma_r.c and
  4. // came with this notice.
  5. //
  6. // ====================================================
  7. // Copyright (C) 1993 by Sun Microsystems, Inc. All rights reserved.
  8. //
  9. // Developed at SunPro, a Sun Microsystems, Inc. business.
  10. // Permission to use, copy, modify, and distribute this
  11. // software is freely granted, provided that this notice
  12. // is preserved.
  13. // ====================================================
  14. //
  15. // __ieee754_lgamma_r(x, signgamp)
  16. // Reentrant version of the logarithm of the Gamma function
  17. // with user provided pointer for the sign of Gamma(x).
  18. //
  19. // Method:
  20. // 1. Argument Reduction for 0 < x <= 8
  21. // Since gamma(1+s)=s*gamma(s), for x in [0,8], we may
  22. // reduce x to a number in [1.5,2.5] by
  23. // lgamma(1+s) = log(s) + lgamma(s)
  24. // for example,
  25. // lgamma(7.3) = log(6.3) + lgamma(6.3)
  26. // = log(6.3*5.3) + lgamma(5.3)
  27. // = log(6.3*5.3*4.3*3.3*2.3) + lgamma(2.3)
  28. // 2. Polynomial approximation of lgamma around its
  29. // minimum (ymin=1.461632144968362245) to maintain monotonicity.
  30. // On [ymin-0.23, ymin+0.27] (i.e., [1.23164,1.73163]), use
  31. // Let z = x-ymin;
  32. // lgamma(x) = -1.214862905358496078218 + z**2*poly(z)
  33. // poly(z) is a 14 degree polynomial.
  34. // 2. Rational approximation in the primary interval [2,3]
  35. // We use the following approximation:
  36. // s = x-2.0;
  37. // lgamma(x) = 0.5*s + s*P(s)/Q(s)
  38. // with accuracy
  39. // |P/Q - (lgamma(x)-0.5s)| < 2**-61.71
  40. // Our algorithms are based on the following observation
  41. //
  42. // zeta(2)-1 2 zeta(3)-1 3
  43. // lgamma(2+s) = s*(1-Euler) + --------- * s - --------- * s + ...
  44. // 2 3
  45. //
  46. // where Euler = 0.5772156649... is the Euler constant, which
  47. // is very close to 0.5.
  48. //
  49. // 3. For x>=8, we have
  50. // lgamma(x)~(x-0.5)log(x)-x+0.5*log(2pi)+1/(12x)-1/(360x**3)+....
  51. // (better formula:
  52. // lgamma(x)~(x-0.5)*(log(x)-1)-.5*(log(2pi)-1) + ...)
  53. // Let z = 1/x, then we approximation
  54. // f(z) = lgamma(x) - (x-0.5)(log(x)-1)
  55. // by
  56. // 3 5 11
  57. // w = w0 + w1*z + w2*z + w3*z + ... + w6*z
  58. // where
  59. // |w - f(z)| < 2**-58.74
  60. //
  61. // 4. For negative x, since (G is gamma function)
  62. // -x*G(-x)*G(x) = pi/sin(pi*x),
  63. // we have
  64. // G(x) = pi/(sin(pi*x)*(-x)*G(-x))
  65. // since G(-x) is positive, sign(G(x)) = sign(sin(pi*x)) for x<0
  66. // Hence, for x<0, signgam = sign(sin(pi*x)) and
  67. // lgamma(x) = log(|Gamma(x)|)
  68. // = log(pi/(|x*sin(pi*x)|)) - lgamma(-x);
  69. // Note: one should avoid computing pi*(-x) directly in the
  70. // computation of sin(pi*(-x)).
  71. //
  72. // 5. Special Cases
  73. // lgamma(2+s) ~ s*(1-Euler) for tiny s
  74. // lgamma(1)=lgamma(2)=0
  75. // lgamma(x) ~ -log(x) for tiny x
  76. // lgamma(0) = lgamma(inf) = inf
  77. // lgamma(-integer) = +-inf
  78. //
  79. //
  80. lgamma_f64 :: proc "contextless" (x: f64) -> (lgamma: f64, sign: int) {
  81. sin_pi :: proc "contextless" (x: f64) -> f64 {
  82. if x < 0.25 {
  83. return -sin(PI * x)
  84. }
  85. x := x
  86. // argument reduction
  87. z := floor(x)
  88. n: int
  89. if z != x { // inexact
  90. x = mod(x, 2)
  91. n = int(x * 4)
  92. } else {
  93. if x >= TWO_53 { // x must be even
  94. x = 0
  95. n = 0
  96. } else {
  97. if x < TWO_52 {
  98. z = x + TWO_52 // exact
  99. }
  100. n = int(1 & transmute(u64)z)
  101. x = f64(n)
  102. n <<= 2
  103. }
  104. }
  105. switch n {
  106. case 0:
  107. x = sin(PI * x)
  108. case 1, 2:
  109. x = cos(PI * (0.5 - x))
  110. case 3, 4:
  111. x = sin(PI * (1 - x))
  112. case 5, 6:
  113. x = -cos(PI * (x - 1.5))
  114. case:
  115. x = sin(PI * (x - 2))
  116. }
  117. return -x
  118. }
  119. @static lgamA := [?]f64{
  120. 0h3FB3C467E37DB0C8,
  121. 0h3FD4A34CC4A60FAD,
  122. 0h3FB13E001A5562A7,
  123. 0h3F951322AC92547B,
  124. 0h3F7E404FB68FEFE8,
  125. 0h3F67ADD8CCB7926B,
  126. 0h3F538A94116F3F5D,
  127. 0h3F40B6C689B99C00,
  128. 0h3F2CF2ECED10E54D,
  129. 0h3F1C5088987DFB07,
  130. 0h3EFA7074428CFA52,
  131. 0h3F07858E90A45837,
  132. }
  133. @static lgamR := [?]f64{
  134. 1.0,
  135. 0h3FF645A762C4AB74,
  136. 0h3FE71A1893D3DCDC,
  137. 0h3FC601EDCCFBDF27,
  138. 0h3F9317EA742ED475,
  139. 0h3F497DDACA41A95B,
  140. 0h3EDEBAF7A5B38140,
  141. }
  142. @static lgamS := [?]f64{
  143. 0hBFB3C467E37DB0C8,
  144. 0h3FCB848B36E20878,
  145. 0h3FD4D98F4F139F59,
  146. 0h3FC2BB9CBEE5F2F7,
  147. 0h3F9B481C7E939961,
  148. 0h3F5E26B67368F239,
  149. 0h3F00BFECDD17E945,
  150. }
  151. @static lgamT := [?]f64{
  152. 0h3FDEF72BC8EE38A2,
  153. 0hBFC2E4278DC6C509,
  154. 0h3FB08B4294D5419B,
  155. 0hBFA0C9A8DF35B713,
  156. 0h3F9266E7970AF9EC,
  157. 0hBF851F9FBA91EC6A,
  158. 0h3F78FCE0E370E344,
  159. 0hBF6E2EFFB3E914D7,
  160. 0h3F6282D32E15C915,
  161. 0hBF56FE8EBF2D1AF1,
  162. 0h3F4CDF0CEF61A8E9,
  163. 0hBF41A6109C73E0EC,
  164. 0h3F34AF6D6C0EBBF7,
  165. 0hBF347F24ECC38C38,
  166. 0h3F35FD3EE8C2D3F4,
  167. }
  168. @static lgamU := [?]f64{
  169. 0hBFB3C467E37DB0C8,
  170. 0h3FE4401E8B005DFF,
  171. 0h3FF7475CD119BD6F,
  172. 0h3FEF497644EA8450,
  173. 0h3FCD4EAEF6010924,
  174. 0h3F8B678BBF2BAB09,
  175. }
  176. @static lgamV := [?]f64{
  177. 1.0,
  178. 0h4003A5D7C2BD619C,
  179. 0h40010725A42B18F5,
  180. 0h3FE89DFBE45050AF,
  181. 0h3FBAAE55D6537C88,
  182. 0h3F6A5ABB57D0CF61,
  183. }
  184. @static lgamW := [?]f64{
  185. 0h3FDACFE390C97D69,
  186. 0h3FB555555555553B,
  187. 0hBF66C16C16B02E5C,
  188. 0h3F4A019F98CF38B6,
  189. 0hBF4380CB8C0FE741,
  190. 0h3F4B67BA4CDAD5D1,
  191. 0hBF5AB89D0B9E43E4,
  192. }
  193. Y_MIN :: 0h3ff762d86356be3f // 1.461632144968362245
  194. TWO_52 :: 0h4330000000000000 // ~4.5036e+15
  195. TWO_53 :: 0h4340000000000000 // ~9.0072e+15
  196. TWO_58 :: 0h4390000000000000 // ~2.8823e+17
  197. TINY :: 0h3b90000000000000 // ~8.47033e-22
  198. Tc :: 0h3FF762D86356BE3F
  199. Tf :: 0hBFBF19B9BCC38A42
  200. Tt :: 0hBC50C7CAA48A971F
  201. // special cases
  202. sign = 1
  203. switch {
  204. case is_nan(x):
  205. lgamma = x
  206. return
  207. case is_inf(x):
  208. lgamma = x
  209. return
  210. case x == 0:
  211. lgamma = inf_f64(1)
  212. return
  213. }
  214. x := x
  215. neg := false
  216. if x < 0 {
  217. x = -x
  218. neg = true
  219. }
  220. if x < TINY { // if |x| < 2**-70, return -log(|x|)
  221. if neg {
  222. sign = -1
  223. }
  224. lgamma = -ln(x)
  225. return
  226. }
  227. nadj: f64
  228. if neg {
  229. if x >= TWO_52 { // |x| >= 2**52, must be -integer
  230. lgamma = inf_f64(1)
  231. return
  232. }
  233. t := sin_pi(x)
  234. if t == 0 {
  235. lgamma = inf_f64(1) // -integer
  236. return
  237. }
  238. nadj = ln(PI / abs(t*x))
  239. if t < 0 {
  240. sign = -1
  241. }
  242. }
  243. switch {
  244. case x == 1 || x == 2: // purge off 1 and 2
  245. lgamma = 0
  246. return
  247. case x < 2: // use lgamma(x) = lgamma(x+1) - log(x)
  248. y: f64
  249. i: int
  250. if x <= 0.9 {
  251. lgamma = -ln(x)
  252. switch {
  253. case x >= (Y_MIN - 1 + 0.27): // 0.7316 <= x <= 0.9
  254. y = 1 - x
  255. i = 0
  256. case x >= (Y_MIN - 1 - 0.27): // 0.2316 <= x < 0.7316
  257. y = x - (Tc - 1)
  258. i = 1
  259. case: // 0 < x < 0.2316
  260. y = x
  261. i = 2
  262. }
  263. } else {
  264. lgamma = 0
  265. switch {
  266. case x >= (Y_MIN + 0.27): // 1.7316 <= x < 2
  267. y = 2 - x
  268. i = 0
  269. case x >= (Y_MIN - 0.27): // 1.2316 <= x < 1.7316
  270. y = x - Tc
  271. i = 1
  272. case: // 0.9 < x < 1.2316
  273. y = x - 1
  274. i = 2
  275. }
  276. }
  277. switch i {
  278. case 0:
  279. z := y * y
  280. p1 := lgamA[0] + z*(lgamA[2]+z*(lgamA[4]+z*(lgamA[6]+z*(lgamA[8]+z*lgamA[10]))))
  281. p2 := z * (lgamA[1] + z*(+lgamA[3]+z*(lgamA[5]+z*(lgamA[7]+z*(lgamA[9]+z*lgamA[11])))))
  282. p := y*p1 + p2
  283. lgamma += (p - 0.5*y)
  284. case 1:
  285. z := y * y
  286. w := z * y
  287. p1 := lgamT[0] + w*(lgamT[3]+w*(lgamT[6]+w*(lgamT[9]+w*lgamT[12]))) // parallel comp
  288. p2 := lgamT[1] + w*(lgamT[4]+w*(lgamT[7]+w*(lgamT[10]+w*lgamT[13])))
  289. p3 := lgamT[2] + w*(lgamT[5]+w*(lgamT[8]+w*(lgamT[11]+w*lgamT[14])))
  290. p := z*p1 - (Tt - w*(p2+y*p3))
  291. lgamma += (Tf + p)
  292. case 2:
  293. p1 := y * (lgamU[0] + y*(lgamU[1]+y*(lgamU[2]+y*(lgamU[3]+y*(lgamU[4]+y*lgamU[5])))))
  294. p2 := 1 + y*(lgamV[1]+y*(lgamV[2]+y*(lgamV[3]+y*(lgamV[4]+y*lgamV[5]))))
  295. lgamma += (-0.5*y + p1/p2)
  296. }
  297. case x < 8: // 2 <= x < 8
  298. i := int(x)
  299. y := x - f64(i)
  300. p := y * (lgamS[0] + y*(lgamS[1]+y*(lgamS[2]+y*(lgamS[3]+y*(lgamS[4]+y*(lgamS[5]+y*lgamS[6]))))))
  301. q := 1 + y*(lgamR[1]+y*(lgamR[2]+y*(lgamR[3]+y*(lgamR[4]+y*(lgamR[5]+y*lgamR[6])))))
  302. lgamma = 0.5*y + p/q
  303. z := 1.0 // lgamma(1+s) = ln(s) + lgamma(s)
  304. switch i {
  305. case 7:
  306. z *= (y + 6)
  307. fallthrough
  308. case 6:
  309. z *= (y + 5)
  310. fallthrough
  311. case 5:
  312. z *= (y + 4)
  313. fallthrough
  314. case 4:
  315. z *= (y + 3)
  316. fallthrough
  317. case 3:
  318. z *= (y + 2)
  319. lgamma += ln(z)
  320. }
  321. case x < TWO_58: // 8 <= x < 2**58
  322. t := ln(x)
  323. z := 1 / x
  324. y := z * z
  325. w := lgamW[0] + z*(lgamW[1]+y*(lgamW[2]+y*(lgamW[3]+y*(lgamW[4]+y*(lgamW[5]+y*lgamW[6])))))
  326. lgamma = (x-0.5)*(t-1) + w
  327. case: // 2**58 <= x <= Inf
  328. lgamma = x * (ln(x) - 1)
  329. }
  330. if neg {
  331. lgamma = nadj - lgamma
  332. }
  333. return
  334. }
  335. lgamma_f16 :: proc "contextless" (x: f16) -> (lgamma: f16, sign: int) { r, s := lgamma_f64(f64(x)); return f16(r), s }
  336. lgamma_f32 :: proc "contextless" (x: f32) -> (lgamma: f32, sign: int) { r, s := lgamma_f64(f64(x)); return f32(r), s }
  337. lgamma_f16le :: proc "contextless" (x: f16le) -> (lgamma: f16le, sign: int) { r, s := lgamma_f64(f64(x)); return f16le(r), s }
  338. lgamma_f16be :: proc "contextless" (x: f16be) -> (lgamma: f16be, sign: int) { r, s := lgamma_f64(f64(x)); return f16be(r), s }
  339. lgamma_f32le :: proc "contextless" (x: f32le) -> (lgamma: f32le, sign: int) { r, s := lgamma_f64(f64(x)); return f32le(r), s }
  340. lgamma_f32be :: proc "contextless" (x: f32be) -> (lgamma: f32be, sign: int) { r, s := lgamma_f64(f64(x)); return f32be(r), s }
  341. lgamma_f64le :: proc "contextless" (x: f64le) -> (lgamma: f64le, sign: int) { r, s := lgamma_f64(f64(x)); return f64le(r), s }
  342. lgamma_f64be :: proc "contextless" (x: f64be) -> (lgamma: f64be, sign: int) { r, s := lgamma_f64(f64(x)); return f64be(r), s }
  343. lgamma :: proc{
  344. lgamma_f16, lgamma_f16le, lgamma_f16be,
  345. lgamma_f32, lgamma_f32le, lgamma_f32be,
  346. lgamma_f64, lgamma_f64le, lgamma_f64be,
  347. }