diff --git a/Sources/PropertyBased/Gen+SIMD.swift b/Sources/PropertyBased/Gen+SIMD.swift index e63b6a8..22d8c7e 100644 --- a/Sources/PropertyBased/Gen+SIMD.swift +++ b/Sources/PropertyBased/Gen+SIMD.swift @@ -103,6 +103,35 @@ extension Gen> { } } +#if !((os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64)) +@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension Gen> { + /// A generator of vectors with length 1. + public static var unitVector: Generator, Shrink.None<(Float16, Float16)>> { + let gen = Gen.float16(in: 0...1) + return gen.simd2.map { normalize($0) }.filter { $0.x.isFinite }.withoutShrink() + } +} + +@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension Gen> { + /// A generator of vectors with length 1. + public static var unitVector: Generator, Shrink.None<(Float16, Float16, Float16)>> { + let gen = Gen.float16(in: 0...1) + return gen.simd3.map { normalize($0) }.filter { $0.x.isFinite }.withoutShrink() + } +} + +@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension Gen> { + /// A generator of vectors with length 1. + public static var unitVector: Generator, Shrink.None<(Float16, Float16, Float16, Float16)>> { + let gen = Gen.float16(in: 0...1) + return gen.simd4.map { normalize($0) }.filter { $0.x.isFinite }.withoutShrink() + } +} +#endif + #if canImport(simd) extension Gen where Value == simd_quatf { /// A generator of rotation quaternions with length 1 and a random angle. @@ -121,6 +150,19 @@ extension Gen where Value == simd_quatd { return zip(angle, vector).map { t in simd.simd_quatd(angle: t.0, axis: t.1) } } } + +#if !((os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64)) +@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) +extension Gen where Value == simd_quath { + /// A generator of rotation quaternions with length 1 and a random angle. + public static var simd_quath: Generator> { + return Gen.simd_quatf.map { q in + simd.simd_quath(vector: simd_half4(q.vector)) + } + } +} +#endif + #endif @_disfavoredOverload diff --git a/Tests/PropertyBasedTests/GenTests+SIMD.swift b/Tests/PropertyBasedTests/GenTests+SIMD.swift index e26bcb7..daca3f9 100644 --- a/Tests/PropertyBasedTests/GenTests+SIMD.swift +++ b/Tests/PropertyBasedTests/GenTests+SIMD.swift @@ -74,6 +74,21 @@ import simd await unitVectorCheck(Gen>.unitVector) } + #if !((os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64)) + @available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) + @Test func testUnitVectorHalf2() async { + await unitVectorCheck(Gen>.unitVector) + } + @available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) + @Test func testUnitVectorHalf3() async { + await unitVectorCheck(Gen>.unitVector) + } + @available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) + @Test func testUnitVectorHalf4() async { + await unitVectorCheck(Gen>.unitVector) + } + #endif + @Test func testVectorLength() { #expect(!length([2, 0] as SIMD2).isApproximately(1)) #expect(length([3, 0] as SIMD2).isApproximately(3)) @@ -96,11 +111,23 @@ import simd #expect(quat.length.isApproximately(1)) } } + + #if !((os(macOS) || targetEnvironment(macCatalyst)) && arch(x86_64)) + @available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) + @Test func testQuatH() async { + await testGen(Gen.simd_quath.map(\.vector)) + await propertyCheck(input: Gen.simd_quath) { quat in + let cast = simd_quatf(vector: .init(quat.vector)) + #expect(cast.length.isApproximately(1)) + } + } + #endif + #endif } extension FloatingPoint where Self: ExpressibleByFloatLiteral { - func isApproximately(_ other: Self, tolerance: Self = 0.001) -> Bool { + func isApproximately(_ other: Self, tolerance: Self = 0.01) -> Bool { return abs(self - other) < tolerance } }