Files
swift-mirror/stdlib/public/core/MathFunctions.swift.gyb
Steve (Numerics) Canon bcc7e2c901 Obscene rant about linters.
2019-04-03 15:15:28 -04:00

330 lines
9.9 KiB
Swift

//===--- MathFunctions.swift ----------------------------------*- swift -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2019 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import SwiftShims
%from SwiftMathFunctions import *
%from SwiftFloatingPointTypes import all_floating_point_types
/// A type that has elementary functions available.
///
/// An ["elementary function"][elfn] is a function built up from powers, roots,
/// exponentials, logarithms, trigonometric functions (sin, cos, tan) and
/// their inverses, and the hyperbolic functions (sinh, cosh, tanh) and their
/// inverses.
///
/// Conformance to this protocol means that all of these building blocks are
/// available as static functions on the type.
///
/// ```swift
/// let x: Float = 1
/// let y = Float.sin(x) // 0.84147096
/// ```
///
/// All of these are made available as free functions by importing the Math
/// module:
///
/// ```swift
/// import Math
/// let y = sin(x) // 0.84147096
/// ```
///
/// Additional operations, such as `atan2(y:x:)`, `hypot(_:_:)` and some
/// special functions, are provided on the Real protocol, which refines both
/// ElementaryFunctions and FloatingPoint.
///
/// [elfn]: http://en.wikipedia.org/wiki/Elementary_function
public protocol ElementaryFunctions {
%for func in ElementaryFunctions:
${func.comment}
static func ${func.decl("Self")}
%end
/// `exp(y log(x))` computed without loss of intermediate precision.
///
/// For real types, if `x` is negative the result is NaN, even if `y` has
/// an integral value. For complex types, there is a branch cut on the
/// negative real axis.
static func pow(_ x: Self, _ y: Self) -> Self
/// `x` raised to the `n`th power.
static func pow(_ x: Self, _ n: Int) -> Self
/// The `n`th root of `x`.
///
/// For real types, if `x` is negative and `n` is even, the result is NaN.
/// For complex types, there is a branch cut along the negative real axis.
static func root(_ x: Self, _ n: Int) -> Self
}
/// A type that models the real numbers.
public protocol Real: ElementaryFunctions, FloatingPoint {
%for func in RealFunctions:
${func.comment}
static func ${func.decl("Self")}
%end
/// `atan(y/x)` with quadrant fixup.
///
/// There is an infinite family of angles whose tangent is `y/x`. `atan2`
/// selects the representative that is the angle between the vector `(x, y)`
/// and the real axis in the range [-π, π].
static func atan2(y: Self, x: Self) -> Self
#if !os(Windows)
// lgamma is not available on Windows.
// TODO: provide an implementation of lgamma with the stdlib to support
// Windows so we can vend a uniform interface.
/// `log(gamma(x))` computed without undue overflow.
///
/// `log(abs(gamma(x)))` is returned. To get the sign of `gamma(x)` cheaply,
/// use `signGamma(x)`.
static func logGamma(_ x: Self) -> Self
#endif
}
extension Real {
#if !os(Windows)
// lgamma is not available on Windows; no lgamma means signGamma
// is basically useless, so don't bother exposing it.
/// The sign of `gamma(x)`.
///
/// This function is typically used in conjunction with `logGamma(x)`, which
/// computes `log(abs(gamma(x)))`, to recover the sign information that is
/// lost to the absolute value.
///
/// `gamma(x)` has a simple pole at each non-positive integer and an
/// essential singularity at infinity; we arbitrarily choose to return
/// `.plus` for the sign in those cases. For all other values, `signGamma(x)`
/// is `.plus` if `x >= 0` or `trunc(x)` is odd, and `.minus` otherwise.
@_alwaysEmitIntoClient
public static func signGamma(_ x: Self) -> FloatingPointSign {
if x >= 0 { return .plus }
let trunc = x.rounded(.towardZero)
// Treat poles as gamma(x) == +inf. This is arbitrary, but we need to
// pick one sign or the other.
if x == trunc { return .plus }
// Result is .minus if trunc is even, .plus otherwise. To figure out if
// trunc is even or odd, check if trunc/2 is an integer.
let halfTrunc = trunc/2
if halfTrunc == halfTrunc.rounded(.towardZero) { return .minus }
return .plus
}
#endif
}
%for type in all_floating_point_types():
% if type.bits == 80:
#if (arch(i386) || arch(x86_64)) && !os(Windows)
% end
% Self = type.stdlib_name
extension ${Self}: Real {
% for func in ElementaryFunctions + RealFunctions:
@_alwaysEmitIntoClient
public static func ${func.decl(Self)} {
return ${func.impl(type)}
}
% end
@_alwaysEmitIntoClient
public static func pow(_ x: ${Self}, _ y: ${Self}) -> ${Self} {
guard x >= 0 else { return .nan }
return ${Self}(Builtin.int_pow_FPIEEE${type.bits}(x._value, y._value))
}
@_alwaysEmitIntoClient
public static func pow(_ x: ${Self}, _ n: Int) -> ${Self} {
// TODO: this implementation isn't quite right for n so large that
// the conversion to `${Self}` rounds. We could also consider using
// a multiply-chain implementation for small `n`; this would be faster
// for static `n`, but less accurate on platforms with a good `pow`
// implementation.
return ${Self}(Builtin.int_pow_FPIEEE${type.bits}(x._value, ${Self}(n)._value))
}
@_alwaysEmitIntoClient
public static func root(_ x: ${Self}, _ n: Int) -> ${Self} {
guard x >= 0 || n % 2 != 0 else { return .nan }
// TODO: this implementation isn't quite right for n so large that
// the conversion to `${Self}` rounds.
return ${Self}(signOf: x, magnitudeOf: pow(x, 1/${Self}(n)))
}
@_alwaysEmitIntoClient
public static func atan2(y: ${Self}, x: ${Self}) -> ${Self} {
return _swift_stdlib_atan2${type.cFuncSuffix}(y, x)
}
#if !os(Windows)
@_alwaysEmitIntoClient
public static func logGamma(_ x: ${Self}) -> ${Self} {
return _swift_stdlib_lgamma${type.cFuncSuffix}(x)
}
#endif
}
% if type.bits == 80:
#endif
% end
%end
// MARK: - Free functions defined on ElementaryFunctions:
%from SwiftMathFunctions import *
%for func in ElementaryFunctions:
% if func.name == 'sqrt':
// sqrt requires availability because it was previous generic on FloatingPoint.
@available(swift, introduced: 5.1)
% end
@_alwaysEmitIntoClient
public func ${func.free_decl()} {
return T.${func.swiftName}(${func.params()})
}
@_alwaysEmitIntoClient
public func ${func.free_decl("T: SIMD, T.Scalar: ElementaryFunctions")} {
var r = T()
for i in r.indices { r[i] = T.Scalar.${func.swiftName}(${func.params("","[i]")}) }
return r
}
%end
@_alwaysEmitIntoClient
public func pow<T>(_ x: T, _ y: T) -> T where T: ElementaryFunctions {
return T.pow(x, y)
}
@_alwaysEmitIntoClient
public func pow<T>(_ x: T, _ y: T) -> T where T: SIMD, T.Scalar: ElementaryFunctions {
var r = T()
for i in r.indices { r[i] = T.Scalar.pow(x[i], y[i]) }
return r
}
@_alwaysEmitIntoClient
public func pow<T>(_ x: T, _ n: Int) -> T where T: ElementaryFunctions {
return T.pow(x, n)
}
@_alwaysEmitIntoClient
public func pow<T>(_ x: T, _ n: Int) -> T where T: SIMD, T.Scalar: ElementaryFunctions {
var r = T()
for i in r.indices { r[i] = T.Scalar.pow(x[i], n) }
return r
}
@_alwaysEmitIntoClient
public func root<T>(_ x: T, _ n: Int) -> T where T: ElementaryFunctions {
return T.root(x, n)
}
@_alwaysEmitIntoClient
public func root<T>(_ x: T, _ n: Int) -> T where T: SIMD, T.Scalar: ElementaryFunctions {
var r = T()
for i in r.indices { r[i] = T.Scalar.root(x[i], n) }
return r
}
// MARK: - Free functions defined on Real:
%for func in RealFunctions:
@_alwaysEmitIntoClient
public func ${func.free_decl("T: Real")} {
return T.${func.swiftName}(${func.params()})
}
@_alwaysEmitIntoClient
public func ${func.free_decl("T: SIMD, T.Scalar: Real")} {
var r = T()
for i in r.indices { r[i] = T.Scalar.${func.swiftName}(${func.params("","[i]")}) }
return r
}
%end
@_alwaysEmitIntoClient
public func atan2<T>(y: T, x: T) -> T where T: Real {
return T.atan2(y: y, x: x)
}
@_alwaysEmitIntoClient
public func atan2<T>(y: T, x: T) -> T where T: SIMD, T.Scalar: Real {
var r = T()
for i in r.indices { r[i] = T.Scalar.atan2(y: y[i], x: x[i]) }
return r
}
// logGamma is not available on Windows.
// TODO: logGamma, signGamma for SIMD types.
#if !os(Windows)
@_alwaysEmitIntoClient
public func logGamma<T>(_ x: T) -> T where T: Real {
return T.logGamma(x)
}
@_alwaysEmitIntoClient
public func signGamma<T>(_ x: T) -> FloatingPointSign where T: Real {
return T.signGamma(x)
}
#endif
// MARK: - Free functions defined on FloatingPoint:
% for (func,dir) in [("ceil", ".up"), ("floor", ".down"), ("round", ""), ("trunc", ".towardZero")]:
@_alwaysEmitIntoClient
public func ${func}<T>(_ x: T) -> T where T: FloatingPoint {
return x.rounded(${dir})
}
@_alwaysEmitIntoClient
public func ${func}<T>(_ x: T) -> T where T: SIMD, T.Scalar: FloatingPoint {
return x.rounded(${dir})
}
%end
@_alwaysEmitIntoClient
public func fma<T>(_ x: T, _ y: T, _ z: T) -> T where T: FloatingPoint {
return z.addingProduct(x, y)
}
@_alwaysEmitIntoClient
public func fma<T>(_ x: T, _ y: T, _ z: T) -> T where T: SIMD, T.Scalar: FloatingPoint {
return z.addingProduct(x, y)
}
@_alwaysEmitIntoClient
public func remainder<T>(_ dividend: T, _ divisor: T) -> T where T: FloatingPoint {
return dividend.remainder(dividingBy: divisor)
}
@_alwaysEmitIntoClient
public func remainder<T>(_ dividend: T, _ divisor: T) -> T where T: SIMD, T.Scalar: FloatingPoint {
var r = T()
for i in r.indices { r[i] = dividend[i].remainder(dividingBy: divisor[i]) }
return r
}
@_alwaysEmitIntoClient
public func copysign<T>(_ magnitude: T, _ sign: T) -> T where T: FloatingPoint {
return T(signOf: sign, magnitudeOf: magnitude)
}
@_alwaysEmitIntoClient
public func copysign<T>(_ magnitude: T, _ sign: T) -> T where T: SIMD, T.Scalar: FloatingPoint {
var r = T()
for i in r.indices { r[i] = copysign(magnitude[i], sign[i]) }
return r
}