Skip to main content

Builder

Struct Builder 

Source
pub struct Builder { /* private fields */ }
Expand description

High-level builder for constructing FHE circuits as IR graphs.

A Builder accumulates IR instructions through its methods, using interior mutability so that all operations take &self. The typical lifecycle is: create a builder, declare inputs, emit block-level or vector-level operations, declare outputs, and finally call into_ir to obtain the optimized IR.

Every builder is parameterized by a single CiphertextBlockSpec that defines the message/carry bit layout shared by all ciphertext blocks in the circuit. This spec is set at construction time and accessible via spec.

§Input / Output Ordering

Inputs and outputs are positional: they are recorded in the order they are declared. The first call to ciphertext_input or plaintext_input becomes input 0, the second becomes input 1, and so on — both kinds share the same index space. Likewise, the first ciphertext_output becomes output 0. This ordering defines the circuit’s signature and must match the order of values passed to eval.

§Comments

The builder maintains a comment stack that annotates IR instructions for debugging and readability. When the stack is non-empty, every emitted instruction is tagged with the full stack joined by /. Use with_comment for scoped annotations, or push_comment / pop_comment for manual control. Comments nest naturally: a comment pushed inside a with_comment closure appends to the existing stack.

§Examples

let builder = Builder::new(CiphertextBlockSpec(2, 2));
let input = builder.ciphertext_input(8);
let blocks = builder.ciphertext_split(&input);
// ... operate on blocks ...
let output = builder.ciphertext_join(&blocks, None);
builder.ciphertext_output(&output);
let ir = builder.into_ir();

Implementations§

Source§

impl Builder

Source

pub fn new(spec: CiphertextBlockSpec) -> Self

Creates a new builder with the given block specification.

The spec defines the message and carry bit sizes for every ciphertext block produced by this builder. The builder starts with an empty IR and no declared inputs or outputs.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
Source

pub fn into_ir(self) -> IR<IopLang>

Consumes the builder and returns the optimized IR graph.

Finalizes the circuit by running optimization passes — alias elimination, dead-code elimination, and common subexpression elimination — then returns the resulting IR. This is typically the last step after declaring all inputs, emitting operations, and declaring outputs.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let input = builder.ciphertext_input(8);
// ... build circuit ...
builder.ciphertext_output(&input);
let ir = builder.into_ir();
Source

pub fn spec(&self) -> &CiphertextBlockSpec

Returns the block specification shared by all ciphertext blocks in this circuit.

Source

pub fn signature(&self) -> Signature<Type>

Returns a clone of the circuit’s current I/O signature.

The signature records every input and output declared so far, in declaration order, as Type values.

Source

pub fn ir(&self) -> Ref<'_, IR<IopLang>>

Borrows the current (unoptimized) IR graph.

Unlike into_ir, this does not consume the builder and does not apply any optimization passes. Useful for debugging and inspection mid-construction.

Source

pub fn dump_and_panic(&self) -> !

Prints the current IR to stdout and panics.

This is a debugging helper intended for use during circuit development. It dumps a human-readable representation of the unoptimized IR graph, then unconditionally panics to halt execution.

§Panics

Always panics after printing.

Source

pub fn dump_eval_and_panic(&self, inputs: impl AsRef<[IopValue]>) -> !

Interprets the current IR with the given inputs, prints the annotated result, and panics.

Like dump_and_panic, but first runs the IR interpreter so each node is annotated with its computed value. The inputs slice must match the declared input signature in order and length. The ciphertext spec used for interpretation is inferred from the maximum int_size among the ciphertext inputs.

§Panics

Always panics after printing, regardless of whether interpretation succeeded.

Source

pub fn eval(&self, inputs: impl AsRef<[IopValue]>) -> Vec<IopValue>

Interprets the current IR with the given inputs and returns the output values.

Runs the IR interpreter on the unoptimized graph with the provided inputs, which must match the declared input signature in order and length. Returns the computed output values in declaration order. The ciphertext spec used for interpretation is inferred from the maximum int_size among the ciphertext inputs.

This is useful for validating circuit correctness without running actual FHE operations. Construct input values with the make_value methods on the handle types.

§Panics

Panics if interpretation fails (e.g. due to a malformed graph).

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let a = builder.ciphertext_input(8);
let b = builder.ciphertext_input(8);
// ... build circuit ...
let outputs = builder.eval(&[a.make_value(42), b.make_value(7)]);
Source

pub fn comment(&self, comment: impl Into<String>) -> Builder

Returns a new builder handle with the given comment appended to the annotation stack.

Unlike push_comment which mutates the current builder, this method returns a new Builder sharing the same underlying IR but with an independent comment stack containing the new comment. All instructions emitted through the returned builder are annotated with the full stack including the new comment; instructions emitted through the original builder remain unaffected. This is useful for forking annotation contexts without manual push/pop management.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let commented = builder.comment("add phase");
let ct = commented.ciphertext_input(4);
// Instructions through `commented` carry the "add phase" annotation.
Source

pub fn push_comment(&self, comment: impl Into<String>)

Pushes a comment onto the annotation stack.

All IR instructions emitted while this comment is on the stack will be annotated with the full stack joined by /. Use pop_comment to remove it, or prefer the RAII-style with_comment.

Source

pub fn pop_comment(&self)

Pops the most recent comment from the annotation stack.

Source

pub fn with_comment<R>( &self, comment: impl Into<String>, f: impl FnOnce() -> R, ) -> R

Executes a closure with a temporary comment pushed onto the annotation stack.

The comment is pushed before calling f and popped after it returns, ensuring proper nesting even if f itself pushes additional comments. Returns whatever f returns.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(4);
let blocks = builder.ciphertext_split(&ct);
let result = builder.with_comment("carry propagation", || {
    builder.block_add(&blocks[0], &blocks[1])
});
Source

pub fn ciphertext_input(&self, int_size: u16) -> Ciphertext

Declares an encrypted integer input of the given bit-width.

Registers a new ciphertext input in the circuit signature and emits the corresponding IR input instruction. The input is assigned the next positional index (see Input / Output Ordering). The int_size specifies the total number of message bits across all blocks (e.g. 8 for an 8-bit integer). The resulting ciphertext is a radix-decomposed integer with int_size / message_size blocks.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let input = builder.ciphertext_input(8);
let blocks = builder.ciphertext_split(&input);
Source

pub fn ciphertext_split( &self, inp: impl AsRef<Ciphertext>, ) -> Vec<CiphertextBlock>

Decomposes a Ciphertext into its individual radix blocks.

Returns one CiphertextBlock per block in the radix-decomposed representation, ordered from least-significant to most-significant digit. The length of the returned vector is int_size / message_size.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(8);
let blocks = builder.ciphertext_split(&ct);
assert_eq!(blocks.len(), 4); // 8 bits / 2-bit message = 4 blocks
Source

pub fn plaintext_input(&self, int_size: u16) -> Plaintext

Declares a plaintext integer input of the given bit-width.

Registers a new plaintext input in the circuit signature and emits the corresponding IR input instruction. The input is assigned the next positional index, shared with ciphertext inputs (see Input / Output Ordering). The plaintext block spec is derived from the builder’s ciphertext block spec (matching message size, no carry bits).

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(8);
let pt = builder.plaintext_input(8);
let ct_blocks = builder.ciphertext_split(&ct);
let pt_blocks = builder.plaintext_split(&pt);
let sum = builder.block_add_plaintext(&ct_blocks[0], &pt_blocks[0]);
Source

pub fn plaintext_split(&self, inp: impl AsRef<Plaintext>) -> Vec<PlaintextBlock>

Decomposes a Plaintext into its individual radix blocks.

Returns one PlaintextBlock per digit in the radix-decomposed representation, ordered from least-significant to most-significant digit. The length of the returned vector is int_size / message_size.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let pt = builder.plaintext_input(8);
let blocks = builder.plaintext_split(&pt);
assert_eq!(blocks.len(), 4); // 8 bits / 2-bit message = 4 blocks
Source

pub fn ciphertext_join( &self, blocks: impl AsRef<[CiphertextBlock]>, int_size: Option<u16>, ) -> Ciphertext

Reassembles a slice of radix blocks into a single Ciphertext.

The blocks are stored in order, with block 0 as the least-significant radix block. When int_size is None, the total bit-width is inferred as blocks.len() * message_size. When int_size is Some, it overrides the bit-width explicitly. This is useful if the expected bit-width is not a multiple of the message size.

§Panics

Panics if int_size is Some and the number of blocks exceeds the capacity implied by the given bit-width.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let input = builder.ciphertext_input(8);
let blocks = builder.ciphertext_split(&input);
// ... operate on blocks ...
let ct = builder.ciphertext_join(&blocks, None);
builder.ciphertext_output(&ct);
Source

pub fn ciphertext_inspect(&self, src: impl AsRef<Ciphertext>) -> Ciphertext

Creates a new IR node that aliases an existing ciphertext.

The returned ciphertext references the same underlying value but has a distinct IR node identity. This is useful for debugging, as the node appears separately in IR dumps and can be annotated with the current comment stack.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let input = builder.ciphertext_input(8);
let labeled = builder.comment("after input").ciphertext_inspect(&input);
Source

pub fn ciphertext_output(&self, ct: impl AsRef<Ciphertext>)

Declares an encrypted integer output for the circuit.

Registers the ciphertext as a circuit output in the signature and emits the corresponding IR output instruction. The output is assigned the next positional index (see Input / Output Ordering).

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let input = builder.ciphertext_input(8);
builder.ciphertext_output(&input);
Source

pub fn block_let_plaintext(&self, value: u8) -> PlaintextBlock

Creates a constant PlaintextBlock with the given message value.

The value is stored as a message-only plaintext block. Its bit-width must fit within the builder’s message size.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(4);
let blocks = builder.ciphertext_split(&ct);
let one = builder.block_let_plaintext(1);
let incremented = builder.block_add_plaintext(&blocks[0], &one);
Source

pub fn block_add( &self, src_a: impl AsRef<CiphertextBlock>, src_b: impl AsRef<CiphertextBlock>, ) -> CiphertextBlock

Adds two ciphertext blocks (protect flavor).

Computes src_a + src_b at the block level. Uses protect semantics — see Operation Flavors.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(4);
let blocks = builder.ciphertext_split(&ct);
let sum = builder.block_add(&blocks[0], &blocks[1]);
Source

pub fn block_inspect(&self, src: impl AsRef<CiphertextBlock>) -> CiphertextBlock

Creates a new IR node that aliases an existing ciphertext block.

The returned block references the same underlying value but has a distinct IR node identity. This is useful for debugging purposes.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(4);
let blocks = builder.ciphertext_split(&ct);
let labeled = builder.comment("lsb").block_inspect(&blocks[0]);
Source

pub fn block_temper_add( &self, src_a: impl AsRef<CiphertextBlock>, src_b: impl AsRef<CiphertextBlock>, ) -> CiphertextBlock

Adds two ciphertext blocks (temper flavor).

Computes src_a + src_b at the block level. Uses temper semantics — see Operation Flavors.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(4);
let blocks = builder.ciphertext_split(&ct);
let sum = builder.block_temper_add(&blocks[0], &blocks[1]);
Source

pub fn block_wrapping_add( &self, src_a: impl AsRef<CiphertextBlock>, src_b: impl AsRef<CiphertextBlock>, ) -> CiphertextBlock

Adds two ciphertext blocks (wrapping flavor).

Computes src_a + src_b at the block level. Uses wrapping semantics — see Operation Flavors.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(4);
let blocks = builder.ciphertext_split(&ct);
let sum = builder.block_wrapping_add(&blocks[0], &blocks[1]);
Source

pub fn block_add_plaintext( &self, src_a: impl AsRef<CiphertextBlock>, src_b: impl AsRef<PlaintextBlock>, ) -> CiphertextBlock

Adds a plaintext block to a ciphertext block (protect flavor).

Computes src_a + src_b where src_a is encrypted and src_b is plaintext. Uses protect semantics — see Operation Flavors.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(4);
let blocks = builder.ciphertext_split(&ct);
let one = builder.block_let_plaintext(1);
let incremented = builder.block_add_plaintext(&blocks[0], &one);
Source

pub fn block_wrapping_add_plaintext( &self, src_a: impl AsRef<CiphertextBlock>, src_b: impl AsRef<PlaintextBlock>, ) -> CiphertextBlock

Adds a plaintext block to a ciphertext block (wrapping flavor).

Computes src_a + src_b where src_a is encrypted and src_b is plaintext. Uses wrapping semantics — see Operation Flavors.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(4);
let blocks = builder.ciphertext_split(&ct);
let one = builder.block_let_plaintext(1);
let incremented = builder.block_wrapping_add_plaintext(&blocks[0], &one);
Source

pub fn block_sub( &self, src_a: impl AsRef<CiphertextBlock>, src_b: impl AsRef<CiphertextBlock>, ) -> CiphertextBlock

Subtracts two ciphertext blocks (protect flavor).

Computes src_a - src_b at the block level. Uses protect semantics — see Operation Flavors.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(4);
let blocks = builder.ciphertext_split(&ct);
let diff = builder.block_sub(&blocks[1], &blocks[0]);
Source

pub fn block_sub_plaintext( &self, src_a: impl AsRef<CiphertextBlock>, src_b: impl AsRef<PlaintextBlock>, ) -> CiphertextBlock

Subtracts a plaintext block from a ciphertext block (protect flavor).

Computes src_a - src_b where src_a is encrypted and src_b is plaintext. Uses protect semantics — see Operation Flavors.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(4);
let blocks = builder.ciphertext_split(&ct);
let one = builder.block_let_plaintext(1);
let decremented = builder.block_sub_plaintext(&blocks[0], &one);
Source

pub fn block_plaintext_sub( &self, src_a: impl AsRef<PlaintextBlock>, src_b: impl AsRef<CiphertextBlock>, ) -> CiphertextBlock

Subtracts a ciphertext block from a plaintext block (protect flavor).

Computes src_a - src_b where src_a is plaintext and src_b is encrypted. The result is a ciphertext block. Uses protect semantics — see Operation Flavors. Note the reversed operand order compared to block_sub_plaintext.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(4);
let blocks = builder.ciphertext_split(&ct);
let three = builder.block_let_plaintext(3);
let result = builder.block_plaintext_sub(&three, &blocks[0]); // 3 - blocks[0]
Source

pub fn block_pack( &self, src_a: impl AsRef<CiphertextBlock>, src_b: impl AsRef<CiphertextBlock>, ) -> CiphertextBlock

Packs two ciphertext blocks into one.

Computes src_a * 2^message_size + src_b, placing src_a in the high (carry) bits and src_b in the low (message) bits of the resulting block. This is the standard way to pack two blocks to be processed within a single programmable bootstrapping (PBS) lookup.

§Panics

Panics if the builder’s carry_size != message_size, since packing requires equal-width carry and message fields.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(4);
let blocks = builder.ciphertext_split(&ct);
let packed = builder.block_pack(&blocks[1], &blocks[0]);
let result = builder.block_lookup(&packed, Lut1Def::MsgOnly);
Source

pub fn block_pack_then_lookup( &self, src_a: impl AsRef<CiphertextBlock>, src_b: impl AsRef<CiphertextBlock>, lut: Lut1Def, ) -> CiphertextBlock

Packs two ciphertext blocks and applies a single-output PBS lookup.

Equivalent to calling block_pack followed by block_lookup. This is a convenience for the common pack-then-lookup pattern.

§Panics

Panics if carry_size != message_size (see block_pack).

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(4);
let blocks = builder.ciphertext_split(&ct);
let result = builder.block_pack_then_lookup(&blocks[1], &blocks[0], Lut1Def::MsgOnly);
Source

pub fn block_lookup( &self, src: impl AsRef<CiphertextBlock>, lut: Lut1Def, ) -> CiphertextBlock

Applies a single-output programmable bootstrapping (PBS) lookup to a block.

The lut defines the function computed by the bootstrapping. The input block’s full data bits (carry + message) index into the lookup table, and the result is a fresh ciphertext block with clean noise.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(4);
let blocks = builder.ciphertext_split(&ct);
// Extract only the message bits, clearing the carry.
let clean = builder.block_lookup(&blocks[0], Lut1Def::MsgOnly);
Source

pub fn block_padding_lookup( &self, src: impl AsRef<CiphertextBlock>, lut: Lut1Def, ) -> CiphertextBlock

Applies a single-output PBS lookup allowing output padding overflow.

Like block_lookup, but the bootstrapping allows the result to overflow into the output padding bit. The input padding bit must still be clear (protect semantics on the input side). This is useful when a subsequent operation will consume the padding bit before the next lookup.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(4);
let blocks = builder.ciphertext_split(&ct);
let result = builder.block_padding_lookup(&blocks[0], Lut1Def::MsgOnly);
Source

pub fn block_wrapping_lookup( &self, src: impl AsRef<CiphertextBlock>, lut: Lut1Def, ) -> CiphertextBlock

Applies a single-output PBS lookup using wrapping (negacyclic) semantics.

Like block_lookup, but uses wrapping semantics for the lookup — see Operation Flavors. This is appropriate when the input block’s padding bit may be set, enabling negacyclic lookup behavior.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(4);
let blocks = builder.ciphertext_split(&ct);
let result = builder.block_wrapping_lookup(&blocks[0], Lut1Def::MsgOnly);
Source

pub fn block_lookup2( &self, src: impl AsRef<CiphertextBlock>, lut: Lut2Def, ) -> (CiphertextBlock, CiphertextBlock)

Applies a dual-output programmable bootstrapping (PBS) lookup to a block.

Like block_lookup, but the bootstrapping produces two output blocks from a single input. The two lookup functions are defined by the Lut2Def variant. This amortizes the cost of a PBS when two related values need to be extracted simultaneously.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(4);
let blocks = builder.ciphertext_split(&ct);
let packed = builder.block_pack(&blocks[1], &blocks[0]);
let (msg, carry) = builder.block_lookup2(&packed, Lut2Def::ManyCarryMsg);
Source

pub fn block_let_ciphertext(&self, value: u8) -> CiphertextBlock

Creates a constant CiphertextBlock with the given value.

The value is stored as a trivially-encrypted block (zero noise). This is useful for initializing accumulators or providing constant operands in arithmetic. The value’s bit-width must fit within the block’s data bits (carry + message).

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let zero = builder.block_let_ciphertext(0);
let ct = builder.ciphertext_input(4);
let blocks = builder.ciphertext_split(&ct);
let sum = builder.block_add(&zero, &blocks[0]); // 0 + blocks[0]
Source§

impl Builder

Source

pub fn vector_pack( &self, blocks: impl AsRef<[CiphertextBlock]>, ) -> Vec<CiphertextBlock>

Packs consecutive pairs of blocks in a slice.

Iterates over blocks in chunks of two, calling block_pack on each pair. Within each pair, the second element (blocks[2i+1]) goes to the high bits and the first (blocks[2i]) to the low bits. If the slice has an odd number of elements, the trailing block is passed through unchanged.

The output has length ceil(blocks.len() / 2).

§Panics

Panics if carry_size != message_size (see block_pack).

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(8);
let blocks = builder.ciphertext_split(&ct); // 4 blocks
let packed = builder.vector_pack(&blocks);  // 2 packed blocks
Source

pub fn vector_pack_then_clean( &self, blocks: impl AsRef<[CiphertextBlock]>, ) -> Vec<CiphertextBlock>

Packs consecutive pairs and applies an identity PBS to clean noise.

Equivalent to calling vector_pack_then_lookup with Lut1Def::None. The PBS acts as a noise-refresh: each packed pair is bootstrapped through the identity function, producing a clean block. Trailing odd blocks are passed through without bootstrapping.

§Panics

Panics if carry_size != message_size (see block_pack).

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(8);
let blocks = builder.ciphertext_split(&ct);
let cleaned = builder.vector_pack_then_clean(&blocks);
Source

pub fn vector_pack_then_lookup( &self, blocks: impl AsRef<[CiphertextBlock]>, lut: Lut1Def, ) -> Vec<CiphertextBlock>

Packs consecutive pairs and applies a single-output PBS lookup to each.

Iterates over blocks in chunks of two: each pair is block_packed and then passed through block_lookup with the given lut. If the slice has an odd number of elements, the trailing block is passed through unchanged (no PBS).

The output has length ceil(blocks.len() / 2).

§Panics

Panics if carry_size != message_size (see block_pack).

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(8);
let blocks = builder.ciphertext_split(&ct);
let results = builder.vector_pack_then_lookup(&blocks, Lut1Def::MsgOnly);
Source

pub fn vector_zip_then_lookup( &self, lhs: impl AsRef<[CiphertextBlock]>, rhs: impl AsRef<[CiphertextBlock]>, lut: Lut1Def, extension: ExtensionBehavior, ) -> Vec<CiphertextBlock>

Zips two block slices, packs each pair, and applies a PBS lookup.

For each position, packs lhs[i] into the high bits and rhs[i] into the low bits via block_pack, then passes the result through block_lookup with the given lut. When the two slices have different lengths, extension controls the behavior (see ExtensionBehavior).

§Panics

Panics if carry_size != message_size (see block_pack), or if the slices differ in length and extension is Panic.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let a = builder.ciphertext_input(8);
let b = builder.ciphertext_input(8);
let a_blocks = builder.ciphertext_split(&a);
let b_blocks = builder.ciphertext_split(&b);
let results = builder.vector_zip_then_lookup(
    &a_blocks,
    &b_blocks,
    Lut1Def::MsgOnly,
    ExtensionBehavior::Panic,
);
Source

pub fn vector_lookup( &self, blocks: impl AsRef<[CiphertextBlock]>, lut: Lut1Def, ) -> Vec<CiphertextBlock>

Applies a single-output PBS lookup to every block in a slice.

Maps block_lookup over each element. Unlike vector_pack_then_lookup, no packing is performed — each block is bootstrapped independently.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(8);
let blocks = builder.ciphertext_split(&ct);
let cleaned = builder.vector_lookup(&blocks, Lut1Def::MsgOnly);
Source

pub fn vector_lookup2( &self, blocks: impl AsRef<[CiphertextBlock]>, lut: Lut2Def, ) -> Vec<(CiphertextBlock, CiphertextBlock)>

Applies a dual-output PBS lookup to every block in a slice.

Maps block_lookup2 over each element, returning a pair of output blocks per input block.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(8);
let blocks = builder.ciphertext_split(&ct);
let packed = builder.vector_pack(&blocks);
let pairs = builder.vector_lookup2(&packed, Lut2Def::ManyCarryMsg);
Source

pub fn vector_add( &self, lhs: impl AsRef<[CiphertextBlock]>, rhs: impl AsRef<[CiphertextBlock]>, extension: ExtensionBehavior, ) -> Vec<CiphertextBlock>

Adds two block slices element-wise.

For each position, calls block_add on the corresponding pair. When the two slices have different lengths, extension controls the behavior (see ExtensionBehavior).

§Panics

Panics if the slices differ in length and extension is Panic.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let a = builder.ciphertext_input(8);
let b = builder.ciphertext_input(8);
let a_blocks = builder.ciphertext_split(&a);
let b_blocks = builder.ciphertext_split(&b);
let sums = builder.vector_add(&a_blocks, &b_blocks, ExtensionBehavior::Panic);
Source

pub fn vector_unsigned_extension( &self, inp: impl AsRef<[CiphertextBlock]>, size: usize, ) -> Vec<CiphertextBlock>

Zero-extends a block slice to a given length.

Pads inp with zero-valued constant ciphertext blocks (block_let_ciphertext(0)) until the result has size elements. This implements unsigned integer extension: the original blocks represent the low-order radix digits, and the appended zeros represent high-order digits.

§Panics

Panics if inp.len() > size.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(4);        // 2 blocks
let blocks = builder.ciphertext_split(&ct);
let extended = builder.vector_unsigned_extension(&blocks, 4); // now 4 blocks
Source

pub fn vector_add_reduce( &self, inp: impl AsRef<[CiphertextBlock]>, ) -> CiphertextBlock

Reduces a block slice to a single block by summing all elements (protect flavor).

Folds block_add across every element, returning their cumulative sum. This is useful for combining partial results from parallel computations into a single accumulator block.

§Panics

Panics if inp is empty.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(8);
let blocks = builder.ciphertext_split(&ct);
let total = builder.vector_add_reduce(&blocks);
Source

pub fn vector_inspect( &self, inp: impl AsRef<[CiphertextBlock]>, ) -> Vec<CiphertextBlock>

Applies block_inspect to every block in a slice.

Each block is inspected with an index-based comment ("0", "1", …) appended to the current comment stack. This is useful for labeling block positions in IR dumps.

§Examples
let builder = Builder::new(CiphertextBlockSpec(2, 2));
let ct = builder.ciphertext_input(8);
let blocks = builder.ciphertext_split(&ct);
let labeled = builder.comment("radix digits").vector_inspect(&blocks);
Source§

impl Builder

Source

pub fn iop_add_hillis_steele( &mut self, lhs: &Ciphertext, rhs: &Ciphertext, ) -> Ciphertext

Adds two encrypted integers with Hillis-Steele carry propagation.

The operation computes lhs + rhs (wrapping) using a parallel-prefix carry-propagation scheme with 4-block grouping. Both operands must share the same block decomposition as determined by the CiphertextSpec used to create the builder. The result is a fresh Ciphertext whose message bits have been cleaned of any residual carry via a final programmable bootstrapping pass.

Non-power-of-two and non-multiple-of-four block counts are handled transparently: the computation is extended to a favourable size and dead-code elimination trims the unused portion downstream.

§Examples
let sum = builder.iop_add_hillis_steele(&a, &b);
Source§

impl Builder

Source

pub fn iop_bitwise( &mut self, lhs: &Ciphertext, rhs: &Ciphertext, kind: BwKind, ) -> Ciphertext

Applies a block-wise bitwise operation on two encrypted integers.

Both operands must have the same block decomposition; the builder panics if their lengths differ.

§Panics

Panics if lhs and rhs have different numbers of blocks.

§Examples
let result = builder.iop_bitwise(&a, &b, BwKind::Xor);
Source§

impl Builder

Source

pub fn iop_cmp( &self, src_a: &Ciphertext, src_b: &Ciphertext, kind: CmpKind, ) -> Ciphertext

Compares two encrypted integers under the given relation.

The comparison proceeds in three phases: block-wise subtraction with sign extraction, tree-based reduction of the per-block results, and a final merge PBS that produces the boolean output. The kind parameter selects which relation is evaluated (see CmpKind).

Both src_a and src_b must have the same block decomposition. The returned Ciphertext is a single-block integer encoding the boolean result: 1 when the relation holds, 0 otherwise.

§Examples
let is_eq = builder.iop_cmp(&a, &b, CmpKind::Equal);
Source§

impl Builder

Source

pub fn iop_count(&mut self, inp: &Ciphertext, kind: BitType) -> Ciphertext

Counts the number of zero or one bits in an encrypted integer.

The operation splits inp into individual bits, then performs a recursive column-based reduction that sums them with carry propagation. When kind is BitType::Zero, the first reduction pass uses inverted look-up tables so that each bit contributes 1 when it is zero. The returned Ciphertext has a width of ⌈log₂(int_size + 1)⌉ bits, just enough to represent every possible count from 0 to int_size.

§Examples
let pop = builder.iop_count(&a, BitType::One);
Source§

impl Builder

Source

pub fn iop_if_then_else( &self, src_a: &Ciphertext, src_b: &Ciphertext, cond: &Ciphertext, ) -> Ciphertext

Selects between two encrypted integers based on an encrypted condition.

When cond is zero (false) the result equals src_a; when cond is non-zero (true) the result equals src_b. The selection is performed block-wise: each block of src_a is zeroed when the condition is true and each block of src_b is zeroed when it is false, then the two are added together.

Both src_a and src_b must have the same block decomposition, and cond must be a single-block ciphertext (typically the output of a comparison operation such as iop_cmp).

§Examples
let selected = builder.iop_if_then_else(&a, &b, &cond);
Source§

impl Builder

Source

pub fn iop_if_then_zero( &self, src: &Ciphertext, cond: &Ciphertext, ) -> Ciphertext

Zeroes an encrypted integer when a condition is true.

When cond is zero (false) the result equals src; when cond is non-zero (true) the result is zero. The operation applies a single programmable bootstrapping per block, making it cheaper than a full iop_if_then_else.

The cond operand must be a single-block ciphertext (typically the output of a comparison operation such as iop_cmp).

§Examples
let zeroed = builder.iop_if_then_zero(&src, &cond);
Source§

impl Builder

Source

pub fn iop_ilog2(&self, src: impl AsRef<Ciphertext>) -> Ciphertext

Source

pub fn iop_trail0(&self, src: impl AsRef<Ciphertext>) -> Ciphertext

Source

pub fn iop_trail1(&self, src: impl AsRef<Ciphertext>) -> Ciphertext

Source

pub fn iop_lead0(&self, src: impl AsRef<Ciphertext>) -> Ciphertext

Source

pub fn iop_lead1(&self, src: impl AsRef<Ciphertext>) -> Ciphertext

Source§

impl Builder

Source

pub fn iop_mul_raw( &self, src_a_blocks: &Vec<CiphertextBlock>, src_b_blocks: &Vec<CiphertextBlock>, cut_off_block: u8, ) -> (CiphertextBlock, Vec<CiphertextBlock>)

Multiply two ciphertext in a raw fashion. I.e. Compute all output up to cut-off point then only overflow flag status. This function should be wrapped specialized instances that select the desired output information and use the deadcode analysis to remove useless part

The muliplication is done in two phases:

  • Expansion: generate all the partial product
  • Reduction: sum partial product and propagate the carry

Overflow computation also uses same phases, whith slight differences:

  • Expansion: only compute NonNull flag of the product
  • Reduction: sum NonNull flag (no carry propagation)
§Examples
let (flag, res) = builder.iop_mul_raw(&a, &b, spec.block_count());

Trait Implementations§

Source§

impl Clone for Builder

Source§

fn clone(&self) -> Builder

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.