Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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
96 changes: 46 additions & 50 deletions Sources/HomomorphicEncryption/Bfv/Bfv+Encrypt.swift
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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
Expand Down
26 changes: 12 additions & 14 deletions Sources/HomomorphicEncryption/Bfv/Bfv+Keys.swift
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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..<degree {
let prod = bufferSlice[columnIndex]
.multipliedFullWidth(by: polyPtr[polyIndex &+ columnIndex])
// Overflow avoided by `maxLazyProductAccumulationCount()` check during context
// initialization
accumulator[accIndex &+ columnIndex] &+= T.DoubleWidth(prod)
}
let polySpan = poly.data.data.span
for columnIndex in 0..<degree {
let prod = bufferSlice[columnIndex]
.multipliedFullWidth(by: polySpan[polyIndex &+ columnIndex])
// Overflow avoided by `maxLazyProductAccumulationCount()` check during context
// initialization
accumulator[accIndex &+ columnIndex] &+= T.DoubleWidth(prod)
}
}
}
let prodIndex = ciphertextProd.polys[0].data.index(row: rnsIndex, column: 0)
for rowIndex in ciphertextProd.polys.indices {
let accIndex = accumulator.index(row: rowIndex, column: 0)
ciphertextProd.polys[rowIndex].data.data.withUnsafeMutableBufferPointer { ciphertextProdPtr in
for columnIndex in 0..<degree {
ciphertextProdPtr[prodIndex &+ columnIndex] = keyModulus
.reduce(accumulator[accIndex &+ columnIndex])
}
var ciphertextProdSpan = ciphertextProd.polys[rowIndex].data.data.mutableSpan
for columnIndex in 0..<degree {
ciphertextProdSpan[prodIndex &+ columnIndex] = keyModulus
.reduce(accumulator[accIndex &+ columnIndex])
}
}
}
Expand Down
12 changes: 5 additions & 7 deletions Sources/HomomorphicEncryption/Bfv/Bfv.swift
Original file line number Diff line number Diff line change
Expand Up @@ -283,13 +283,11 @@ public enum Bfv<T: ScalarType>: 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])
}
}
}
Expand Down
26 changes: 12 additions & 14 deletions Sources/HomomorphicEncryption/PolyRq/Galois.swift
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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]
}
}
}
Expand Down
62 changes: 26 additions & 36 deletions Sources/HomomorphicEncryption/PolyRq/PolyRq.swift
Original file line number Diff line number Diff line change
@@ -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.
Expand Down Expand Up @@ -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)
}
}
}
Expand All @@ -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)
}
}
}
Expand All @@ -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])
}
}
}
Expand All @@ -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]))
}
}
}
Expand All @@ -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])
}
}
}
Expand Down
Loading
Loading