Search code examples
swiftgenericscomplex-numbers

Swift generics for Real and Complex that allow isApproximatelyEqual


I have the following code working but find it rather lacking in elegance to access isAppromatelyEqual for both FloatingPoint types and Complex<T> types.

func aboutEquals<T: AlgebraicField>(_ d1: T, _ d2: T) -> Bool  {
    //return d1.isApproximatelyEqual(to: d2)
    if d1 is Complex<Double> { return (d1 as! Complex<Double>).isApproximatelyEqual(to: d2 as! Complex<Double>) }
    if d1 is Complex<Float> { return (d1 as! Complex<Float>).isApproximatelyEqual(to: d2 as! Complex<Float>)}
    if d1 is Complex<Float80> { return(d1 as! Complex<Float80>).isApproximatelyEqual(to: d2 as! Complex<Float80>)}
    if d1 is Float { return (d1 as! Float).isApproximatelyEqual(to: d2 as! Float)}
    if d1 is Double { return (d1 as! Double).isApproximatelyEqual(to: d2 as! Double)}
    if d1 is Float80 { return (d1 as! Float80).isApproximatelyEqual(to: d2 as! Float80)}
    return true
}

The first line of the func, which is commented out, //return ... gives an error that T.Magnitude must conform to FloatingPoint. If I add a "where T.Magnitude : FloatingPoint" then the calling function is flagged that the variables calling the the func must have T.Magnitude conforming to FloatingPoint. I use a generic function with T: AlgebraicField to call the global func above with Complex or Real values. How do I tell the compiler to do all these conversions without enumerating them as above.


Solution

  • I created a protocol that allows Real and Complex called RealOrComplex, to make it easy to create generic with no need for a where on every struct that wants this capability. It is as follows:

    import Numerics
    // Set up protocol for Real and Complex<Real> types
    protocol RealOrComplex : AlgebraicField where Magnitude : Real {}
    // and enumerate types that fit it and that you want to use
    extension Complex : RealOrComplex {}
    extension Float   : RealOrComplex {}
    extension Double  : RealOrComplex {}
    

    Its great for making numerical functions and structs generic.