plum

#treesitter#compiler#wasm

git clone https://git.pyrossh.dev/plum

A statically typed, imperative programming language inspired by rust, python


plum-std/Number.plum
ca5fd6f 1
module std
ca5fd6f 2
import std/Str
ca5fd6f 3
import std/Bool
ca5fd6f 4
import std/Result
ca5fd6f 5
ca5fd6f 6
MIN_VALUE = -0x8000_0000_0000_0000 # Lowest value of Int
ca5fd6f 7
MAX_VALUE = 0x7FFF_FFFF_FFFF_FFFF  # Highest value of Int
ca5fd6f 8
LARGE = 1 << 28 # 2**28
ca5fd6f 9
ca5fd6f 10
E = 2.718281828459045f # Euler's number, the base of natural logarithms, e, https://oeis.org/A001113
ca5fd6f 11
LN10 = 2.302585092994046f # The natural logarithm of 10, https://oeis.org/A002392
ca5fd6f 12
LN2 = 0.6931471805599453f # The natural logarithm of 2, https://oeis.org/A002162
ca5fd6f 13
LOG10E = 0.4342944819032518f # The base 10 logarithm of e, formula: 1 / LN10
ca5fd6f 14
LOG2E = 1.4426950408889634f # The base 2 logarithm of e, formula: 1 / LN2
ca5fd6f 15
PI = 3.141592653589793f # The ratio of the circumference of a circle to its diameter, https://oeis.org/A000796
ca5fd6f 16
PHI = 1.618033988749895f # https://oeis.org/A001622
ca5fd6f 17
SQRT1_2 = 0.7071067811865476f # The square root of 1/2
ca5fd6f 18
SQRT2 = 1.4142135623730951f # The square root of 2, https://oeis.org/A002193
ca5fd6f 19
SQRT_E = 1.6487212707001282f # https://oeis.org/A019774
ca5fd6f 20
SQRT_PI = 1.7724538509055159f # https://oeis.org/A002161
ca5fd6f 21
SQRT_PHI = 1.272019649514069f # https://oeis.org/A139339
ca5fd6f 22
EPSILON = 2.220446049250313e-16f # The difference between 1 and the smallest floating point number greater than 1, formula: 7/3 - 4/3 - 1
ca5fd6f 23
MIN_FLOAT_VALUE = 4.9406564584124654417656879286822137236505980e-324 # Lowest value of float
ca5fd6f 24
MAX_FLOAT_VALUE = 1.79769313486231570814527423731704356798070e+308 # Highest value of float
ca5fd6f 25
HALF_PI = 1.5707963267948966f # PI / 2
ca5fd6f 26
TAU = 6.283185307179586f # 2 * PI
ca5fd6f 27
ca5fd6f 28
# `Int`/`Float` bare-wrap into `Number` with no wrapper syntax at all (see
ca5fd6f 29
# `plum-checker`'s `unify`/`monomorphize::wrapPrimitiveAgainstExpected`) —
ca5fd6f 30
# assigning/returning/passing a bare `Int`/`Float` value wherever `Number` is
ca5fd6f 31
# expected just works. A method not found on `Int`/`Float` directly (they no
ca5fd6f 32
# longer have their own method tables at all) falls back to `Number`'s,
ca5fd6f 33
# boxing `self` first (see `plum-checker`/`plum-wasm-codegen`'s matching
ca5fd6f 34
# `AttrKind::Method` dispatch fallback) — so `x.abs()` for a bare `Int`/
ca5fd6f 35
# `Float` `x` dispatches here exactly as if `Int`/`Float` still had their own
ca5fd6f 36
# `abs` method.
ca5fd6f 37
enum Number =
ca5fd6f 38
  | Int
ca5fd6f 39
  | Float
ca5fd6f 40
ca5fd6f 41
  fun kind(self) -> Str =
ca5fd6f 42
    match self
ca5fd6f 43
      Int(_) => "Int"
ca5fd6f 44
      Float(_) => "Float"
ca5fd6f 45
ca5fd6f 46
  fun toFloatValue(self) -> Float =
ca5fd6f 47
    match self
ca5fd6f 48
      Int(i) => Float(i)
ca5fd6f 49
      Float(f) => f
ca5fd6f 50
ca5fd6f 51
  fun abs(self) -> Number =
ca5fd6f 52
    match self
ca5fd6f 53
      Int(i) => Int(i < 0 ? -i : i)
ca5fd6f 54
      Float(f) => Float(f < 0.0f ? -f : f)
ca5fd6f 55
ca5fd6f 56
  # Preserves 0.0 / -0.0 / NaN for the `Float` case, matching Go's
ca5fd6f 57
  # `math.Signbit`-adjacent `Copysign`/`sign` conventions loosely.
ca5fd6f 58
  fun sign(self) -> Number =
ca5fd6f 59
    match self
ca5fd6f 60
      Int(i) => Int(i > 0 ? 1 : i < 0 ? -1 : 0)
ca5fd6f 61
      Float(f) =>
ca5fd6f 62
        if f > 0.0f
ca5fd6f 63
          return Float(1.0f)
ca5fd6f 64
        if f < 0.0f
ca5fd6f 65
          return Float(-1.0f)
ca5fd6f 66
        return Float(f)
ca5fd6f 67
ca5fd6f 68
  # An `Int` already IS its own well-distributed bit pattern — used by
ca5fd6f 69
  # `Map[K: Hashable, V]` to bucket `Int` keys. A `Float` hashes via a
ca5fd6f 70
  # truncating conversion (no bit-level float hashing attempted).
ca5fd6f 71
  fun hash(self) -> Int =
ca5fd6f 72
    match self
ca5fd6f 73
      Int(i) => i
ca5fd6f 74
      Float(f) => Int(f)
ca5fd6f 75
ca5fd6f 76
  # An Int is always already whole, so trunc/floor/ceil/round are all just
ca5fd6f 77
  # the value itself widened to Float.
ca5fd6f 78
  fun trunc(self) -> Float =
ca5fd6f 79
    match self
ca5fd6f 80
      Int(i) => Float(i)
ca5fd6f 81
      # `Int(f)` truncates toward zero (see `plum-wasm-codegen`'s `Int(x)`
ca5fd6f 82
      # cross-type conversion) — exactly `trunc`'s definition.
ca5fd6f 83
      Float(f) => Float(Int(f))
ca5fd6f 84
ca5fd6f 85
  fun floor(self) -> Float =
ca5fd6f 86
    match self
ca5fd6f 87
      Int(i) => Float(i)
ca5fd6f 88
      Float(f) =>
ca5fd6f 89
        t := Float(Int(f))
ca5fd6f 90
        if f < 0.0f && t != f
ca5fd6f 91
          return t - 1.0f
ca5fd6f 92
        return t
ca5fd6f 93
ca5fd6f 94
  fun ceil(self) -> Float =
ca5fd6f 95
    match self
ca5fd6f 96
      Int(i) => Float(i)
ca5fd6f 97
      Float(f) =>
ca5fd6f 98
        t := Float(Int(f))
ca5fd6f 99
        if f > 0.0f && t != f
ca5fd6f 100
          return t + 1.0f
ca5fd6f 101
        return t
ca5fd6f 102
ca5fd6f 103
  # Round-half-away-from-zero (matching Go's `math.Round`), not
ca5fd6f 104
  # round-half-to-even.
ca5fd6f 105
  fun round(self) -> Float =
ca5fd6f 106
    match self
ca5fd6f 107
      Int(i) => Float(i)
ca5fd6f 108
      Float(f) =>
ca5fd6f 109
        neg := f < 0.0f
ca5fd6f 110
        v := neg ? -f : f
ca5fd6f 111
        r := {v + 0.5f}.floor()
ca5fd6f 112
        return neg ? -r : r
ca5fd6f 113
ca5fd6f 114
  fun log(self) -> Float =
ca5fd6f 115
    ln(self.toFloatValue())
ca5fd6f 116
ca5fd6f 117
  fun log2(self) -> Float =
ca5fd6f 118
    ln(self.toFloatValue()) * LOG2E
ca5fd6f 119
ca5fd6f 120
  fun log10(self) -> Float =
ca5fd6f 121
    ln(self.toFloatValue()) * LOG10E
ca5fd6f 122
ca5fd6f 123
  # floor(log2(|self|)). An `Int` self computes this exactly with integer
ca5fd6f 124
  # division rather than through Float rounding error; a `Float` self falls
ca5fd6f 125
  # back to the real logarithm.
ca5fd6f 126
  fun logb(self) -> Float =
ca5fd6f 127
    match self
ca5fd6f 128
      Int(i) =>
ca5fd6f 129
        if i == 0
ca5fd6f 130
          return -1.0f / 0.0f
ca5fd6f 131
        n := i < 0 ? -i : i
ca5fd6f 132
        k := 0
ca5fd6f 133
        while n >= 2
ca5fd6f 134
          n = n / 2
ca5fd6f 135
          k = k + 1
ca5fd6f 136
        return Float(k)
ca5fd6f 137
      Float(f) =>
ca5fd6f 138
        if f == 0.0f
ca5fd6f 139
          return -1.0f / 0.0f
ca5fd6f 140
        return {ln(f < 0.0f ? -f : f) * LOG2E}.floor()
ca5fd6f 141
ca5fd6f 142
  fun sqrt(self) -> Float =
ca5fd6f 143
    sqrt(self.toFloatValue())
ca5fd6f 144
ca5fd6f 145
  # self^y. Mirrors the original `Int.pow`/`Float.pow`: a whole-number `y`
ca5fd6f 146
  # (including negative) is computed exactly by repeated squaring; a
ca5fd6f 147
  # fractional `y` falls back to self^y = exp(y * ln(self)), which requires
ca5fd6f 148
  # self > 0.
ca5fd6f 149
  fun pow(self, y: Float) -> Float =
ca5fd6f 150
    powFloat(self.toFloatValue(), y)
ca5fd6f 151
ca5fd6f 152
  # Check whether this number is finite, ie not +/-infinity and not NaN.
ca5fd6f 153
  fun isFinite(self) -> Bool =
ca5fd6f 154
    !self.isNaN() && !self.isInfinite()
ca5fd6f 155
ca5fd6f 156
  fun isInfinite(self) -> Bool =
ca5fd6f 157
    match self
ca5fd6f 158
      Int(_) => False
ca5fd6f 159
      Float(f) => f > MAX_FLOAT_VALUE || f < -MAX_FLOAT_VALUE
ca5fd6f 160
ca5fd6f 161
  fun isNaN(self) -> Bool =
ca5fd6f 162
    match self
ca5fd6f 163
      Int(_) => False
ca5fd6f 164
      Float(f) => f != f
ca5fd6f 165
ca5fd6f 166
  fun min(self, other: Number) -> Number =
ca5fd6f 167
    self.toFloatValue() < other.toFloatValue() ? self : other
ca5fd6f 168
ca5fd6f 169
  fun max(self, other: Number) -> Number =
ca5fd6f 170
    self.toFloatValue() > other.toFloatValue() ? self : other
ca5fd6f 171
ca5fd6f 172
  # Inverse hyperbolic cosine, via acosh(x) = ln(x + sqrt(x^2 - 1)), x >= 1.
ca5fd6f 173
  fun acosh(self) -> Float =
ca5fd6f 174
    v := self.toFloatValue()
ca5fd6f 175
    if v < 1.0f
ca5fd6f 176
      return 0.0f / 0.0f
ca5fd6f 177
    return ln(v + sqrt(v * v - 1.0f))
ca5fd6f 178
ca5fd6f 179
  fun sinh(self) -> Float =
ca5fd6f 180
    v := self.toFloatValue()
ca5fd6f 181
    return {exp(v) - exp(-v)} / 2.0f
ca5fd6f 182
ca5fd6f 183
  fun cosh(self) -> Float =
ca5fd6f 184
    v := self.toFloatValue()
ca5fd6f 185
    return {exp(v) + exp(-v)} / 2.0f
ca5fd6f 186
ca5fd6f 187
  fun tanh(self) -> Float =
ca5fd6f 188
    return self.sinh() / self.cosh()
ca5fd6f 189
ca5fd6f 190
  # Inverse hyperbolic sine: asinh(x) = ln(x + sqrt(x^2 + 1)).
ca5fd6f 191
  fun asinh(self) -> Float =
ca5fd6f 192
    v := self.toFloatValue()
ca5fd6f 193
    return ln(v + sqrt(v * v + 1.0f))
ca5fd6f 194
ca5fd6f 195
  # Inverse hyperbolic tangent: atanh(x) = 0.5*ln((1+x)/(1-x)), |x| < 1.
ca5fd6f 196
  fun atanh(self) -> Float =
ca5fd6f 197
    v := self.toFloatValue()
ca5fd6f 198
    if v <= -1.0f || v >= 1.0f
ca5fd6f 199
      return 0.0f / 0.0f
ca5fd6f 200
    return 0.5f * ln({1.0f + v} / {1.0f - v})
ca5fd6f 201
ca5fd6f 202
  # Rounded to 6 fractional digits, with trailing zeros trimmed (but always
ca5fd6f 203
  # at least one digit after the point, e.g. `3.0` not `3.`), for a `Float`;
ca5fd6f 204
  # plain decimal digits for an `Int`.
ca5fd6f 205
  fun toStr(self) -> Str =
ca5fd6f 206
    match self
ca5fd6f 207
      Int(i) => intToStr(i)
ca5fd6f 208
      Float(f) =>
ca5fd6f 209
        if f != f
ca5fd6f 210
          return "NaN"
ca5fd6f 211
        if f > MAX_FLOAT_VALUE || f < -MAX_FLOAT_VALUE
ca5fd6f 212
          return f < 0.0f ? "-Infinity" : "Infinity"
ca5fd6f 213
        neg := f < 0.0f
ca5fd6f 214
        abs_val := neg ? -f : f
ca5fd6f 215
        int_part := Int(abs_val)
ca5fd6f 216
        frac_val := abs_val - Float(int_part)
ca5fd6f 217
        scaled := Int(frac_val * 1000000.0f + 0.5f)
ca5fd6f 218
        carry := scaled >= 1000000
ca5fd6f 219
        whole_part := carry ? int_part + 1 : int_part
ca5fd6f 220
        frac := carry ? 0 : scaled
ca5fd6f 221
        s := intToStr(whole_part) + "." + trimmedFracDigits(frac, 6)
ca5fd6f 222
        return neg ? "-" + s : s
ca5fd6f 223
ca5fd6f 224
# ---- free-function helpers (no `self` to dispatch on) ----
ca5fd6f 225
ca5fd6f 226
# Plain decimal rendering of an `Int` — factored out of `Number.toStr` so the
ca5fd6f 227
# `Float` branch can reuse it (via `intToStr(whole_part)`) without
ca5fd6f 228
# re-dispatching back through `Number.toStr` itself.
ca5fd6f 229
fun intToStr(n: Int) -> Str =
ca5fd6f 230
  if n == 0
ca5fd6f 231
    return "0"
ca5fd6f 232
  neg := n < 0
ca5fd6f 233
  v := neg ? -n : n
ca5fd6f 234
  s := digitsToStr(v)
ca5fd6f 235
  return neg ? "-" + s : s
ca5fd6f 236
ca5fd6f 237
# Builds the decimal digits of a positive Int, most significant first, by
ca5fd6f 238
# peeling off one digit at a time from the least significant end (so the
ca5fd6f 239
# recursion emits digits in reverse call order — the leading, smallest-place
ca5fd6f 240
# digit is appended last).
ca5fd6f 241
fun digitsToStr(n: Int) -> Str =
ca5fd6f 242
  if n == 0
ca5fd6f 243
    return ""
ca5fd6f 244
  return digitsToStr(n / 10) + digitChar(n % 10)
ca5fd6f 245
ca5fd6f 246
fun digitChar(d: Int) -> Str =
ca5fd6f 247
  match d
ca5fd6f 248
    0 => "0"
ca5fd6f 249
    1 => "1"
ca5fd6f 250
    2 => "2"
ca5fd6f 251
    3 => "3"
ca5fd6f 252
    4 => "4"
ca5fd6f 253
    5 => "5"
ca5fd6f 254
    6 => "6"
ca5fd6f 255
    7 => "7"
ca5fd6f 256
    8 => "8"
ca5fd6f 257
    9 => "9"
ca5fd6f 258
    _ => "0"
ca5fd6f 259
ca5fd6f 260
# Renders `n`'s digits most-significant-first, always emitting exactly as many
ca5fd6f 261
# digits as `place_value` has powers of ten (leading zeros included) — used to
ca5fd6f 262
# render a fractional part at a fixed width regardless of its own leading zeros.
ca5fd6f 263
fun fixedFracDigits(n: Int, place_value: Int) -> Str =
ca5fd6f 264
  if place_value == 0
ca5fd6f 265
    return ""
ca5fd6f 266
  return digitChar(n / place_value % 10) + fixedFracDigits(n, place_value / 10)
ca5fd6f 267
ca5fd6f 268
fun pow10(n: Int) -> Int =
ca5fd6f 269
  if n <= 0
ca5fd6f 270
    return 1
ca5fd6f 271
  return 10 * pow10(n - 1)
ca5fd6f 272
ca5fd6f 273
# `fixedFracDigits(n, pow10(digits - 1))`, minus however many trailing-zero
ca5fd6f 274
# digits `n` has — but always at least one digit ("0" if `n` is 0 outright).
ca5fd6f 275
fun trimmedFracDigits(n: Int, digits: Int) -> Str =
ca5fd6f 276
  if n == 0 || digits <= 1
ca5fd6f 277
    return digitChar(n % 10)
ca5fd6f 278
  if n % 10 == 0
ca5fd6f 279
    return trimmedFracDigits(n / 10, digits - 1)
ca5fd6f 280
  return fixedFracDigits(n, pow10(digits - 1))
ca5fd6f 281
ca5fd6f 282
# Shared `base^y` algorithm behind `Number.pow`, extracted so both the `Int`
ca5fd6f 283
# and `Float` self cases (which only differ in how `base` was obtained) share
ca5fd6f 284
# one body.
ca5fd6f 285
fun powFloat(base: Float, y: Float) -> Float =
ca5fd6f 286
  if y == 0.0f
ca5fd6f 287
    return 1.0f
ca5fd6f 288
  yi := Int(y)
ca5fd6f 289
  if Float(yi) == y
ca5fd6f 290
    neg := yi < 0
ca5fd6f 291
    n := neg ? -yi : yi
ca5fd6f 292
    result := 1.0f
ca5fd6f 293
    b := base
ca5fd6f 294
    m := n
ca5fd6f 295
    while m > 0
ca5fd6f 296
      if m % 2 == 1
ca5fd6f 297
        result = result * b
ca5fd6f 298
      b = b * b
ca5fd6f 299
      m = m / 2
ca5fd6f 300
    return neg ? 1.0f / result : result
ca5fd6f 301
  if base < 0.0f
ca5fd6f 302
    return 0.0f / 0.0f
ca5fd6f 303
  return exp(y * ln(base))
ca5fd6f 304
ca5fd6f 305
# Parses a decimal integer, with an optional leading `+`/`-`.
ca5fd6f 306
fun parseInt(s: Str) -> Result[Int, Str] =
ca5fd6f 307
  len := s.length()
ca5fd6f 308
  if len == 0
ca5fd6f 309
    return Err("empty string")
ca5fd6f 310
  neg := s.byteAt(0) == 45
ca5fd6f 311
  start := neg || s.byteAt(0) == 43 ? 1 : 0
ca5fd6f 312
  if start >= len
ca5fd6f 313
    return Err("invalid integer: '{s}'")
ca5fd6f 314
  value := 0
ca5fd6f 315
  i := start
ca5fd6f 316
  while i < len
ca5fd6f 317
    b := s.byteAt(i)
ca5fd6f 318
    if b < 48 || b > 57
ca5fd6f 319
      return Err("invalid integer: '{s}'")
ca5fd6f 320
    value = value * 10 + b - 48
ca5fd6f 321
    i = i + 1
ca5fd6f 322
  return Ok(neg ? -value : value)
ca5fd6f 323
ca5fd6f 324
# Parses a decimal float, with an optional leading `+`/`-` and an optional
ca5fd6f 325
# `.` fractional part (no exponent notation).
ca5fd6f 326
fun parseFloat(s: Str) -> Result[Float, Str] =
ca5fd6f 327
  len := s.length()
ca5fd6f 328
  if len == 0
ca5fd6f 329
    return Err("empty string")
ca5fd6f 330
  neg := s.byteAt(0) == 45
ca5fd6f 331
  start := neg || s.byteAt(0) == 43 ? 1 : 0
ca5fd6f 332
  if start >= len
ca5fd6f 333
    return Err("invalid float: '{s}'")
ca5fd6f 334
  int_part := 0.0f
ca5fd6f 335
  saw_digit := False
ca5fd6f 336
  i := start
ca5fd6f 337
  while i < len && s.byteAt(i) != 46
ca5fd6f 338
    b := s.byteAt(i)
ca5fd6f 339
    if b < 48 || b > 57
ca5fd6f 340
      return Err("invalid float: '{s}'")
ca5fd6f 341
    int_part = int_part * 10.0f + Float(b - 48)
ca5fd6f 342
    saw_digit = True
ca5fd6f 343
    i = i + 1
ca5fd6f 344
  frac_part := 0.0f
ca5fd6f 345
  frac_scale := 1.0f
ca5fd6f 346
  if i < len && s.byteAt(i) == 46
ca5fd6f 347
    i = i + 1
ca5fd6f 348
    while i < len
ca5fd6f 349
      b = s.byteAt(i)
ca5fd6f 350
      if b < 48 || b > 57
ca5fd6f 351
        return Err("invalid float: '{s}'")
ca5fd6f 352
      frac_scale = frac_scale / 10.0f
ca5fd6f 353
      frac_part = frac_part + Float(b - 48) * frac_scale
ca5fd6f 354
      saw_digit = True
ca5fd6f 355
      i = i + 1
ca5fd6f 356
  if !saw_digit
ca5fd6f 357
    return Err("invalid float: '{s}'")
ca5fd6f 358
  value := int_part + frac_part
ca5fd6f 359
  return Ok(neg ? -value : value)
ca5fd6f 360
ca5fd6f 361
# A raw, host-provided pseudo-random 64-bit Int (xorshift64*, seeded from the
ca5fd6f 362
# wall clock — not cryptographically secure). Every other random-number
ca5fd6f 363
# function in std is built on top of this one host extern.
ca5fd6f 364
extern fun rawRandomInt() -> Int
ca5fd6f 365
ca5fd6f 366
# A random Float uniformly distributed in [0.0, 1.0).
ca5fd6f 367
fun random() -> Float =
ca5fd6f 368
  return Float(rawRandomInt() & MAX_VALUE) / Float(MAX_VALUE)
ca5fd6f 369
ca5fd6f 370
# A random Int uniformly distributed in [0, n). Returns 0 for n <= 0.
ca5fd6f 371
fun randomInt(n: Int) -> Int =
ca5fd6f 372
  if n <= 0
ca5fd6f 373
    return 0
ca5fd6f 374
  return { rawRandomInt() & MAX_VALUE } % n
ca5fd6f 375
ca5fd6f 376
# Natural exponential, e^x, via range reduction (halve x until |x| <= 0.5,
ca5fd6f 377
# run the Taylor series there where it converges fast, then square the
ca5fd6f 378
# result back up the same number of halvings).
ca5fd6f 379
fun exp(x: Float) -> Float =
ca5fd6f 380
  if x != x
ca5fd6f 381
    return x
ca5fd6f 382
  if x > 700.0f
ca5fd6f 383
    return 1.0f / 0.0f
ca5fd6f 384
  if x < -700.0f
ca5fd6f 385
    return 0.0f
ca5fd6f 386
  v := x
ca5fd6f 387
  k := 0
ca5fd6f 388
  while v > 0.5f || v < -0.5f
ca5fd6f 389
    v = v / 2.0f
ca5fd6f 390
    k = k + 1
ca5fd6f 391
  term := 1.0f
ca5fd6f 392
  sum := 1.0f
ca5fd6f 393
  n := 1
ca5fd6f 394
  while n < 25
ca5fd6f 395
    term = term * v / Float(n)
ca5fd6f 396
    sum = sum + term
ca5fd6f 397
    n = n + 1
ca5fd6f 398
  result := sum
ca5fd6f 399
  i := 0
ca5fd6f 400
  while i < k
ca5fd6f 401
    result = result * result
ca5fd6f 402
    i = i + 1
ca5fd6f 403
  return result
ca5fd6f 404
ca5fd6f 405
# Natural logarithm, via range reduction to v in [1, 2) plus the
ca5fd6f 406
# fast-converging series ln(v) = 2*atanh((v-1)/(v+1)).
ca5fd6f 407
fun ln(x: Float) -> Float =
ca5fd6f 408
  if x != x || x < 0.0f
ca5fd6f 409
    return 0.0f / 0.0f
ca5fd6f 410
  if x == 0.0f
ca5fd6f 411
    return -1.0f / 0.0f
ca5fd6f 412
  if x > MAX_FLOAT_VALUE
ca5fd6f 413
    return x
ca5fd6f 414
  v := x
ca5fd6f 415
  k := 0
ca5fd6f 416
  while v >= 2.0f
ca5fd6f 417
    v = v / 2.0f
ca5fd6f 418
    k = k + 1
ca5fd6f 419
  while v < 1.0f
ca5fd6f 420
    v = v * 2.0f
ca5fd6f 421
    k = k - 1
ca5fd6f 422
  t := {v - 1.0f} / {v + 1.0f}
ca5fd6f 423
  t2 := t * t
ca5fd6f 424
  term := t
ca5fd6f 425
  sum := t
ca5fd6f 426
  n := 1
ca5fd6f 427
  while n < 30
ca5fd6f 428
    term = term * t2
ca5fd6f 429
    sum = sum + term / Float(2 * n + 1)
ca5fd6f 430
    n = n + 1
ca5fd6f 431
  return Float(k) * LN2 + 2.0f * sum
ca5fd6f 432
ca5fd6f 433
# Square root via Newton's method, iterating to a fixed point.
ca5fd6f 434
fun sqrt(x: Float) -> Float =
ca5fd6f 435
  if x < 0.0f
ca5fd6f 436
    return 0.0f / 0.0f
ca5fd6f 437
  if x == 0.0f || x != x
ca5fd6f 438
    return x
ca5fd6f 439
  guess := x
ca5fd6f 440
  prev := 0.0f
ca5fd6f 441
  i := 0
ca5fd6f 442
  while guess != prev && i < 100
ca5fd6f 443
    prev = guess
ca5fd6f 444
    guess = 0.5f * {guess + x / guess}
ca5fd6f 445
    i = i + 1
ca5fd6f 446
  return guess
ca5fd6f 447
ca5fd6f 448
# Reduces `x` into roughly `[-PI, PI]` by subtracting the nearest multiple of
ca5fd6f 449
# `TAU` — the range `sin`/`cos`'s Taylor series below actually converge
ca5fd6f 450
# quickly over. For very large `|x|` (many multiples of `TAU`), floating-point
ca5fd6f 451
# cancellation in `x - k*TAU` loses precision the same way any naive
ca5fd6f 452
# range-reduction by subtraction does; a real libm uses extended-precision
ca5fd6f 453
# constants to avoid this, which isn't attempted here.
ca5fd6f 454
fun reduceToPi(x: Float) -> Float =
ca5fd6f 455
  k := Int(x / TAU + {x >= 0.0f ? 0.5f : -0.5f})
ca5fd6f 456
  return x - Float(k) * TAU
ca5fd6f 457
ca5fd6f 458
fun sin(x: Float) -> Float =
ca5fd6f 459
  if x != x || x > MAX_FLOAT_VALUE || x < -MAX_FLOAT_VALUE
ca5fd6f 460
    return 0.0f / 0.0f
ca5fd6f 461
  r := reduceToPi(x)
ca5fd6f 462
  r2 := r * r
ca5fd6f 463
  term := r
ca5fd6f 464
  sum := r
ca5fd6f 465
  n := 1
ca5fd6f 466
  while n < 10
ca5fd6f 467
    term = term * {-r2} / Float({2 * n} * {2 * n + 1})
ca5fd6f 468
    sum = sum + term
ca5fd6f 469
    n = n + 1
ca5fd6f 470
  return sum
ca5fd6f 471
ca5fd6f 472
fun cos(x: Float) -> Float =
ca5fd6f 473
  if x != x || x > MAX_FLOAT_VALUE || x < -MAX_FLOAT_VALUE
ca5fd6f 474
    return 0.0f / 0.0f
ca5fd6f 475
  return sin(x + HALF_PI)
ca5fd6f 476
ca5fd6f 477
fun tan(x: Float) -> Float =
ca5fd6f 478
  return sin(x) / cos(x)
ca5fd6f 479
ca5fd6f 480
# Arctangent, via repeated argument-halving (`atan(x) = 2*atan(x / (1 +
ca5fd6f 481
# sqrt(1+x^2)))`) until `|x| <= 0.5` (where the Taylor series below converges
ca5fd6f 482
# quickly), then doubling the result back up the same number of times.
ca5fd6f 483
fun atan(x: Float) -> Float =
ca5fd6f 484
  if x != x
ca5fd6f 485
    return x
ca5fd6f 486
  neg := x < 0.0f
ca5fd6f 487
  v := neg ? -x : x
ca5fd6f 488
  k := 0
ca5fd6f 489
  while v > 0.5f && k < 8
ca5fd6f 490
    v = v / {1.0f + sqrt(1.0f + v * v)}
ca5fd6f 491
    k = k + 1
ca5fd6f 492
  v2 := v * v
ca5fd6f 493
  term := v
ca5fd6f 494
  sum := v
ca5fd6f 495
  n := 1
ca5fd6f 496
  while n < 20
ca5fd6f 497
    term = term * {-v2}
ca5fd6f 498
    sum = sum + term / Float(2 * n + 1)
ca5fd6f 499
    n = n + 1
ca5fd6f 500
  scale := Float(1 << k)
ca5fd6f 501
  result := sum * scale
ca5fd6f 502
  return neg ? -result : result
ca5fd6f 503
ca5fd6f 504
fun asin(x: Float) -> Float =
ca5fd6f 505
  if x != x || x < -1.0f || x > 1.0f
ca5fd6f 506
    return 0.0f / 0.0f
ca5fd6f 507
  if x == 1.0f
ca5fd6f 508
    return HALF_PI
ca5fd6f 509
  if x == -1.0f
ca5fd6f 510
    return -HALF_PI
ca5fd6f 511
  return atan(x / sqrt(1.0f - x * x))
ca5fd6f 512
ca5fd6f 513
fun acos(x: Float) -> Float =
ca5fd6f 514
  return HALF_PI - asin(x)
ca5fd6f 515
ca5fd6f 516
# Angle (in radians) of the point `(x, y)` from the origin, in the correct
ca5fd6f 517
# quadrant for any sign combination of `x`/`y` (unlike plain `atan(y/x)`,
ca5fd6f 518
# which can't distinguish opposite quadrants).
ca5fd6f 519
fun atan2(y: Float, x: Float) -> Float =
ca5fd6f 520
  if x > 0.0f
ca5fd6f 521
    return atan(y / x)
ca5fd6f 522
  if x < 0.0f && y >= 0.0f
ca5fd6f 523
    return atan(y / x) + PI
ca5fd6f 524
  if x < 0.0f && y < 0.0f
ca5fd6f 525
    return atan(y / x) - PI
ca5fd6f 526
  if x == 0.0f && y > 0.0f
ca5fd6f 527
    return HALF_PI
ca5fd6f 528
  if x == 0.0f && y < 0.0f
ca5fd6f 529
    return -HALF_PI
ca5fd6f 530
  return 0.0f
ca5fd6f 531
ca5fd6f 532
fun hypot(a: Float, b: Float) -> Float =
ca5fd6f 533
  return sqrt(a * a + b * b)
ca5fd6f 534
ca5fd6f 535
# Cube root via Newton's method (fixed iteration count, unlike `sqrt`'s
ca5fd6f 536
# converge-to-a-fixed-point loop, since the cubic update step doesn't reach an
ca5fd6f 537
# exact fixed point in float precision as reliably as the quadratic one does).
ca5fd6f 538
fun cbrt(x: Float) -> Float =
ca5fd6f 539
  if x == 0.0f || x != x
ca5fd6f 540
    return x
ca5fd6f 541
  neg := x < 0.0f
ca5fd6f 542
  v := neg ? -x : x
ca5fd6f 543
  guess := v
ca5fd6f 544
  i := 0
ca5fd6f 545
  while i < 60
ca5fd6f 546
    guess = {2.0f * guess + v / {guess * guess}} / 3.0f
ca5fd6f 547
    i = i + 1
ca5fd6f 548
  return neg ? -guess : guess