diff --git a/Package.swift b/Package.swift index af6b28a4..2047dc0d 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version: 6.0 +// swift-tools-version: 6.2 // The swift-tools-version declares the minimum version of Swift required to build this package. // Remember to update CI if changing @@ -318,6 +318,6 @@ if enableDocCPlugin { // Set the minimum macOS version for the package #if canImport(Darwin) package.platforms = [ - .macOS(.v15), // Constrained by UInt128 support + .macOS(.v26), // Constrained by UInt128 support ] #endif diff --git a/Sources/HomomorphicEncryption/Bfv/Bfv+Encrypt.swift b/Sources/HomomorphicEncryption/Bfv/Bfv+Encrypt.swift index b93359f5..25f03ff6 100644 --- a/Sources/HomomorphicEncryption/Bfv/Bfv+Encrypt.swift +++ b/Sources/HomomorphicEncryption/Bfv/Bfv+Encrypt.swift @@ -1,4 +1,4 @@ -// Copyright 2024 Apple Inc. and the Swift Homomorphic Encryption project authors +// Copyright 2024-2025 Apple Inc. and the Swift Homomorphic Encryption project authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -85,61 +85,57 @@ extension Bfv { let plaintextIndices = plaintext.poly.polyIndices(rnsIndex: 0) var adjust = plaintext - // swiftlint:disable:next closure_body_length - plaintext.poly.data.data.withUnsafeBufferPointer { plaintextData in - adjust.poly.data.data.withUnsafeMutableBufferPointer { adjustPtr in - let t = rnsTool.t.modulus - if T.DoubleWidth(t.multipliedFullWidth(by: t)) <= T.max { - // Prefer single-word division, since it's faster - for index in plaintextIndices { - var adjustCoeff = rnsTool.qModT &* plaintextData[index] - adjustCoeff &+= rnsTool.tThreshold - adjustPtr[index] = adjustCoeff.dividingFloor(by: rnsTool.t) - } - } else { - let tThreshold = Scalar.DoubleWidth(rnsTool.tThreshold) - for index in plaintextIndices { - var adjustCoeff = Scalar - .DoubleWidth(rnsTool.qModT.multipliedFullWidth(by: plaintextData[index])) - adjustCoeff &+= tThreshold - adjustPtr[index] = adjustCoeff.dividingFloor(by: rnsTool.t).low - } - } + + let plaintextData = plaintext.poly.data.data.span + var adjustSpan = adjust.poly.data.data.mutableSpan + let t = rnsTool.t.modulus + if T.DoubleWidth(t.multipliedFullWidth(by: t)) <= T.max { + // Prefer single-word division, since it's faster + for index in plaintextIndices { + var adjustCoeff = rnsTool.qModT &* plaintextData[index] + adjustCoeff &+= rnsTool.tThreshold + adjustSpan[index] = adjustCoeff.dividingFloor(by: rnsTool.t) + } + } else { + let tThreshold = Scalar.DoubleWidth(rnsTool.tThreshold) + for index in plaintextIndices { + var adjustCoeff = Scalar + .DoubleWidth(rnsTool.qModT.multipliedFullWidth(by: plaintextData[index])) + adjustCoeff &+= tThreshold + adjustSpan[index] = adjustCoeff.dividingFloor(by: rnsTool.t).low } + } - var c0 = ciphertext.polys[0] - let c1 = ciphertext.polys[1] // used for polynomial index computation only - - switch op { - case PlaintextTranslateOp.Add: - c0.data.data.withUnsafeMutableBufferPointer { c0Ptr in - for (rnsIndex, rnsDelta) in rnsTool.qDivT.enumerated() { - for (plainIndex, cipherIndex) in c1.polyIndices(rnsIndex: rnsIndex).enumerated() { - let plainTimesDelta = rnsDelta.multiplyMod(plaintextData[plainIndex]) - let roundQTimesMt = plainTimesDelta.addMod( - adjust[plainIndex], - modulus: rnsDelta.modulus) - c0Ptr[cipherIndex] = c0Ptr[cipherIndex].addMod(roundQTimesMt, modulus: rnsDelta.modulus) - } - } + var c0 = ciphertext.polys[0] + let c1 = ciphertext.polys[1] // used for polynomial index computation only + + switch op { + case PlaintextTranslateOp.Add: + var c0Span = c0.data.data.mutableSpan + for (rnsIndex, rnsDelta) in rnsTool.qDivT.enumerated() { + for (plainIndex, cipherIndex) in c1.polyIndices(rnsIndex: rnsIndex).enumerated() { + let plainTimesDelta = rnsDelta.multiplyMod(plaintextData[plainIndex]) + let roundQTimesMt = plainTimesDelta.addMod( + adjust[plainIndex], + modulus: rnsDelta.modulus) + c0Span[cipherIndex] = c0Span[cipherIndex].addMod(roundQTimesMt, modulus: rnsDelta.modulus) } - case PlaintextTranslateOp.Subtract: - c0.data.data.withUnsafeMutableBufferPointer { c0Ptr in - for (rnsIndex, rnsDelta) in rnsTool.qDivT.enumerated() { - for (plainIndex, cipherIndex) in c1.polyIndices(rnsIndex: rnsIndex).enumerated() { - let plainTimesDelta = rnsDelta.multiplyMod(plaintextData[plainIndex]) - let roundQTimesMt = plainTimesDelta.addMod( - adjust[plainIndex], - modulus: rnsDelta.modulus) - c0Ptr[cipherIndex] = c0Ptr[cipherIndex].subtractMod( - roundQTimesMt, - modulus: rnsDelta.modulus) - } - } + } + case PlaintextTranslateOp.Subtract: + var c0Span = c0.data.data.mutableSpan + for (rnsIndex, rnsDelta) in rnsTool.qDivT.enumerated() { + for (plainIndex, cipherIndex) in c1.polyIndices(rnsIndex: rnsIndex).enumerated() { + let plainTimesDelta = rnsDelta.multiplyMod(plaintextData[plainIndex]) + let roundQTimesMt = plainTimesDelta.addMod( + adjust[plainIndex], + modulus: rnsDelta.modulus) + c0Span[cipherIndex] = c0Span[cipherIndex].subtractMod( + roundQTimesMt, + modulus: rnsDelta.modulus) } } - ciphertext.polys[0] = c0 } + ciphertext.polys[0] = c0 } @inlinable diff --git a/Sources/HomomorphicEncryption/Bfv/Bfv+Keys.swift b/Sources/HomomorphicEncryption/Bfv/Bfv+Keys.swift index 49c05ac7..a24dd11a 100644 --- a/Sources/HomomorphicEncryption/Bfv/Bfv+Keys.swift +++ b/Sources/HomomorphicEncryption/Bfv/Bfv+Keys.swift @@ -1,4 +1,4 @@ -// Copyright 2024 Apple Inc. and the Swift Homomorphic Encryption project authors +// Copyright 2024-2025 Apple Inc. and the Swift Homomorphic Encryption project authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -178,25 +178,23 @@ extension Bfv { for (index, poly) in keyCiphers[decomposeIndex].polys.enumerated() { let accIndex = poly.data.index(row: index, column: 0) let polyIndex = poly.data.index(row: keyIndex, column: 0) - poly.data.data.withUnsafeBufferPointer { polyPtr in - for columnIndex in 0..: HeScheme { { let poly = result.polys[0] for (polyIndex, accumulatorPoly) in accumulator.enumerated() { - accumulatorPoly.data.withUnsafeBufferPointer { accumulatorPolyPtr in - result.polys[polyIndex].data.data.withUnsafeMutableBufferPointer { resultPtr in - for (rnsIndex, modulus) in poly.polyContext().reduceModuli.enumerated() { - for index in poly.polyIndices(rnsIndex: rnsIndex) { - resultPtr[index] = modulus.reduce(accumulatorPolyPtr[index]) - } - } + let accumulatorPolySpan = accumulatorPoly.data.span + var resultSpan = result.polys[polyIndex].data.data.mutableSpan + for (rnsIndex, modulus) in poly.polyContext().reduceModuli.enumerated() { + for index in poly.polyIndices(rnsIndex: rnsIndex) { + resultSpan[index] = modulus.reduce(accumulatorPolySpan[index]) } } } diff --git a/Sources/HomomorphicEncryption/PolyRq/Galois.swift b/Sources/HomomorphicEncryption/PolyRq/Galois.swift index 0d5a6dbf..cd09b2c9 100644 --- a/Sources/HomomorphicEncryption/PolyRq/Galois.swift +++ b/Sources/HomomorphicEncryption/PolyRq/Galois.swift @@ -1,4 +1,4 @@ -// Copyright 2024 Apple Inc. and the Swift Homomorphic Encryption project authors +// Copyright 2024-2025 Apple Inc. and the Swift Homomorphic Encryption project authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -121,19 +121,17 @@ extension PolyRq where F == Coeff { func outputIndex(column: Int) -> Int { data.index(row: rnsIndex, column: column) } - data.data.withUnsafeBufferPointer { dataPtr in - output.data.data.withUnsafeMutableBufferPointer { outputPtr in - for dataIndex in dataIndices { - guard let (negate, outIndex) = iterator.next() else { - preconditionFailure("GaloisCoeffIterator goes out of index") - } - if negate { - outputPtr[outputIndex(column: outIndex)] = dataPtr[dataIndex] - .negateMod(modulus: modulus) - } else { - outputPtr[outputIndex(column: outIndex)] = dataPtr[dataIndex] - } - } + let dataSpan = data.data.span + var outputSpan = output.data.data.mutableSpan + for dataIndex in dataIndices { + guard let (negate, outIndex) = iterator.next() else { + preconditionFailure("GaloisCoeffIterator goes out of index") + } + if negate { + outputSpan[outputIndex(column: outIndex)] = dataSpan[dataIndex] + .negateMod(modulus: modulus) + } else { + outputSpan[outputIndex(column: outIndex)] = dataSpan[dataIndex] } } } diff --git a/Sources/HomomorphicEncryption/PolyRq/PolyRq.swift b/Sources/HomomorphicEncryption/PolyRq/PolyRq.swift index 05f253ae..3d926475 100644 --- a/Sources/HomomorphicEncryption/PolyRq/PolyRq.swift +++ b/Sources/HomomorphicEncryption/PolyRq/PolyRq.swift @@ -1,4 +1,4 @@ -// Copyright 2024 Apple Inc. and the Swift Homomorphic Encryption project authors +// Copyright 2024-2025 Apple Inc. and the Swift Homomorphic Encryption project authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -147,13 +147,11 @@ extension PolyRq { public static func += (_ lhs: inout Self, _ rhs: Self) { lhs.validateMetadataEquality(with: rhs) - lhs.data.data.withUnsafeMutableBufferPointer { lhsData in - rhs.data.data.withUnsafeBufferPointer { rhsData in - for (rnsIndex, modulus) in rhs.moduli.enumerated() { - for index in rhs.polyIndices(rnsIndex: rnsIndex) { - lhsData[index] = lhsData[index].addMod(rhsData[index], modulus: modulus) - } - } + var lhsData = lhs.data.data.mutableSpan + var rhsData = rhs.data.data.span + for (rnsIndex, modulus) in rhs.moduli.enumerated() { + for index in rhs.polyIndices(rnsIndex: rnsIndex) { + lhsData[index] = lhsData[index].addMod(rhsData[index], modulus: modulus) } } } @@ -166,13 +164,11 @@ extension PolyRq { public static func -= (_ lhs: inout Self, _ rhs: Self) { lhs.validateMetadataEquality(with: rhs) - lhs.data.data.withUnsafeMutableBufferPointer { lhsData in - rhs.data.data.withUnsafeBufferPointer { rhsData in - for (rnsIndex, modulus) in rhs.moduli.enumerated() { - for index in rhs.polyIndices(rnsIndex: rnsIndex) { - lhsData[index] = lhsData[index].subtractMod(rhsData[index], modulus: modulus) - } - } + var lhsData = lhs.data.data.mutableSpan + let rhsData = rhs.data.data.span + for (rnsIndex, modulus) in rhs.moduli.enumerated() { + for index in rhs.polyIndices(rnsIndex: rnsIndex) { + lhsData[index] = lhsData[index].subtractMod(rhsData[index], modulus: modulus) } } } @@ -188,13 +184,11 @@ extension PolyRq { static func mulAssign(_ lhs: inout Self, secretPoly: borrowing Self) where F == Eval { let context = lhs.context assert(secretPoly.context.isParentOfOrEqual(to: context)) - lhs.data.data.withUnsafeMutableBufferPointer { lhsData in - secretPoly.data.data.withUnsafeBufferPointer { rhsData in - for (rnsIndex, modulus) in context.reduceModuli.enumerated() { - for index in secretPoly.polyIndices(rnsIndex: rnsIndex) { - lhsData[index] = modulus.multiplyMod(lhsData[index], rhsData[index]) - } - } + var lhsData = lhs.data.data.mutableSpan + let rhsData = secretPoly.data.data.span + for (rnsIndex, modulus) in context.reduceModuli.enumerated() { + for index in secretPoly.polyIndices(rnsIndex: rnsIndex) { + lhsData[index] = modulus.multiplyMod(lhsData[index], rhsData[index]) } } } @@ -219,16 +213,13 @@ extension PolyRq { precondition(accumulator.shape == rhs.data.shape) lhs.validateMetadataEquality(with: rhs) - lhs.data.data.withUnsafeBufferPointer { lhsData in - rhs.data.data.withUnsafeBufferPointer { rhsData in - accumulator.data.withUnsafeMutableBufferPointer { accumulatorPtr in - for rnsIndex in rhs.moduli.indices { - for index in rhs.polyIndices(rnsIndex: rnsIndex) { - accumulatorPtr[index] &+= - T.DoubleWidth(lhsData[index].multipliedFullWidth(by: rhsData[index])) - } - } - } + let lhsData = lhs.data.data.span + let rhsData = rhs.data.data.span + var accumulatorSpan = accumulator.data.mutableSpan + for rnsIndex in rhs.moduli.indices { + for index in rhs.polyIndices(rnsIndex: rnsIndex) { + accumulatorSpan[index] &+= + T.DoubleWidth(lhsData[index].multipliedFullWidth(by: rhsData[index])) } } } @@ -246,10 +237,9 @@ extension PolyRq { multiplicand: rhsResidue, divisionModulus: modulus.divisionModulus) let polyIndices = poly.polyIndices(rnsIndex: rnsIndex) - poly.data.data.withUnsafeMutableBufferPointer { lhsData in - for index in polyIndices { - lhsData[index] = multiplicationModulus.multiplyMod(lhsData[index]) - } + var lhsData = poly.data.data.mutableSpan + for index in polyIndices { + lhsData[index] = multiplicationModulus.multiplyMod(lhsData[index]) } } } diff --git a/Sources/HomomorphicEncryption/RnsBaseConverter.swift b/Sources/HomomorphicEncryption/RnsBaseConverter.swift index 74b032b9..b2583500 100644 --- a/Sources/HomomorphicEncryption/RnsBaseConverter.swift +++ b/Sources/HomomorphicEncryption/RnsBaseConverter.swift @@ -1,4 +1,4 @@ -// Copyright 2024 Apple Inc. and the Swift Homomorphic Encryption project authors +// Copyright 2024-2025 Apple Inc. and the Swift Homomorphic Encryption project authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -99,10 +99,9 @@ struct RnsBaseConverter: Sendable { assert(poly.context == inputContext) for (rnsIndex, puncturedProduct) in inversePuncturedProducts.enumerated() { let indices = poly.polyIndices(rnsIndex: rnsIndex) - poly.data.data.withUnsafeMutableBufferPointer { dataPtr in - for index in indices { - dataPtr[index] = puncturedProduct.multiplyMod(dataPtr[index]) - } + var dataSpan = poly.data.data.mutableSpan + for index in indices { + dataSpan[index] = puncturedProduct.multiplyMod(dataSpan[index]) } } } @@ -118,26 +117,25 @@ struct RnsBaseConverter: Sendable { @inlinable func convertApproximate(using products: PolyRq) -> PolyRq { var result = PolyRq.zero(context: outputContext) - result.data.data.withUnsafeMutableBufferPointer { resultPtr in - for (rnsOutIndex, tj) in outputContext.reduceModuli.enumerated() { - let puncturedProductColumnIndices = puncturedProducts.rowIndices(row: rnsOutIndex) - var sums = Array(repeating: T.DoubleWidth(0), count: outputContext.degree) - var productsIndex = 0 - for (rnsInIndex, puncturedProdIdx) in puncturedProductColumnIndices.enumerated() { - let puncturedProd = puncturedProducts[puncturedProdIdx] - if rnsInIndex == inputContext.moduli.count &- 1 { - for (coeffIndex, outIndex) in products.polyIndices(rnsIndex: rnsOutIndex).enumerated() { - sums[coeffIndex] &+= - T.DoubleWidth(products.data[productsIndex].multipliedFullWidth(by: puncturedProd)) - resultPtr[outIndex] = tj.reduce(sums[coeffIndex]) - productsIndex &+= 1 - } - } else { - for coeffIndex in 0..: Sendable { var polyModQ = poly try polyModQ.dropContext(to: inputContext) var output = try rnsConvertQToBSk.convertApproximate(poly: polyModQ) - polyModBSk.withUnsafeBufferPointer { polyModBSkPtr in - for (rnsIndex, inverseQModBSk) in inverseQModBSk.enumerated() { - let bSk = rnsConvertQToBSk.outputContext.moduli[rnsIndex] - let outputIndices = output.polyIndices(rnsIndex: rnsIndex) - output.data.data.withUnsafeMutableBufferPointer { outputPtr in - for polyIndex in outputIndices { - let inputCoeff = polyModBSkPtr[polyIndex] - let outputCoeff = outputPtr[polyIndex] - outputPtr[polyIndex] = inverseQModBSk.multiplyMod(inputCoeff &+ bSk &- outputCoeff) - } - } + let polyModBSkSpan = polyModBSk.span + for (rnsIndex, inverseQModBSk) in inverseQModBSk.enumerated() { + let bSk = rnsConvertQToBSk.outputContext.moduli[rnsIndex] + let outputIndices = output.polyIndices(rnsIndex: rnsIndex) + var outputSpan = output.data.data.mutableSpan + for polyIndex in outputIndices { + let inputCoeff = polyModBSkSpan[polyIndex] + let outputCoeff = outputSpan[polyIndex] + outputSpan[polyIndex] = inverseQModBSk.multiplyMod(inputCoeff &+ bSk &- outputCoeff) } } return output @@ -422,34 +420,29 @@ package struct RnsTool: Sendable { let coeffIndices = alphaSk.coeffIndices var alphaExceedsThreshold = [T]() alphaExceedsThreshold.reserveCapacity(coeffIndices.count) - alphaSk.data.data.withUnsafeMutableBufferPointer { alphaSkPtr in - for coeffIndex in coeffIndices { - let polyModMSkCoeff = polyModMSk[coeffIndex] - var alphaSk = alphaSkPtr[coeffIndex] - alphaSk = inverseBModMSk - .multiplyMod(alphaSk &+ inverseBModMSk.modulus &- polyModMSkCoeff) - alphaSkPtr[coeffIndex] = alphaSk - alphaExceedsThreshold.append(alphaSk.constantTimeGreaterThan(mSkThreshold)) - } + var alphaSkSpan = alphaSk.data.data.mutableSpan + for coeffIndex in coeffIndices { + let polyModMSkCoeff = polyModMSk[coeffIndex] + var alphaSk = alphaSkSpan[coeffIndex] + alphaSk = inverseBModMSk + .multiplyMod(alphaSk &+ inverseBModMSk.modulus &- polyModMSkCoeff) + alphaSkSpan[coeffIndex] = alphaSk + alphaExceedsThreshold.append(alphaSk.constantTimeGreaterThan(mSkThreshold)) } var output = rnsConvertBtoQ.convertApproximate(using: polyModB) - output.data.data.withUnsafeMutableBufferPointer { outputPtr in - alphaSk.data.data.withUnsafeBufferPointer { alphaSkPtr in - alphaExceedsThreshold.withUnsafeBufferPointer { alphaExceedsThresholdPtr in - for (rnsIndex, (qi, (bModQi, negBModQi))) in zip(inputContext.moduli, zip(bModQ, negBModQ)) - .enumerated() - { - for (coeffIndex, outputIndex) in polyModB.polyIndices(rnsIndex: rnsIndex).enumerated() { - // Center alphaSk before Shenoy-Kumeresan conversion - let adjust = T.constantTimeSelect( - if: alphaExceedsThresholdPtr[coeffIndex], - then: bModQi.multiplyMod(mSk &- alphaSkPtr[coeffIndex]), - else: negBModQi.multiplyMod(alphaSkPtr[coeffIndex])) - outputPtr[outputIndex] = outputPtr[outputIndex].addMod(adjust, modulus: qi) - } - } - } + var outputSpan = output.data.data.mutableSpan + let alphaExceedsThresholdSpan = alphaExceedsThreshold.span + for (rnsIndex, (qi, (bModQi, negBModQi))) in zip(inputContext.moduli, zip(bModQ, negBModQ)) + .enumerated() + { + for (coeffIndex, outputIndex) in polyModB.polyIndices(rnsIndex: rnsIndex).enumerated() { + // Center alphaSk before Shenoy-Kumeresan conversion + let adjust = T.constantTimeSelect( + if: alphaExceedsThresholdSpan[coeffIndex], + then: bModQi.multiplyMod(mSk &- alphaSkSpan[coeffIndex]), + else: negBModQi.multiplyMod(alphaSkSpan[coeffIndex])) + outputSpan[outputIndex] = outputSpan[outputIndex].addMod(adjust, modulus: qi) } } diff --git a/Sources/ModularArithmetic/Modulus.swift b/Sources/ModularArithmetic/Modulus.swift index 64689168..bafc8612 100644 --- a/Sources/ModularArithmetic/Modulus.swift +++ b/Sources/ModularArithmetic/Modulus.swift @@ -430,13 +430,11 @@ public struct MultiplyConstantArrayModulus: Equatable, Sendab @inlinable public subscript(index: Int) -> MultiplyConstantModulus { - factors.withUnsafeBufferPointer { factorPtr in - multiplicands.withUnsafeBufferPointer { multiplicandsPtr in - MultiplyConstantModulus( - multiplicand: multiplicandsPtr[index], - modulus: modulus, - factor: factorPtr[index]) - } - } + let factorSpan = factors.span + let multiplicandsSpan = multiplicands.span + return MultiplyConstantModulus( + multiplicand: multiplicandsSpan[index], + modulus: modulus, + factor: factorSpan[index]) } }