If the only thing I know about the variables x, y is that they are of type T which is conforming to the FloatingPoint protocol, how can I calculate the value of atan2(y, x) and return it as an instance of type T?
Actually, I was trying to implement a Vector2D protocol, but I met problems with the following methods/properties:
.init(radius r:Component, angle a:Component)
.angle
The following is my implementation:
/*
Swift Tip: Vector Algebra with Protocols
https://www.objc.io/blog/2018/11/29/vector-calculus-with-protocols/
FloatingPoint
https://developer.apple.com/documentation/swift/floatingpoint
*/
import Foundation // cos, sin
// math constants
extension FloatingPoint {
public static var deg: Self { .pi / 180 }
}
/// Vector2D
public protocol Vector2D: ExpressibleByArrayLiteral, CustomStringConvertible {
// vector component type
associatedtype Component: FloatingPoint
// (x,y) coordinates
var x: Component { get }
var y: Component { get }
// required initializer
init(x: Component, y: Component)
}
// custom operators (declaration)
// ⭐️ can only be declared at file scope
infix operator • : MultiplicationPrecedence // inner product
infix operator × : MultiplicationPrecedence // outer product
// ExpressibleByArrayLiteral
public extension Vector2D {
public init(arrayLiteral a: Component...) {
assert(a.count >= 2, "Must initialize vector with at least 2 values.")
self.init(x: a[0], y: a[1])
}
}
// CustomStringConvertible
public extension Vector2D {
public var description: String { "(\(x), \(y))" }
}
// initializers & factory methods
public extension Vector2D {
// usage: Self(x, y)
public init(_ x:Component, _ y:Component) {
self.init(x: x, y: y)
}
// usage: Self(radius: r, angle: a)
public init(radius r:Component, angle a:Component) {
let r = r as! Double // ⛔️doesn't work‼️
let a = a as! Double
let x = r * cos(a) as! Component
let y = r * sin(a) as! Component
self.init(x: x, y: y)
}
// usage: Self.polar(r, a)
public static func polar(_ r: Component, _ a: Component) -> Self {
Self(radius: r, angle: a)
}
}
// constant vectors
public extension Vector2D {
// zero vector: (0,0)
public static var zero: Self {
Self(x: Component.zero, y: Component.zero)
}
}
// vector operations
public extension Vector2D {
// u + v
public static func + (u: Self, v: Self) -> Self {
Self(x: u.x + v.x, y: u.y + v.y)
}
// -u (prefix)
public static prefix func - (v: Self) -> Self {
Self(x: -v.x, y: -v.y)
}
// u - v
public static func - (u: Self, v: Self) -> Self {
u + (-v)
}
// a * v, v * a, v / a (scalar product)
public static func * (a: Component, v: Self) -> Self {
Self(x:a * v.x, y: a * v.y)
}
public static func * (v: Self, a: Component) -> Self {
a * v
}
public static func / (v: Self, a: Component) -> Self {
(1/a) * v
}
// u • v (dot product)
public static func • (u: Self, v: Self) -> Component {
u.x * v.x + u.y * v.y // x1x2 + y1y2
}
// u × v (cross product)
public static func × (u: Self, v: Self) -> Component {
u.x * v.y - u.y * v.x // ad - bc
}
}
// complex numbers
public extension Vector2D {
// z1 * z2 (complex product)
public static func * (z1: Self, z2: Self) -> Self {
let (a,b) = (z1.x, z1.y) // z1 = a + bi
let (c,d) = (z2.x, z2.y) // z2 = c + di
return Self(x: a*c - b*d, y: a*d + b*c) // z1 * z2 = (ac-bd) + (ad+bc)i
}
// z.conjugate
public var conjugate: Self { Self(x: x, y: -y) } // a - bi
// z1 / z2 (complex division)
public static func / (z1: Self, z2: Self) -> Self {
z1 * z2.conjugate / (z2 • z2)
}
}
// vector properties
public extension Vector2D {
public var length: Component { sqrt(self • self) } // |v|
public var magnitude: Component { length } // |v|
public var angle: Component { // in radians
atan2(y as! Double, x as! Double) as! Component // ⛔️doesn't work‼️
}
public var degrees: Component { angle / .deg } // in degrees
}
My workaround: (including 2 files - Vector2D.swift and CGPoint+Vector2D.swift)
Vector2D.swift
import Foundation
// math constants
extension FloatingPoint {
public static var deg: Self { .pi / 180 }
}
// protocol for Vector2D.Component
public protocol VectorComponent: FloatingPoint {
static func cos(_ x: Self) -> Self
static func sin(_ x: Self) -> Self
static func atan2(_ y: Self, _ x: Self) -> Self
}
/// Vector2D
public protocol Vector2D: ExpressibleByArrayLiteral, CustomStringConvertible {
// vector component type
associatedtype Component: VectorComponent
// (x,y) coordinates
var x: Component { get }
var y: Component { get }
// required initializer
init(x: Component, y: Component)
}
// custom operators (declaration)
// ⭐️ can only be declared at file scope
infix operator • : MultiplicationPrecedence // inner product
infix operator × : MultiplicationPrecedence // outer product
// ExpressibleByArrayLiteral
public extension Vector2D {
public init(arrayLiteral elements: Component...) {
let a = elements + [0, 0] // make sure that a.count >= 2
self.init(x: a[0], y: a[1])
}
}
// CustomStringConvertible
public extension Vector2D {
public var description: String { "(\(x), \(y))" }
}
// initializers & factory methods
public extension Vector2D {
// usage: Self(x, y)
public init(_ x:Component, _ y:Component) {
self.init(x: x, y: y)
}
// usage: Self(radius: r, angle: a)
public init(radius r: Component, angle a: Component) {
self.init(x: r * Component.cos(a), y: r * Component.sin(a))
}
// usage: Self.polar(r, a)
public static func polar(_ r: Component, _ a: Component) -> Self {
Self(radius: r, angle: a)
}
}
// constant vectors
public extension Vector2D {
// zero vector: (0,0)
public static var zero: Self {
Self(x: Component.zero, y: Component.zero)
}
}
// vector operations
public extension Vector2D {
// u + v
public static func + (u: Self, v: Self) -> Self {
Self(x: u.x + v.x, y: u.y + v.y)
}
// -u (prefix)
public static prefix func - (v: Self) -> Self {
Self(x: -v.x, y: -v.y)
}
// u - v
public static func - (u: Self, v: Self) -> Self {
u + (-v)
}
// a * v, v * a, v / a (scalar product)
public static func * (a: Component, v: Self) -> Self {
Self(x:a * v.x, y: a * v.y)
}
public static func * (v: Self, a: Component) -> Self {
a * v
}
public static func / (v: Self, a: Component) -> Self {
(1/a) * v
}
// u • v (dot product)
public static func • (u: Self, v: Self) -> Component {
u.x * v.x + u.y * v.y // x1x2 + y1y2
}
// u × v (cross product)
public static func × (u: Self, v: Self) -> Component {
u.x * v.y - u.y * v.x // ad - bc
}
}
// complex numbers
public extension Vector2D {
// z1 * z2 (complex product)
public static func * (z1: Self, z2: Self) -> Self {
let (a,b) = (z1.x, z1.y) // z1 = a + bi
let (c,d) = (z2.x, z2.y) // z2 = c + di
return Self(x: a*c - b*d, y: a*d + b*c) // z1 * z2 = (ac-bd) + (ad+bc)i
}
// z.conjugate
public var conjugate: Self { Self(x: x, y: -y) } // a - bi
// z1 / z2 (complex division)
public static func / (z1: Self, z2: Self) -> Self {
z1 * z2.conjugate / (z2 • z2)
}
}
// vector properties
public extension Vector2D {
public var length: Component { sqrt(self • self) } // |v|
public var magnitude: Component { length } // |v|
public var angle: Component { Component.atan2(y, x) } // in radians
public var degrees: Component { angle / .deg } // in degrees
}
// vector functions
public func abs<T: Vector2D>(_ v: T) -> T.Component {
v.length
}
CGPoint+Vector2D.swift
import CoreGraphics // for CGPoint, cos, sin, atan2
// CGFloat (protocol conformance)
extension CGFloat: VectorComponent {
public static func cos(_ x: CGFloat) -> CGFloat { CoreGraphics.cos(x) }
public static func sin(_ x: CGFloat) -> CGFloat { CoreGraphics.sin(x) }
public static func atan2(_ y: CGFloat, _ x: CGFloat) -> CGFloat { CoreGraphics.atan2(y,x) }
}
// CGPoint (protocol conformance)
extension CGPoint: Vector2D {}
I'm trying to output a value relative to a base value that is calculated elsewhere. I was hoping that ValueTransformer would do the trick but I have no idea how to get a base value into the ValueTransformer.The initWithBase func shown below does not work. Any advice would be welcome. Thanks.
import Cocoa
class PercentTransformer: ValueTransformer {
var baseValue: Double = 1
override class func transformedValueClass() -> Swift.AnyClass {
return NSString.self
}
override class func allowsReverseTransformation() -> Bool {
return false
}
func initWithBase(base: Double) {
baseValue = base
return
}
override func transformedValue(_ value: Any?) -> Any? {
let inputValue = value as! Double
if inputValue == 0.0 {
return "--------------"
}
// calculate output value
let percent = inputValue/baseValue * 100
let output = String(format: "%.2e", inputValue) + " " + String(format: "%.1f", percent) + "%"
return output as NSString
}
}
For example, write an extension for operator ">" to compare CGSize
let sizeA = CGSize(...)
let sizeB = CGSize(...)
sizeA > sizeB // How to let operator ">" support CGSize
You can extend CGSize to conform to Comparable protocol. #MartinR got the point. I updated my answer to compare the size as area comparison.
extension CGSize {
public static func <(lhs: CGSize, rhs: CGSize) -> Bool {
return (lhs.width * lhs.height) < (rhs.width * rhs.height)
}
public static func >(lhs: CGSize, rhs: CGSize) -> Bool {
return (lhs.width * lhs.height) > (rhs.width * rhs.height)
}
}
Simply define the > function for CGSize:
extension CGSize {
static func > (_ lhs: CGSize, _ rhs: CGSize) -> Bool {
// Implement as needed
return lhs.width > rhs.width && lhs.height > rhs.height
}
}
Just to overload the operator >
extension CGSize{
static func >(_ left: CGSize, _ right: CGSize)->Bool{
return left.width*left.height > right.width*right.height
}
}
I would like to call floating point methods on floating point types in swift.
func example<T : FloatingPoint>(_ x:T) -> T {
return cos(cbrt(x + 1))
}
Is there a better way to do so than this?
protocol SupportsFloatingPoint : FloatingPoint {
var cubeRoot:Self { get }
var cosine:Self { get }
}
extension Double : SupportsFloatingPoint {
var cubeRoot:Double { return Darwin.cbrt(self) }
var cosine:Double { return Darwin.cos(self) }
}
extension Float : SupportsFloatingPoint {
var cubeRoot:Float { return Darwin.cbrt(self) }
var cosine:Float { return Darwin.cos(self) }
}
func cbrt<T : SupportsFloatingPoint>(_ x:T) -> T {
return x.cubeRoot
}
func cos<T : SupportsFloatingPoint>(_ x:T) -> T {
return x.cosine
}
func example<T : SupportsFloatingPoint>(_ x:T) -> T {
return cos(cbrt(x - 1))
}
Note that the addition operator is lost here. You can do -, * and / but not + on SupportsFloatingPoint types.
Generic Math(s) Functions were proposed in SE-0246, and accepted with modifications. The implementation is currently blocked by some other factors.
In the meanwhile, you can access the same functionality from the Numerics library hosted on Apple's own GitHub page, and distributed with the Swift Package Manager.
Original Answer
You don't need a new protocol. You can extend the existing FloatingPoint protocol for supported types:
// Just for fun :)
prefix operator √
prefix operator ∛
extension FloatingPoint where Self == Double {
var squareRoot: Self { return sqrt(self) }
var cubeRoot: Self { return cbrt(self) }
var sine: Self { return sin(self) }
var cosine: Self { return cos(self) }
static prefix func √(_ x: Self) -> Self { return x.squareRoot }
static prefix func ∛(_ x: Self) -> Self { return x.cubeRoot }
}
extension FloatingPoint where Self == Float {
var squareRoot: Self { return sqrt(self) }
var cubeRoot: Self { return cbrt(self) }
var sine: Self { return sin(self) }
var cosine: Self { return cos(self) }
static prefix func √(_ x: Self) -> Self { return x.squareRoot }
static prefix func ∛(_ x: Self) -> Self { return x.cubeRoot }
}
print(Double.pi.cosine)
print(√25.0)
print(∛8.0)
Admittedly, there's a lot of code duplication, which I'm currently investigating how to minimize. On the bright side, at least this is all static, inlineable code that'll produce very fast machine code.
This is working great for Doubles and Floats. But getting other numerals like Ints involved is proving really difficult.
public protocol TemperatureConvertable: FloatLiteralConvertible, CustomStringConvertible {
func +(lhs: Self, rhs: Self) -> Self
func -(lhs: Self, rhs: Self) -> Self
func *(lhs: Self, rhs: Self) -> Self
func /(lhs: Self, rhs: Self) -> Self
}
extension Double: TemperatureConvertable {}
extension Float: TemperatureConvertable {}
public func fahrenheitToCelsius<T: TemperatureConvertable>(fahrenheit: T) -> T {
// (°F − 32) ÷ 1.8 =°C
return (fahrenheit - 32.0) / 1.8
}
public func celsiusToFahrenheit<T: TemperatureConvertable>(celsius: T) -> T {
// (°C × 1.8) + 32 =°F
return (celsius * 1.8) + 32.0
}
From what I can find there isn't a NumberLiteralConvertible or similar. And just creating a Numeric protocol like others suggest makes the arithmetic fail because the types don't match. Ideally I'd like something like this thats more generic but this I get Binary operator '-' cannot be applied to operands of type 'T' and 'Double' on the line of actual math for fahrenheit - 32.0 because the lhs and rhs aren't both Self.
public protocol TemperatureConvertable: CustomStringConvertible {
func +(lhs: Self, rhs: Self) -> Self
func -(lhs: Self, rhs: Self) -> Self
func *(lhs: Self, rhs: Self) -> Self
func /(lhs: Self, rhs: Self) -> Self
}
extension Int: TemperatureConvertable {}
extension Double: TemperatureConvertable {}
extension Float: TemperatureConvertable {}
public func fahrenheitToCelsius<T: TemperatureConvertable>(fahrenheit: T) -> T {
// (°F − 32) ÷ 1.8 =°C
return (fahrenheit - 32.0) / 1.8
}
public func celsiusToFahrenheit<T: TemperatureConvertable>(celsius: T) -> T {
// (°C × 1.8) + 32 =°F
return (celsius * 1.8) + 32.0
}
You need a way to create a TemperatureConvertable from a Double and viceversa.
So your functions will be able to internally use Double(s) to make the operations. And finally convert the result to T for the output.
Step 1
public protocol TemperatureConvertible: CustomStringConvertible {
// this part is not needed
// func +(lhs: Self, rhs: Self) -> Self
// func -(lhs: Self, rhs: Self) -> Self
// func *(lhs: Self, rhs: Self) -> Self
// func /(lhs: Self, rhs: Self) -> Self
init(_ other: Double)
var double: Double { get }
}
Now lets update your extensions to Double, Float and Int.
You don't need to add the initializer we declared above since these types already have it.
extension Int: TemperatureConvertible {
public var double: Double { return Double(self) }
}
extension Double: TemperatureConvertible {
public var double: Double { return self }
}
extension Float: TemperatureConvertible {
public var double: Double { return Double(self) }
}
Step 2
Now you can rewrite your functions like this
public func fahrenheitToCelsius<T: TemperatureConvertible>(fahrenheit: T) -> T {
// (°F − 32) ÷ 1.8 =°C
let celsius = (fahrenheit.double - 32.0) / 1.8
return T(celsius)
}
public func celsiusToFahrenheit<T: TemperatureConvertible>(celsius: T) -> T {
// (°C × 1.8) + 32 =°F
let fahrenheit = (celsius.double * 1.8) + 32.0
return T(fahrenheit)
}
Test
let celsius: Int = 20
let fahrenheit = celsiusToFahrenheit(celsius)
// ^ it's an Int
Tip
For readability and consistency with the Cocoa framework I suggest you to rename your functions like this
public func fahrenheitFromCelsius<T: TemperatureConvertible>(_ celsius: T) -> T
public func celsiusFromFahrenheit<T: TemperatureConvertible>(_ fahrenheit: T) -> T