//===--- 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 * /// 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(_ x: T, _ y: T) -> T where T: ElementaryFunctions { return T.pow(x, y) } @_alwaysEmitIntoClient public func pow(_ 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(_ x: T, _ n: Int) -> T where T: ElementaryFunctions { return T.pow(x, n) } @_alwaysEmitIntoClient public func pow(_ 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(_ x: T, _ n: Int) -> T where T: ElementaryFunctions { return T.root(x, n) } @_alwaysEmitIntoClient public func root(_ 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(y: T, x: T) -> T where T: Real { return T.atan2(y: y, x: x) } @_alwaysEmitIntoClient public func atan2(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(_ x: T) -> T where T: Real { return T.logGamma(x) } @_alwaysEmitIntoClient public func signGamma(_ 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}(_ x: T) -> T where T: FloatingPoint { return x.rounded(${dir}) } @_alwaysEmitIntoClient public func ${func}(_ x: T) -> T where T: SIMD, T.Scalar: FloatingPoint { return x.rounded(${dir}) } %end @_alwaysEmitIntoClient public func fma(_ x: T, _ y: T, _ z: T) -> T where T: FloatingPoint { return z.addingProduct(x, y) } @_alwaysEmitIntoClient public func fma(_ x: T, _ y: T, _ z: T) -> T where T: SIMD, T.Scalar: FloatingPoint { return z.addingProduct(x, y) } @_alwaysEmitIntoClient public func remainder(_ dividend: T, _ divisor: T) -> T where T: FloatingPoint { return dividend.remainder(dividingBy: divisor) } @_alwaysEmitIntoClient public func remainder(_ 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(_ magnitude: T, _ sign: T) -> T where T: FloatingPoint { return T(signOf: sign, magnitudeOf: magnitude) } @_alwaysEmitIntoClient public func copysign(_ 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 }