> For the complete documentation index, see [llms.txt](https://docs.zama.org/tfhe-rs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.zama.org/tfhe-rs/fhe-computation/data-handling/compress.md).

# Compressing ciphertexts/keys

This document explains the mechanism and steps to compress ciphertext and keys to reduce the storage needed as well as transmission times.

Most **TFHE-rs** entities contain random numbers generated by a Pseudo Random Number Generator (PRNG). Since the implemented PRNG is deterministic, storing only the random seed used to generate those numbers preserves all necessary information. When decompressing the entity, using the same PRNG and the same seed will reconstruct the full chain of random values.

In **TFHE-rs**, compressible entities are prefixed with `Compressed`. For instance, a compressed `FheUint256` is declared as `CompressedFheUint256`.

In the following example code, we use the `bincode` crate dependency to serialize in a binary format and compare serialized sizes.

## Compressing Ciphertexts

### Compressing ciphertexts at encryption time

This example shows how to compress a ciphertext encrypting messages over 16 bits:

```rust
use tfhe::prelude::*;
use tfhe::{ConfigBuilder, generate_keys, CompressedFheUint16};

fn main() {
    let config = ConfigBuilder::default().build();
    let (client_key, _) = generate_keys(config);

    let clear = 12_837u16;
    let compressed = CompressedFheUint16::try_encrypt(clear, &client_key).unwrap();
    println!(
        "compressed size  : {}",
        bincode::serialize(&compressed).unwrap().len()
    );
    
    let decompressed = compressed.decompress();
    
    println!(
        "decompressed size: {}",
        bincode::serialize(&decompressed).unwrap().len()
    );

    let clear_decompressed: u16 = decompressed.decrypt(&client_key);
    assert_eq!(clear_decompressed, clear);
}
```

### Compressing ciphertexts after some homomorphic computation

You can compress ciphertexts at any time, even after performing multiple homomorphic operations.

To do so, you need to build a list containing all the ciphertexts that have to be compressed. This list might contain ciphertexts of different types, e.g., FheBool, FheUint32, FheInt64,... There is no constraint regarding the size of the list.

There are two possible approaches:

* **Single list**: Compressing several ciphertexts into a single list. This generally yields a better compression ratio between output and input sizes;
* **Multiple lists**: Using multiple lists. This offers more flexibility, since compression might happen at different times in the code, but could lead to larger outputs.

In more details, the optimal ratio is achieved with a list whose size is equal to the `lwe_per_glwe` field from the `CompressionParameters`.

The following example shows how to compress and decompress a list containing 4 messages: one 32-bits integer, one 64-bit integer, one boolean, and one 2-bit integer.

```rust
use tfhe::prelude::*;
use tfhe::shortint::parameters::{
    MetaParametersFinder, Log2PFail, Constraint, Backend,
};
use tfhe::{
    set_server_key, CompressedCiphertextList, CompressedCiphertextListBuilder, FheBool,
    FheInt64, FheUint16, FheUint2, FheUint32,
};

fn main() {
    let parameters = MetaParametersFinder::new(
        Constraint::LessThanOrEqual(Log2PFail(-128.0)),
        Backend::Cpu
    )
    .with_compression(true)
    .find()
    .expect("Could not find suitable parameters");

    let ck = tfhe::ClientKey::generate(parameters);
    let sk = tfhe::ServerKey::new(&ck);

    set_server_key(sk);

    let ct1 = FheUint32::encrypt(17_u32, &ck);

    let ct2 = FheInt64::encrypt(-1i64, &ck);

    let ct3 = FheBool::encrypt(false, &ck);

    let ct4 = FheUint2::encrypt(3u8, &ck);

    let compressed_list = CompressedCiphertextListBuilder::new()
        .push(ct1)
        .push(ct2)
        .push(ct3)
        .push(ct4)
        .build()
        .unwrap();

    let serialized = bincode::serialize(&compressed_list).unwrap();

    println!("Serialized size: {} bytes", serialized.len());

    let compressed_list: CompressedCiphertextList = bincode::deserialize(&serialized).unwrap();

    let a: FheUint32 = compressed_list.get(0).unwrap().unwrap();
    let b: FheInt64 = compressed_list.get(1).unwrap().unwrap();
    let c: FheBool = compressed_list.get(2).unwrap().unwrap();
    let d: FheUint2 = compressed_list.get(3).unwrap().unwrap();

    let a: u32 = a.decrypt(&ck);
    assert_eq!(a, 17);
    let b: i64 = b.decrypt(&ck);
    assert_eq!(b, -1);
    let c = c.decrypt(&ck);
    assert!(!c);
    let d: u8 = d.decrypt(&ck);
    assert_eq!(d, 3);

    // Out of bound index 
    assert!(compressed_list.get::<FheBool>(4).unwrap().is_none());

    // Incorrect type
    assert!(compressed_list.get::<FheInt64>(0).is_err());

    // Correct type but wrong number of bits
    assert!(compressed_list.get::<FheUint16>(0).is_err());
}
```

## Compressing keys

### Compressing server keys

This example shows how to compress the server keys:

```rust
use tfhe::prelude::*;
use tfhe::{
    set_server_key, ClientKey, CompressedServerKey, ConfigBuilder, FheUint8,
};

fn main() {
    let config = ConfigBuilder::default().build();

    let cks = ClientKey::generate(config);
    let compressed_sks = CompressedServerKey::new(&cks);

    println!(
        "compressed size  : {}",
        bincode::serialize(&compressed_sks).unwrap().len()
    );

    let sks = compressed_sks.decompress();

    println!(
        "decompressed size: {}",
        bincode::serialize(&sks).unwrap().len()
    );

    set_server_key(sks);

    let clear_a = 12u8;
    let a = FheUint8::try_encrypt(clear_a, &cks).unwrap();

    let c = a + 234u8;
    let decrypted: u8 = c.decrypt(&cks);
    assert_eq!(decrypted, clear_a.wrapping_add(234));
}

```

## Compressed public keys

This example shows how to compress the classical public keys:

{% hint style="warning" %}
It is not currently recommended to use the `CompressedPublicKey` to encrypt ciphertexts without first decompressing them. If the resulting PublicKey is too large to fit in memory, it may result in significant slowdowns.

This issue has been identified and will be addressed in future releases.
{% endhint %}

```rust
use tfhe::prelude::*;
use tfhe::{ConfigBuilder, generate_keys, FheUint8, CompressedPublicKey};

fn main() {
    let config = ConfigBuilder::default().build();
    let (client_key, _) = generate_keys(config);

    let compressed_public_key = CompressedPublicKey::new(&client_key);

    println!("compressed size  : {}", bincode::serialize(&compressed_public_key).unwrap().len());

    let public_key = compressed_public_key.decompress();

    println!("decompressed size: {}", bincode::serialize(&public_key).unwrap().len());


    let a = FheUint8::try_encrypt(213u8, &public_key).unwrap();
    let clear: u8 = a.decrypt(&client_key);
    assert_eq!(clear, 213u8);
}
```

## Compressed compact public key

This example shows how to use compressed compact public keys:

```rust
use tfhe::prelude::*;
use tfhe::{
    generate_keys, CompactCiphertextList, CompressedCompactPublicKey,
    ConfigBuilder, FheUint8,
};

fn main() {
    let config = ConfigBuilder::default()
        .use_custom_parameters(
            tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2_KS_PBS_GAUSSIAN_2M128,
        )
        .build();
    let (client_key, _) = generate_keys(config);

    let public_key_compressed = CompressedCompactPublicKey::new(&client_key);

    println!(
        "compressed size  : {}",
        bincode::serialize(&public_key_compressed).unwrap().len()
    );

    let public_key = public_key_compressed.decompress();

    println!(
        "decompressed size: {}",
        bincode::serialize(&public_key).unwrap().len()
    );

    let compact_list = CompactCiphertextList::builder(&public_key)
        .push(255u8)
        .build();
    let expanded = compact_list.expand().unwrap();
    let a: FheUint8 = expanded.get(0).unwrap().unwrap();

    let clear: u8 = a.decrypt(&client_key);
    assert_eq!(clear, 255u8);
}

```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.zama.org/tfhe-rs/fhe-computation/data-handling/compress.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
