1212// See the License for the specific language governing permissions and
1313// limitations under the License.
1414
15+ public import AsyncAlgorithms
16+
1517/// Ciphertext type.
1618public struct Ciphertext < Scheme: HeScheme , Format: PolyFormat > : Equatable , Sendable {
1719 public typealias Scalar = Scheme . Scalar
@@ -28,7 +30,7 @@ public struct Ciphertext<Scheme: HeScheme, Format: PolyFormat>: Equatable, Senda
2830 ///
2931 /// After a fresh encryption, the ciphertext has ``HeScheme/freshCiphertextPolyCount`` polynomials.
3032 /// The count may change during the course of HE operations, e.g. increase during ciphertext multiplication,
31- /// or decrease during relinearization ``Ciphertext/relinearize(using:)``.
33+ /// or decrease during relinearization ``Ciphertext/relinearize(using:)-41bsm ``.
3234 public var polyCount : Int {
3335 polys. count
3436 }
@@ -314,7 +316,7 @@ public struct Ciphertext<Scheme: HeScheme, Format: PolyFormat>: Equatable, Senda
314316 ///
315317 /// If the ciphertext already has a single modulus, this is a no-op.
316318 /// - Throws: Error upon failure to modulus switch.
317- /// - seealso: ``Ciphertext/modSwitchDown()`` for more information and an alternative API.
319+ /// - seealso: ``Ciphertext/modSwitchDown()-4an2b `` for more information and an alternative API.
318320 @inlinable
319321 public mutating func modSwitchDownToSingle( ) throws where Format == Scheme . CanonicalCiphertextFormat {
320322 try Scheme . modSwitchDownToSingle ( & self )
@@ -535,22 +537,31 @@ extension Ciphertext where Format == Scheme.CanonicalCiphertextFormat {
535537}
536538
537539extension Collection {
540+ /// Sums together the ciphertexts in the collection.
541+ /// - Throws: Precondition failure if the collection is empty.
542+ /// - Returns: The sum.
538543 @inlinable
539- func sum< Scheme> ( ) throws -> Element where Element == Ciphertext < Scheme , Eval > {
544+ public func sum< Scheme> ( ) throws -> Element where Element == Ciphertext < Scheme , Eval > {
540545 precondition ( !isEmpty)
541546 // swiftlint:disable:next force_unwrapping
542547 return try dropFirst ( ) . reduce ( first!) { try $0 + $1 }
543548 }
544549
550+ /// Sums together the ciphertexts in the collection.
551+ /// - Throws: Precondition failure if the collection is empty.
552+ /// - Returns: The sum.
545553 @inlinable
546- func sum< Scheme> ( ) throws -> Element where Element == Ciphertext < Scheme , Coeff > {
554+ public func sum< Scheme> ( ) throws -> Element where Element == Ciphertext < Scheme , Coeff > {
547555 precondition ( !isEmpty)
548556 // swiftlint:disable:next force_unwrapping
549557 return try dropFirst ( ) . reduce ( first!) { try $0 + $1 }
550558 }
551559
560+ /// Sums together the ciphertexts in the collection.
561+ /// - Throws: Precondition failure if the collection is empty.
562+ /// - Returns: The sum.
552563 @inlinable
553- func sum< Scheme> ( ) throws -> Element where Element == Ciphertext < Scheme , Scheme . CanonicalCiphertextFormat > {
564+ public func sum< Scheme> ( ) throws -> Element where Element == Ciphertext < Scheme , Scheme . CanonicalCiphertextFormat > {
554565 precondition ( !isEmpty)
555566 // swiftlint:disable:next force_unwrapping
556567 return try dropFirst ( ) . reduce ( first!) { try $0 + $1 }
@@ -937,4 +948,167 @@ extension Ciphertext {
937948 }
938949 throw HeError . errorCastingPolyFormat ( from: Format . self, to: Scheme . CanonicalCiphertextFormat. self)
939950 }
951+
952+ // MARK: Async rotations
953+
954+ /// Asynchronously rotates the columns of a ciphertext.
955+ ///
956+ /// - Parameters:
957+ /// - step: Number of slots to rotate. Negative values indicate a left rotation, and positive values indicate a
958+ /// right rotation. Must have absolute value in `[1, N / 2 - 1]` where `N` is the RLWE ring dimension, given by
959+ /// ``EncryptionParameters/polyDegree``.
960+ /// - evaluationKey: Evaluation key to use in the HE computation. Must contain the Galois element associated with
961+ /// `step`, see ``GaloisElement/rotatingColumns(by:degree:)``.
962+ /// - Throws: failure to rotate ciphertext's columns.
963+ /// - seealso: ``HeScheme/rotateColumns(of:by:using:)-7h3fz`` for an alternate API and more information.
964+ @inlinable
965+ public mutating func rotateColumns( by step: Int ,
966+ using evaluationKey: EvaluationKey < Scheme > ) async throws
967+ where Format == Scheme . CanonicalCiphertextFormat
968+ {
969+ try await Scheme . rotateColumnsAsync ( of: & self , by: step, using: evaluationKey)
970+ }
971+
972+ /// Asynchronously swaps the rows of a ciphertext.
973+ ///
974+ /// A plaintext in ``EncodeFormat/simd`` format can be viewed a `2 x (N / 2)` matrix of coefficients.
975+ /// For instance, for `N = 8`, given a ciphertext encrypting a plaintext with values
976+ /// ```
977+ /// [1, 2, 3, 4, 5, 6, 7, 8]
978+ /// ```
979+ /// calling ``HeScheme/swapRows(of:using:)`` with `step: 1` will yield a ciphertext decrypting to
980+ /// ```
981+ /// [5, 6, 7, 8, 1, 2, 3, 4]
982+ /// ```
983+ /// - Parameter evaluationKey: Evaluation key to use in the HE computation. Must contain the Galois element
984+ /// associated with `step`, see ``GaloisElement/rotatingColumns(by:degree:)``.
985+ /// - Throws: error upon failure to swap the ciphertext's rows.
986+ /// - seealso: ``HeScheme/swapRows(of:using:)-50tac`` for an alternate API.
987+ @inlinable
988+ public mutating func swapRows( using evaluationKey: EvaluationKey < Scheme > ) async throws
989+ where Format == Scheme . CanonicalCiphertextFormat
990+ {
991+ try await Scheme . swapRowsAsync ( of: & self , using: evaluationKey)
992+ }
993+
994+ /// Asynchronously performs modulus switching on the ciphertext.
995+ ///
996+ /// - Throws: Error upon failure to mod-switch.
997+ /// - seealso: ``HeScheme/modSwitchDown(_:)`` for an alternative API and more information.
998+ @inlinable
999+ public mutating func modSwitchDown( ) async throws where Format == Scheme . CanonicalCiphertextFormat {
1000+ try await Scheme . modSwitchDownAsync ( & self )
1001+ }
1002+
1003+ /// Asynchronously performs modulus switching to a single modulus.
1004+ ///
1005+ /// If the ciphertext already has a single modulus, this is a no-op.
1006+ /// - Throws: Error upon failure to modulus switch.
1007+ /// - seealso: ``Ciphertext/modSwitchDown()-4an2b`` for more information and an alternative API.
1008+ @inlinable
1009+ public mutating func modSwitchDownToSingle( ) async throws where Format == Scheme . CanonicalCiphertextFormat {
1010+ try await Scheme . modSwitchDownToSingleAsync ( & self )
1011+ }
1012+ }
1013+
1014+ extension Ciphertext where Format == Scheme . CanonicalCiphertextFormat {
1015+ /// Asynchronously applies a Galois transformation.
1016+ ///
1017+ /// - Parameters:
1018+ /// - element: Galois element of the transformation. Must be odd in `[1, 2 * N - 1]` where `N` is the RLWE ring
1019+ /// dimension, given by ``EncryptionParameters/polyDegree``.
1020+ /// - key: Evaluation key. Must contain Galois element `element`.
1021+ /// - Throws: Error upon failure to apply the Galois transformation.
1022+ /// - seealso: ``HeScheme/applyGalois(ciphertext:element:using:)`` for an alternative API and more information.
1023+ @inlinable
1024+ public mutating func applyGalois( element: Int , using key: EvaluationKey < Scheme > ) async throws {
1025+ try await Scheme . applyGaloisAsync ( ciphertext: & self , element: element, using: key)
1026+ }
1027+
1028+ /// Asynchronously Relinearizes the ciphertext.
1029+ ///
1030+ /// - Parameter key: Evaluation key to relinearize with. Must contain a `RelinearizationKey`.
1031+ /// - Throws: Error upon failure to relinearize.
1032+ /// - seealso: ``HeScheme/relinearize(_:using:)`` for an alternative API and more information.
1033+ @inlinable
1034+ public mutating func relinearize( using key: EvaluationKey < Scheme > ) async throws {
1035+ try await Scheme . relinearizeAsync ( & self , using: key)
1036+ }
1037+ }
1038+
1039+ // MARK: - Async collection extensions
1040+
1041+ extension Collection {
1042+ /// Sums together the ciphertexts in the collection.
1043+ /// - Throws: Precondition failure if the collection is empty.
1044+ /// - Returns: The sum.
1045+ @inlinable
1046+ public func sum< Scheme> ( ) async throws -> Element where Element == Ciphertext < Scheme , Eval > {
1047+ precondition ( !isEmpty)
1048+ // swiftlint:disable:next force_unwrapping
1049+ return try await dropFirst ( ) . async . reduce ( first!) { try await $0 + $1 }
1050+ }
1051+
1052+ /// Sums together the ciphertexts in the collection.
1053+ /// - Throws: Precondition failure if the collection is empty.
1054+ /// - Returns: The sum.
1055+ @inlinable
1056+ public func sum< Scheme> ( ) async throws -> Element where Element == Ciphertext < Scheme , Coeff > {
1057+ precondition ( !isEmpty)
1058+ // swiftlint:disable:next force_unwrapping
1059+ return try await dropFirst ( ) . async . reduce ( first!) { try await $0 + $1 }
1060+ }
1061+
1062+ /// Sums together the ciphertexts in the collection.
1063+ /// - Throws: Precondition failure if the collection is empty.
1064+ /// - Returns: The sum.
1065+ @inlinable
1066+ public func sum< Scheme> ( ) async throws -> Element where
1067+ Element == Ciphertext < Scheme , Scheme . CanonicalCiphertextFormat >
1068+ {
1069+ precondition ( !isEmpty)
1070+ // swiftlint:disable:next force_unwrapping
1071+ return try await dropFirst ( ) . async . reduce ( first!) { try await $0 + $1 }
1072+ }
1073+
1074+ /// Asynchronously computes an inner product between self and a collection of (optional) plaintexts in ``Eval``
1075+ /// format.
1076+ ///
1077+ /// The inner product encrypts `sum_{i, plaintexts[i] != nil} self[i] * plaintexts[i]`. `plaintexts[i]`
1078+ /// may be `nil`, which denotes a zero plaintext.
1079+ /// - Parameter plaintexts: Plaintexts. Must not be empty and have `count` matching `self.count`.
1080+ /// - Returns: A ciphertext encrypting the inner product.
1081+ /// - Throws: Error upon failure to compute inner product.
1082+ @inlinable
1083+ public func innerProduct< Scheme> ( plaintexts: some Collection < Plaintext < Scheme , Eval > ? > ) async throws -> Element
1084+ where Element == Ciphertext < Scheme , Eval >
1085+ {
1086+ try await Scheme . innerProductAsync ( ciphertexts: self , plaintexts: plaintexts)
1087+ }
1088+
1089+ /// Asynchronously computes an inner product between self and a collection of plaintexts in ``Eval`` format.
1090+ ///
1091+ /// The inner product encrypts `sum_{i} self[i] * plaintexts[i]`.
1092+ /// - Parameter plaintexts: Plaintexts. Must not be empty and have `count` matching `self.count`.
1093+ /// - Returns: A ciphertext encrypting the inner product.
1094+ /// - Throws: Error upon failure to compute inner product.
1095+ @inlinable
1096+ public func innerProduct< Scheme> ( plaintexts: some Collection < Plaintext < Scheme , Eval > > ) async throws -> Element
1097+ where Element == Ciphertext < Scheme , Eval >
1098+ {
1099+ try await Scheme . innerProductAsync ( ciphertexts: self , plaintexts: plaintexts)
1100+ }
1101+
1102+ /// Asynchronously computes an inner product between self and another collection of ciphertexts.
1103+ ///
1104+ /// The inner product encrypts `sum_{i} self[i] * ciphertexts[i]`.
1105+ /// - Parameter ciphertexts: Ciphertexts. Must not be empty and have `count` matching `self.count`.
1106+ /// - Returns: A ciphertext encrypting the inner product.
1107+ /// - Throws: Error upon failure to compute inner product.
1108+ @inlinable
1109+ public func innerProduct< Scheme> ( ciphertexts: some Collection < Element > ) async throws -> Element
1110+ where Element == Ciphertext < Scheme , Scheme . CanonicalCiphertextFormat >
1111+ {
1112+ try await Scheme . innerProductAsync ( self , ciphertexts)
1113+ }
9401114}
0 commit comments