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.
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.