diff --git a/Makefile b/Makefile
index 01f4c13e3..b5e78cf00 100644
--- a/Makefile
+++ b/Makefile
@@ -582,7 +582,8 @@ test_high_level_api_gpu: install_rs_build_toolchain install_cargo_nextest
.PHONY: test_user_doc # Run tests from the .md documentation
test_user_doc: install_rs_build_toolchain
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) --doc \
- --features=$(TARGET_ARCH_FEATURE),boolean,shortint,integer,internal-keycache -p $(TFHE_SPEC) \
+ --features=$(TARGET_ARCH_FEATURE),boolean,shortint,integer,internal-keycache,pbs-stats \
+ -p $(TFHE_SPEC) \
-- test_user_docs::
.PHONY: test_user_doc_gpu # Run tests for GPU from the .md documentation
@@ -651,13 +652,17 @@ lint_docs: lint_doc
.PHONY: format_doc_latex # Format the documentation latex equations to avoid broken rendering.
format_doc_latex:
- cargo xtask format_latex_doc
+ RUSTFLAGS="" cargo xtask format_latex_doc
@"$(MAKE)" --no-print-directory fmt
@printf "\n===============================\n\n"
@printf "Please manually inspect changes made by format_latex_doc, rustfmt can break equations \
if the line length is exceeded\n"
@printf "\n===============================\n"
+.PHONY: check_md_docs_are_tested # Checks that the rust codeblocks in our .md files are tested
+check_md_docs_are_tested:
+ RUSTFLAGS="" cargo xtask check_tfhe_docs_are_tested
+
.PHONY: check_compile_tests # Build tests in debug without running them
check_compile_tests:
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --no-run \
@@ -918,13 +923,15 @@ sha256_bool: install_rs_check_toolchain
--features=$(TARGET_ARCH_FEATURE),boolean
.PHONY: pcc # pcc stands for pre commit checks (except GPU)
-pcc: no_tfhe_typo no_dbg_log check_fmt lint_doc clippy_all check_compile_tests
+pcc: no_tfhe_typo no_dbg_log check_fmt lint_doc check_md_docs_are_tested clippy_all \
+check_compile_tests
.PHONY: pcc_gpu # pcc stands for pre commit checks for GPU compilation
pcc_gpu: clippy_gpu clippy_cuda_backend check_compile_tests_benches_gpu
.PHONY: fpcc # pcc stands for pre commit checks, the f stands for fast
-fpcc: no_tfhe_typo no_dbg_log check_fmt lint_doc clippy_fast check_compile_tests
+fpcc: no_tfhe_typo no_dbg_log check_fmt lint_doc check_md_docs_are_tested clippy_fast \
+check_compile_tests
.PHONY: conformance # Automatically fix problems that can be fixed
conformance: fix_newline fmt
diff --git a/README.md b/README.md
index 7d65df038..49dcdb5f3 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,10 @@
-
+
+
+
+
+
@@ -173,12 +177,12 @@ to run in release mode with cargo's `--release` flag to have the best performanc
### Tutorials
-- [Homomorphic Parity Bit](https://docs.zama.ai/tfhe-rs/tutorials/parity_bit)
-- [Homomorphic Case Changing on Ascii String](https://docs.zama.ai/tfhe-rs/tutorials/ascii_fhe_string)
+- [[Video tutorial] Implement signed integers using TFHE-rs ](https://www.zama.ai/post/video-tutorial-implement-signed-integers-ssing-tfhe-rs)
+- [Homomorphic parity bit](https://docs.zama.ai/tfhe-rs/tutorials/parity_bit)
+- [Homomorphic case changing on Ascii string](https://docs.zama.ai/tfhe-rs/tutorials/ascii_fhe_string)
- [Boolean SHA256 with TFHE-rs](https://www.zama.ai/post/boolean-sha256-tfhe-rs)
-- [Dark Market with TFHE-rs](https://www.zama.ai/post/dark-market-tfhe-rs)
-- [Regular Expression Engine with TFHE-rs](https://www.zama.ai/post/regex-engine-tfhe-rs)
-
+- [Dark market with TFHE-rs](https://www.zama.ai/post/dark-market-tfhe-rs)
+- [Regular expression engine with TFHE-rs](https://www.zama.ai/post/regex-engine-tfhe-rs)
*Explore more useful resources in [TFHE-rs tutorials](https://docs.zama.ai/tfhe-rs/tutorials) and [Awesome Zama repo](https://github.com/zama-ai/awesome-zama)*
@@ -240,7 +244,11 @@ This software is distributed under the **BSD-3-Clause-Clear** license. If you ha
## Support
-
+
+
+
+
+
🌟 If you find this project helpful or interesting, please consider giving it a star on GitHub! Your support helps to grow the community and motivates further development.
diff --git a/tasks/Cargo.toml b/tasks/Cargo.toml
index 3a25531cc..dfe0aa251 100644
--- a/tasks/Cargo.toml
+++ b/tasks/Cargo.toml
@@ -10,3 +10,5 @@ clap = "=4.4.4"
lazy_static = "1.4"
log = "0.4"
simplelog = "0.12"
+walkdir = "2.5.0"
+no-comment = "0.0.3"
diff --git a/tasks/src/check_tfhe_docs_are_tested.rs b/tasks/src/check_tfhe_docs_are_tested.rs
new file mode 100644
index 000000000..43997c7d7
--- /dev/null
+++ b/tasks/src/check_tfhe_docs_are_tested.rs
@@ -0,0 +1,119 @@
+use no_comment::{languages, IntoWithoutComments};
+use std::collections::HashSet;
+use std::io::{Error, ErrorKind};
+
+const FILES_TO_IGNORE: [&str; 3] = [
+ // This contains fragments of code that are unrelated to TFHE-rs
+ "tfhe/docs/tutorials/sha256_bool.md",
+ // This contains fragments of code coming from the tutorial that cannot be run as a doctest
+ "tfhe/examples/fhe_strings/README.md",
+ // TODO: This contains code that could be executed as a trivium docstring
+ "apps/trivium/README.md",
+];
+
+pub fn check_tfhe_docs_are_tested() -> Result<(), Error> {
+ let curr_dir = std::env::current_dir()?;
+ let tfhe_src = curr_dir.join("tfhe/src");
+ let test_user_doc_file = tfhe_src.join("test_user_docs.rs");
+
+ if !test_user_doc_file.exists() {
+ return Err(Error::new(
+ ErrorKind::NotFound,
+ format!(
+ "{} does not exist, \
+ did you launch the command from the repo root?",
+ test_user_doc_file.display()
+ ),
+ ));
+ }
+
+ // Find files which are tested
+ let file_content = std::fs::read_to_string(&test_user_doc_file)?;
+
+ let file_content = file_content
+ .chars()
+ .without_comments(languages::rust())
+ .collect::();
+
+ let mut file_content = file_content.as_str();
+
+ let mut tested_files = HashSet::new();
+
+ while let Some(doctest_macro_invocation) = file_content.find("doctest!(") {
+ file_content = &file_content[doctest_macro_invocation + 9..];
+
+ let opening_quote = file_content
+ .find('"')
+ .ok_or_else(|| Error::new(ErrorKind::NotFound, "Could not find opening quote"))?;
+
+ file_content = &file_content[opening_quote + 1..];
+
+ let closing_quote = file_content
+ .find('"')
+ .ok_or_else(|| Error::new(ErrorKind::NotFound, "Could not find closing quote"))?;
+
+ let tested_file_name = &file_content[..closing_quote];
+
+ file_content = &file_content[tested_file_name.len() + 1..];
+
+ let mut tested_file_path = tfhe_src.join(tested_file_name);
+
+ if tested_file_path.exists() {
+ tested_file_path = tested_file_path.canonicalize().unwrap();
+ }
+
+ tested_files.insert(tested_file_path);
+ }
+
+ let mut walk_errs = vec![];
+
+ let dir_entries = walkdir::WalkDir::new(&curr_dir)
+ .into_iter()
+ .flat_map(|e| match e {
+ Ok(e) => Some(e),
+ Err(err) => {
+ walk_errs.push(err);
+ None
+ }
+ })
+ .collect::>();
+
+ if !walk_errs.is_empty() {
+ return Err(Error::new(
+ ErrorKind::InvalidData,
+ format!("Encountered errors while walking repo: {walk_errs:#?}"),
+ ));
+ }
+
+ let mut doc_files: HashSet<_> = dir_entries
+ .into_iter()
+ .filter_map(|entry| {
+ let path = entry.path().canonicalize().ok()?;
+ if path.is_file() && path.extension().map_or(false, |e| e == "md") {
+ let file_content = std::fs::read_to_string(&path).ok()?;
+ if file_content.contains("```rust") {
+ Some(path.to_path_buf())
+ } else {
+ None
+ }
+ } else {
+ None
+ }
+ })
+ .collect();
+
+ for value_to_remove in FILES_TO_IGNORE {
+ let path_to_remove = curr_dir.join(value_to_remove).canonicalize()?.to_path_buf();
+ doc_files.remove(&path_to_remove);
+ }
+
+ let difference = doc_files.difference(&tested_files);
+
+ let debug_format = format!("missing file from user doc tests: {difference:#?}");
+
+ if difference.count() != 0 {
+ return Err(Error::new(ErrorKind::NotFound, debug_format));
+ }
+
+ Ok(())
+}
diff --git a/tasks/src/main.rs b/tasks/src/main.rs
index 090c62a75..5090b1962 100644
--- a/tasks/src/main.rs
+++ b/tasks/src/main.rs
@@ -5,6 +5,7 @@ use simplelog::{ColorChoice, CombinedLogger, Config, TermLogger, TerminalMode};
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering::Relaxed;
+mod check_tfhe_docs_are_tested;
mod format_latex_doc;
mod utils;
@@ -19,6 +20,9 @@ lazy_static! {
// MAIN
// -------------------------------------------------------------------------------------------------
+const FORMAT_LATEX_DOC: &str = "format_latext_doc";
+const CHECK_TFHE_DOCS_ARE_TESTED: &str = "check_tfhe_docs_are_tested";
+
fn main() -> Result<(), std::io::Error> {
// We parse the input args
let matches = Command::new("tasks")
@@ -34,7 +38,11 @@ fn main() -> Result<(), std::io::Error> {
.long("dry-run")
.help("Do not execute the commands"),
)
- .subcommand(Command::new("format_latex_doc").about("Escape underscores in latex equations"))
+ .subcommand(Command::new(FORMAT_LATEX_DOC).about("Escape underscores in latex equations"))
+ .subcommand(
+ Command::new(CHECK_TFHE_DOCS_ARE_TESTED)
+ .about("Check that doc files with rust code blocks are tested"),
+ )
.arg_required_else_help(true)
.get_matches();
@@ -57,8 +65,13 @@ fn main() -> Result<(), std::io::Error> {
DRY_RUN.store(true, Relaxed);
}
- if matches.subcommand_matches("format_latex_doc").is_some() {
+ if matches.subcommand_matches(FORMAT_LATEX_DOC).is_some() {
format_latex_doc::escape_underscore_in_latex_doc()?;
+ } else if matches
+ .subcommand_matches(CHECK_TFHE_DOCS_ARE_TESTED)
+ .is_some()
+ {
+ check_tfhe_docs_are_tested::check_tfhe_docs_are_tested()?;
}
Ok(())
diff --git a/tfhe/docs/.gitbook/assets/doc_header_tfhe-rs.png b/tfhe/docs/.gitbook/assets/doc_header_tfhe-rs.png
new file mode 100644
index 000000000..eac33320c
Binary files /dev/null and b/tfhe/docs/.gitbook/assets/doc_header_tfhe-rs.png differ
diff --git a/tfhe/docs/.gitbook/assets/orange1.png b/tfhe/docs/.gitbook/assets/orange1.png
new file mode 100644
index 000000000..7ff77fa96
Binary files /dev/null and b/tfhe/docs/.gitbook/assets/orange1.png differ
diff --git a/tfhe/docs/.gitbook/assets/orange2.png b/tfhe/docs/.gitbook/assets/orange2.png
new file mode 100644
index 000000000..cee8e2c52
Binary files /dev/null and b/tfhe/docs/.gitbook/assets/orange2.png differ
diff --git a/tfhe/docs/.gitbook/assets/orange3.png b/tfhe/docs/.gitbook/assets/orange3.png
new file mode 100644
index 000000000..9f5d305d3
Binary files /dev/null and b/tfhe/docs/.gitbook/assets/orange3.png differ
diff --git a/tfhe/docs/.gitbook/assets/yellow1.png b/tfhe/docs/.gitbook/assets/yellow1.png
new file mode 100644
index 000000000..71007b381
Binary files /dev/null and b/tfhe/docs/.gitbook/assets/yellow1.png differ
diff --git a/tfhe/docs/.gitbook/assets/yellow2.png b/tfhe/docs/.gitbook/assets/yellow2.png
new file mode 100644
index 000000000..2af0c7d9e
Binary files /dev/null and b/tfhe/docs/.gitbook/assets/yellow2.png differ
diff --git a/tfhe/docs/.gitbook/assets/yellow3.png b/tfhe/docs/.gitbook/assets/yellow3.png
new file mode 100644
index 000000000..fa6754002
Binary files /dev/null and b/tfhe/docs/.gitbook/assets/yellow3.png differ
diff --git a/tfhe/docs/README.md b/tfhe/docs/README.md
index b2f55f573..974521abe 100644
--- a/tfhe/docs/README.md
+++ b/tfhe/docs/README.md
@@ -1,36 +1,64 @@
-# What is TFHE-rs?
+---
+description: >-
+ TFHE-rs is a pure Rust implementation of TFHE for Boolean and integer
+ arithmetics over encrypted data. It includes a Rust and C API, as well as a
+ client-side WASM API.
+layout:
+ title:
+ visible: true
+ description:
+ visible: true
+ tableOfContents:
+ visible: true
+ outline:
+ visible: true
+ pagination:
+ visible: false
+---
-📁 [Github](https://github.com/zama-ai/tfhe-rs) | 💛 [Community support](https://zama.ai/community) | 🟨 [Zama Bounty Program](https://github.com/zama-ai/bounty-program)
+# Welcome to TFHE-rs
-
+## Get started
-TFHE-rs is a pure Rust implementation of TFHE for Boolean and integer arithmetics over encrypted data. It includes a Rust and C API, as well as a client-side WASM API.
+Learn the basics of TFHE-rs, set it up, and make it run with ease.
-TFHE-rs is meant for developers and researchers who want full control over what they can do with TFHE, while not worrying about the low level implementation.
+
-The goal is to have a stable, simple, high-performance, and production-ready library for all the advanced features of TFHE.
+## Build with TFHE-rs
-## Key cryptographic concepts
+Start building with TFHE-rs by exploring its core features, discovering essential guides, and learning more with user-friendly tutorials.
-The TFHE-rs library implements Zama’s variant of Fully Homomorphic Encryption over the Torus (TFHE). TFHE is based on Learning With Errors (LWE), a well-studied cryptographic primitive believed to be secure even against quantum computers.
+
-In cryptography, a raw value is called a message (also sometimes called a cleartext), while an encoded message is called a plaintext and an encrypted plaintext is called a ciphertext.
+## Explore more
-The idea of homomorphic encryption is that you can compute on ciphertexts while not knowing messages encrypted within them. A scheme is said to be _fully homomorphic_, meaning any program can be evaluated with it, if at least two of the following operations are supported ($$x$$ is a plaintext and $$E[x]$$ is the corresponding ciphertext):
+Access to additional resources and join the Zama community.
-* homomorphic univariate function evaluation: $$f(E[x]) = E[f(x)]$$
-* homomorphic addition: $$E[x] + E[y] = E[x + y]$$
-* homomorphic multiplication: $$E[x] * E[y] = E[x * y]$$
+### References & Explanations
-Zama's variant of TFHE is fully homomorphic and deals with fixed-precision numbers as messages. It implements all needed homomorphic operations, such as addition and function evaluation via **Programmable Bootstrapping**. You can read more about Zama's TFHE variant in the [preliminary whitepaper](https://whitepaper.zama.ai/).
+Take a deep dive into TFHE-rs, exploring APIs from the highest to the lowest level of abstraction and accessing additional resources for in-depth explanations.
-Using FHE in a Rust program with TFHE-rs consists in:
+* [Rust API reference](https://docs.rs/tfhe/latest/tfhe/): High-level API that abstracts cryptographic complexities and simplifies the development and more
+* [Fine-grained APIs](references/fine-grained-apis/): Mid-level APIs that enable evaluation of Boolean, short integer, and integer circuits
+* [Core crypto API](references/core-crypto-api/): Low-level API with the primitive functions and types of the TFHE scheme
+* [TFHE deep dive](explanations/tfhe-deep-dive.md): Resources that explain the Fully Homomorphic Encryption scheme - TFHE
-* generating a client key and a server key using secure parameters:
- * a client key encrypts/decrypts data and must be kept secret
- * a server key is used to perform operations on encrypted data and could be public (also called an evaluation key)
-* encrypting plaintexts using the client key to produce ciphertexts
-* operating homomorphically on ciphertexts with the server key
-* decrypting the resulting ciphertexts into plaintexts using the client key
+### Support channels
-If you would like to know more about the problems that FHE solves, we suggest you review our [6 minute introduction to homomorphic encryption](https://6min.zama.ai/).
+Ask technical questions and discuss with the community. Our team of experts usually answers within 24 hours during working days.
+
+* [Community forum](https://community.zama.ai/)
+* [Discord channel](https://discord.com/invite/fhe-org)
+
+### Developers
+
+Collaborate with us to advance the FHE spaces and drive innovation together.
+
+* [Contribute to TFHE-rs](dev/contributing.md)
+* [Check the latest release note](https://github.com/zama-ai/tfhe-rs/releases)
+* [Request a feature](https://github.com/zama-ai/tfhe-rs/issues/new?assignees=\&labels=feature\_request\&projects=\&template=feature\_request.md\&title=)
+* [Report a bug](https://github.com/zama-ai/tfhe-rs/issues/new?assignees=\&labels=triage\_required\&projects=\&template=bug\_report.md\&title=)
+
+***
+
+We value your feedback! [Take a 5-question developer survey](https://www.zama.ai/developer-survey) to improve the TFHE-rs library and the documentation and help other developers use FHE.
diff --git a/tfhe/docs/SUMMARY.md b/tfhe/docs/SUMMARY.md
index 00f874c53..e1605bf44 100644
--- a/tfhe/docs/SUMMARY.md
+++ b/tfhe/docs/SUMMARY.md
@@ -1,63 +1,78 @@
# Table of contents
-* [What is TFHE-rs?](README.md)
+* [Welcome to TFHE-rs](README.md)
## Getting Started
-* [Installation](getting_started/installation.md)
-* [Quick Start](getting_started/quick_start.md)
-* [Types & Operations](getting_started/operations.md)
-* [Benchmarks](getting_started/benchmarks.md)
-* [Security and Cryptography](getting_started/security_and_cryptography.md)
+
+* [What is TFHE-rs?](getting\_started/readme.md)
+* [Installation](getting\_started/installation.md)
+* [Quick start](getting\_started/quick\_start.md)
+* [Types & Operations](getting\_started/operations.md)
+* [Benchmarks](getting\_started/benchmarks.md)
+* [Security and cryptography](getting\_started/security\_and\_cryptography.md)
+
+## Fundamentals
+
+* [Configure and generate keys](fundamentals/configure-and-generate-keys.md)
+* [Set the server key](fundamentals/set-the-server-key.md)
+* [Encrypt data](fundamentals/encrypt-data.md)
+* [Compute on encrypted data](fundamentals/compute.md)
+* [Decrypt data](fundamentals/decrypt-data.md)
+* [Generate encrypted pseudo random values](fundamentals/encrypted-prf.md)
+* [Serialize/Deserialize](fundamentals/serialization.md)
+* [Compress ciphertexts/Keys](fundamentals/compress.md)
+* [Debug](fundamentals/debug.md)
+
+## Guides
+
+* [Configure Rust](guides/rust\_configuration.md)
+* [Run on GPU](guides/run\_on\_gpu.md)
+* [Detect overflow](guides/overflow\_operations.md)
+* [Migrate data to newer versions of TFHE-rs](guides/migrate\_data.md)
+* [Use public key encryption](guides/public\_key.md)
+* [Generic function bounds](guides/trait\_bounds.md)
+* [Use parallelized PBS](guides/parallelized\_pbs.md)
+* [Use the C API](guides/c\_api.md)
+* [Use the JS on WASM API](guides/js\_on\_wasm\_api.md)
+* [Use multi-threading using the rayon crate](guides/rayon\_crate.md)
+* [Use trivial ciphertexts](guides/trivial\_ciphertext.md)
+* [PBS statistics](guides/pbs-stats.md)
## Tutorials
-* [Homomorphic Parity Bit](tutorials/parity_bit.md)
-* [Homomorphic Case Changing on Ascii String](tutorials/ascii_fhe_string.md)
-## How To
-* [Run on GPU](how_to/run_on_gpu.md)
-* [Configure Rust](how_to/rust_configuration.md)
-* [Detect Overflow](how_to/overflow_operations.md)
-* [Serialize/Deserialize](how_to/serialization.md)
-* [Migrate Data to Newer Versions of TFHE-rs](how_to/migrate_data.md)
-* [Compress Ciphertexts/Keys](how_to/compress.md)
-* [Use Public Key Encryption](how_to/public_key.md)
-* [Use Trivial Ciphertext](how_to/trivial_ciphertext.md)
-* [Generic Function Bounds](how_to/trait_bounds.md)
-* [Use Parallelized PBS](how_to/parallelized_pbs.md)
-* [Use the C API](how_to/c_api.md)
-* [Use the JS on WASM API](how_to/js_on_wasm_api.md)
-* [Use multi-threading using the rayon crate](how_to/rayon_crate.md)
-* [Debug](how_to/debug.md)
+* [See all tutorials](tutorials/see-all-tutorials.md)
+* [Homomorphic parity bit](tutorials/parity\_bit.md)
+* [Homomorphic case changing on Ascii string](tutorials/ascii\_fhe\_string.md)
+* [SHA256 with Boolean API](tutorials/sha256\_bool.md)
-## Fine-grained APIs
-* [Quick Start](fine_grained_api/quick_start.md)
-* [Boolean](fine_grained_api/Boolean/readme.md)
- * [Operations](fine_grained_api/Boolean/operations.md)
- * [Cryptographic Parameters](fine_grained_api/Boolean/parameters.md)
- * [Serialization/Deserialization](fine_grained_api/Boolean/serialization.md)
+## References
-* [Shortint](fine_grained_api/shortint/readme.md)
- * [Operations](fine_grained_api/shortint/operations.md)
- * [Cryptographic Parameters](fine_grained_api/shortint/parameters.md)
- * [Serialization/Deserialization](fine_grained_api/shortint/serialization.md)
+* [API references](https://docs.rs/tfhe/latest/tfhe/)
+* [Fine-grained APIs](references/fine-grained-apis/README.md)
+ * [Quick start](references/fine-grained-apis/quick\_start.md)
+ * [Boolean](references/fine-grained-apis/boolean/README.md)
+ * [Operations](references/fine-grained-apis/boolean/operations.md)
+ * [Cryptographic parameters](references/fine-grained-apis/boolean/parameters.md)
+ * [Serialization/Deserialization](references/fine-grained-apis/boolean/serialization.md)
+ * [Shortint](references/fine-grained-apis/shortint/README.md)
+ * [Operations](references/fine-grained-apis/shortint/operations.md)
+ * [Cryptographic parameters](references/fine-grained-apis/shortint/parameters.md)
+ * [Serialization/Deserialization](references/fine-grained-apis/shortint/serialization.md)
+ * [Integer](references/fine-grained-apis/integer/README.md)
+ * [Operations](references/fine-grained-apis/integer/operations.md)
+ * [Cryptographic parameters](references/fine-grained-apis/integer/parameters.md)
+ * [Serialization/Deserialization](references/fine-grained-apis/integer/serialization.md)
+* [Core crypto API](references/core-crypto-api/README.md)
+ * [Quick start](references/core-crypto-api/presentation.md)
+ * [Tutorial](references/core-crypto-api/tutorial.md)
-* [Integer](fine_grained_api/integer/readme.md)
- * [Operations](fine_grained_api/integer/operations.md)
- * [Cryptographic Parameters](fine_grained_api/integer/parameters.md)
- * [Serialization/Deserialization](fine_grained_api/integer/serialization.md)
+## Explanations
-## Application Tutorials
-* [SHA256 with *Boolean API*](application_tutorials/sha256_bool.md)
-* [Dark Market with *Integer API*](application_tutorials/dark_market.md)
-* [Homomorphic Regular Expressions *Integer API*](application_tutorials/regex.md)
-
-
-## Crypto Core API [Advanced users]
-* [Quick Start](core_crypto/presentation.md)
-* [Tutorial](core_crypto/tutorial.md)
+* [TFHE deep dive](explanations/tfhe-deep-dive.md)
## Developers
-* [Contributing](dev/contributing.md)
-## API references
-* [docs.rs](https://docs.rs/tfhe/)
+* [Contributing](dev/contributing.md)
+* [Release note](https://github.com/zama-ai/tfhe-rs/releases)
+* [Feature request](https://github.com/zama-ai/tfhe-rs/issues/new?assignees=\&labels=feature\_request\&projects=\&template=feature\_request.md\&title=)
+* [Bug report](https://github.com/zama-ai/tfhe-rs/issues/new?assignees=\&labels=triage\_required\&projects=\&template=bug\_report.md\&title=)
diff --git a/tfhe/docs/application_tutorials/dark_market.md b/tfhe/docs/application_tutorials/dark_market.md
deleted file mode 100644
index d5c329eef..000000000
--- a/tfhe/docs/application_tutorials/dark_market.md
+++ /dev/null
@@ -1,527 +0,0 @@
-# Dark Market Tutorial
-
-In this tutorial, we are going to build a dark market application using TFHE-rs. A dark market is a marketplace where
-buy and sell orders are not visible to the public before they are filled. Different algorithms aim to
-solve this problem, we are going to implement the algorithm defined [in this paper](https://eprint.iacr.org/2022/923.pdf) with TFHE-rs.
-
-We will first implement the algorithm in plain Rust and then we will see how to use TFHE-rs to
-implement the same algorithm with FHE.
-
-In addition, we will also implement a modified version of the algorithm that allows for more concurrent operations which
-improves the performance in hardware where there are multiple cores.
-
-## Specifications
-
-#### Inputs:
-
-* A list of sell orders where each sell order is only defined in volume terms, it is assumed that the price is fetched
- from a different source.
-* A list of buy orders where each buy order is only defined in volume terms, it is assumed that the price is fetched
- from a different source.
-
-#### Input constraints:
-
-* The sell and buy orders are within the range [1,100].
-* The maximum number of sell and buy orders is 500, respectively.
-
-#### Outputs:
-
-There is no output returned at the end of the algorithm. Instead, the algorithm makes changes on the given input lists.
-The number of filled orders is written over the original order count in the respective lists. If it is not possible to
-fill the orders, the order count is set to zero.
-
-#### Example input and output:
-
-##### Example 1:
-
-| | Sell | Buy |
-|--------|--------------------|-----------|
-| Input | [ 5, 12, 7, 4, 3 ] | [ 19, 2 ] |
-| Output | [ 5, 12, 4, 0, 0 ] | [ 19, 2 ] |
-
-Last three indices of the filled sell orders are zero because there is no buy orders to match them.
-
-##### Example 2:
-
-| | Sell | Buy |
-|--------|-------------------|----------------------|
-| Input | [ 3, 1, 1, 4, 2 ] | [ 5, 3, 3, 2, 4, 1 ] |
-| Output | [ 3, 1, 1, 4, 2 ] | [ 5, 3, 3, 0, 0, 0 ] |
-
-Last three indices of the filled buy orders are zero because there is no sell orders to match them.
-
-## Plain Implementation
-
-1. Calculate the total sell volume and the total buy volume.
-
-```rust
-let total_sell_volume: u16 = sell_orders.iter().sum();
-let total_buy_volume: u16 = buy_orders.iter().sum();
-```
-
-2. Find the total volume that will be transacted. In the paper, this amount is calculated with the formula:
-
-```
-(total_sell_volume > total_buy_volume) * (total_buy_volume − total_sell_volume) + total_sell_volume
-```
-
-When closely observed, we can see that this formula can be replaced with the `min` function. Therefore, we calculate this
-value by taking the minimum of the total sell volume and the total buy volume.
-
-```rust
-let total_volume = std::cmp::min(total_buy_volume, total_sell_volume);
-```
-
-3. Beginning with the first item, start filling the sell orders one by one. We apply the `min` function replacement also
- here.
-
-```rust
-let mut volume_left_to_transact = total_volume;
-for sell_order in sell_orders.iter_mut() {
- let filled_amount = std::cmp::min(volume_left_to_transact, *sell_order);
- *sell_order = filled_amount;
- volume_left_to_transact -= filled_amount;
-}
-```
-
-The number of orders that are filled is indicated by modifying the input list. For example, if the first sell order is
-1000 and the total volume is 500, then the first sell order will be modified to 500 and the second sell order will be
-modified to 0.
-
-4. Do the fill operation also for the buy orders.
-
-```rust
-let mut volume_left_to_transact = total_volume;
-for buy_order in buy_orders.iter_mut() {
- let filled_amount = std::cmp::min(volume_left_to_transact, *buy_order);
- *buy_order = filled_amount;
- volume_left_to_transact -= filled_amount;
-}
-```
-
-#### The complete algorithm in plain Rust:
-
-```rust
-fn fill_orders(orders: &mut [u16], total_volume: u16) {
- let mut volume_left_to_transact = total_volume;
- for order in orders {
- let filled_amount = std::cmp::min(volume_left_to_transact, *order);
- *order = filled_amount;
- volume_left_to_transact -= filled_amount;
- }
-}
-
-pub fn volume_match(sell_orders: &mut [u16], buy_orders: &mut [u16]) {
- let total_sell_volume: u16 = sell_orders.iter().sum();
- let total_buy_volume: u16 = buy_orders.iter().sum();
-
- let total_volume = std::cmp::min(total_buy_volume, total_sell_volume);
-
- fill_orders(sell_orders, total_volume);
- fill_orders(buy_orders, total_volume);
-}
-```
-
-## FHE Implementation
-
-For the FHE implementation, we first start with finding the right bit size for our algorithm to work without
-overflows.
-
-The variables that are declared in the algorithm and their maximum values are described in the table below:
-
-| Variable | Maximum Value | Bit Size |
-|-------------------------|---------------|----------|
-| total_sell_volume | 50000 | 16 |
-| total_buy_volume | 50000 | 16 |
-| total_volume | 50000 | 16 |
-| volume_left_to_transact | 50000 | 16 |
-| sell_order | 100 | 7 |
-| buy_order | 100 | 7 |
-
-As we can observe from the table, we need **16 bits of message space** to be able to run the algorithm without
-overflows. TFHE-rs provides different presets for the different bit sizes. Since we need 16 bits of message, we are
-going to use the `integer` module to implement the algorithm.
-
-Here are the input types of our algorithm:
-
-* `sell_orders` is of type `Vec`
-* `buy_orders` is of type `Vec`
-* `server_key` is of type `tfhe::integer::ServerKey`
-
-Now, we can start implementing the algorithm with FHE:
-
-1. Calculate the total sell volume and the total buy volume.
-
-```rust
-fn vector_sum(server_key: &ServerKey, orders: &mut [RadixCiphertext]) -> RadixCiphertext {
- let mut total_volume = server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS);
- for order in orders {
- server_key.smart_add_assign(&mut total_volume, order);
- }
- total_volume
-}
-
-let mut total_sell_volume = vector_sum(server_key, sell_orders);
-let mut total_buy_volume = vector_sum(server_key, buy_orders);
-
-```
-
-2. Find the total volume that will be transacted by taking the minimum of the total sell volume and the total buy
- volume.
-
-```rust
-let total_volume = server_key.smart_min(&mut total_sell_volume, &mut total_buy_volume);
-```
-
-3. Beginning with the first item, start filling the sell and buy orders one by one. We can create `fill_orders` closure to
-reduce code duplication since the code for filling buy orders and sell orders are the same.
-
-```rust
-fn fill_orders(
- server_key: &ServerKey,
- orders: &mut [RadixCiphertext],
- total_volume: RadixCiphertext,
-) {
- let mut volume_left_to_transact = total_volume;
- for order in orders {
- let mut filled_amount = server_key.smart_min(&mut volume_left_to_transact, order);
- server_key.smart_sub_assign(&mut volume_left_to_transact, &mut filled_amount);
- *order = filled_amount;
- }
-}
-
-fill_orders(server_key, sell_orders, total_volume.clone());
-fill_orders(server_key, buy_orders, total_volume);
-```
-
-#### The complete algorithm in TFHE-rs:
-
-```rust
-const NUMBER_OF_BLOCKS: usize = 8;
-
-fn vector_sum(server_key: &ServerKey, orders: &mut [RadixCiphertext]) -> RadixCiphertext {
- let mut total_volume = server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS);
- for order in orders {
- server_key.smart_add_assign(&mut total_volume, order);
- }
- total_volume
-}
-
-fn fill_orders(
- server_key: &ServerKey,
- orders: &mut [RadixCiphertext],
- total_volume: RadixCiphertext,
-) {
- let mut volume_left_to_transact = total_volume;
- for order in orders {
- let mut filled_amount = server_key.smart_min(&mut volume_left_to_transact, order);
- server_key.smart_sub_assign(&mut volume_left_to_transact, &mut filled_amount);
- *order = filled_amount;
- }
-}
-
-pub fn volume_match(
- sell_orders: &mut [RadixCiphertext],
- buy_orders: &mut [RadixCiphertext],
- server_key: &ServerKey,
-) {
- let mut total_sell_volume = vector_sum(server_key, sell_orders);
- let mut total_buy_volume = vector_sum(server_key, buy_orders);
-
- let total_volume = server_key.smart_min(&mut total_sell_volume, &mut total_buy_volume);
-
- fill_orders(server_key, sell_orders, total_volume.clone());
- fill_orders(server_key, buy_orders, total_volume);
-}
-```
-
-### Optimizing the implementation
-
-* TFHE-rs provides parallelized implementations of the operations. We can use these parallelized
- implementations to speed up the algorithm. For example, we can use `smart_add_assign_parallelized` instead of
- `smart_add_assign`.
-
-* We can parallelize vector sum with Rayon and `reduce` operation.
-```rust
-fn vector_sum(server_key: &ServerKey, orders: Vec) -> RadixCiphertext {
- orders.into_par_iter().reduce(
- || server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS),
- |mut acc: RadixCiphertext, mut ele: RadixCiphertext| {
- server_key.smart_add_parallelized(&mut acc, &mut ele)
- },
- )
-}
-```
-
-* We can run vector summation on `buy_orders` and `sell_orders` in parallel since these operations do not depend on each other.
-```rust
-let (mut total_sell_volume, mut total_buy_volume) = rayon::join(
- || vector_sum(server_key, sell_orders.to_owned()),
- || vector_sum(server_key, buy_orders.to_owned()),
-);
-```
-
-* We can match sell and buy orders in parallel since the matching does not depend on each other.
-```rust
-rayon::join(
- || fill_orders(server_key, sell_orders, total_volume.clone()),
- || fill_orders(server_key, buy_orders, total_volume.clone()),
-);
-```
-
-#### Optimized algorithm
-```rust
-fn vector_sum(server_key: &ServerKey, orders: Vec) -> RadixCiphertext {
- orders.into_par_iter().reduce(
- || server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS),
- |mut acc: RadixCiphertext, mut ele: RadixCiphertext| {
- server_key.smart_add_parallelized(&mut acc, &mut ele)
- },
- )
-}
-
-fn fill_orders(
- server_key: &ServerKey,
- orders: &mut [RadixCiphertext],
- total_volume: RadixCiphertext,
-) {
- let mut volume_left_to_transact = total_volume;
- for order in orders {
- let mut filled_amount =
- server_key.smart_min_parallelized(&mut volume_left_to_transact, order);
- server_key.smart_sub_assign_parallelized(&mut volume_left_to_transact, &mut filled_amount);
- *order = filled_amount;
- }
-}
-
-pub fn volume_match(
- sell_orders: &mut [RadixCiphertext],
- buy_orders: &mut [RadixCiphertext],
- server_key: &ServerKey,
-) {
- let (mut total_sell_volume, mut total_buy_volume) = rayon::join(
- || vector_sum(server_key, sell_orders.to_owned()),
- || vector_sum(server_key, buy_orders.to_owned()),
- );
-
- let total_volume =
- server_key.smart_min_parallelized(&mut total_sell_volume, &mut total_buy_volume);
- rayon::join(
- || fill_orders(server_key, sell_orders, total_volume.clone()),
- || fill_orders(server_key, buy_orders, total_volume.clone()),
- );
-}
-```
-
-## Modified Algorithm
-
-When observed closely, there is only a small amount of concurrency introduced in the `fill_orders` part of the algorithm.
-The reason is that the `volume_left_to_transact` is shared between all the orders and should be modified sequentially.
-This means that the orders cannot be filled in parallel. If we can somehow remove this dependency, we can fill the orders in parallel.
-
-In order to do so, we closely observe the function of `volume_left_to_transact` variable in the algorithm. We can see that it is being used to check whether we can fill the current order or not.
-Instead of subtracting the current order value from `volume_left_to_transact` in each loop, we can add this value to the next order
-index and check the availability by comparing the current order value with the total volume. If the current order value
-(now representing the sum of values before this order plus this order) is smaller than the total number of matching orders,
-we can safely fill all the orders and continue the loop. If not, we should partially fill the orders with what is left from
-matching orders.
-
-We will call the new list the "prefix sum" of the array.
-
-The new version for the plain `fill_orders` is as follows:
-```rust
-fn fill_orders(total_orders: u16, orders: &mut [u16], prefix_sum_arr: &[u16]) {
- orders.iter().for_each(|order : &mut u64| {
- let previous_prefix_sum = if i == 0 { 0 } else { prefix_sum_arr[i - 1] };
-
- let diff = total_orders as i64 - previous_prefix_sum as i64;
-
- if (diff < 0) {
- *order = 0;
- } else if diff < order {
- *order = diff as u16;
- } else {
- // *order = *order;
- continue;
- }
- });
-};
-```
-
-To write this new function we need transform the conditional code into a mathematical expression since FHE does not support conditional operations.
-```rust
-
-fn fill_orders(total_orders: u16, orders: &mut [u16], prefix_sum_arr: &[u16]) {
- for (i, order) in orders.iter_mut().enumerate() {
- let previous_prefix_sum = if i == 0 { 0 } else { prefix_sum_arr[i - 1] };
-
- *order = (total_orders as i64 - previous_prefix_sum as i64)
- .max(0)
- .min(*order as i64) as u16;
- }
-}
-```
-
-New `fill_order` function requires a prefix sum array. We are going to calculate this prefix sum array in parallel
-with the algorithm described [here](https://developer.nvidia.com/gpugems/gpugems3/part-vi-gpu-computing/chapter-39-parallel-prefix-sum-scan-cuda).
-
-The sample code in the paper is written in CUDA. When we try to implement the algorithm in Rust we see that the compiler does not allow us to do so.
-The reason for that is while the algorithm does not access the same array element in any of the threads(the index calculations using `d` and `k` values never overlap),
-Rust compiler cannot understand this and does not let us share the same array between threads.
-So we modify how the algorithm is implemented, but we don't change the algorithm itself.
-
-Here is the modified version of the algorithm in TFHE-rs:
-```rust
-fn compute_prefix_sum(server_key: &ServerKey, arr: &[RadixCiphertext]) -> Vec {
- if arr.is_empty() {
- return arr.to_vec();
- }
- let mut prefix_sum: Vec = (0..arr.len().next_power_of_two())
- .into_par_iter()
- .map(|i| {
- if i < arr.len() {
- arr[i].clone()
- } else {
- server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS)
- }
- })
- .collect();
- for d in 0..prefix_sum.len().ilog2() {
- prefix_sum
- .par_chunks_exact_mut(2_usize.pow(d + 1))
- .for_each(move |chunk| {
- let length = chunk.len();
- let mut left = chunk.get((length - 1) / 2).unwrap().clone();
- server_key.smart_add_assign_parallelized(chunk.last_mut().unwrap(), &mut left)
- });
- }
- let last = prefix_sum.last().unwrap().clone();
- *prefix_sum.last_mut().unwrap() = server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS);
- for d in (0..prefix_sum.len().ilog2()).rev() {
- prefix_sum
- .par_chunks_exact_mut(2_usize.pow(d + 1))
- .for_each(move |chunk| {
- let length = chunk.len();
- let temp = chunk.last().unwrap().clone();
- let mut mid = chunk.get((length - 1) / 2).unwrap().clone();
- server_key.smart_add_assign_parallelized(chunk.last_mut().unwrap(), &mut mid);
- chunk[(length - 1) / 2] = temp;
- });
- }
- prefix_sum.push(last);
- prefix_sum[1..=arr.len()].to_vec()
-}
-
-fn fill_orders(
- server_key: &ServerKey,
- total_orders: &RadixCiphertext,
- orders: &mut [RadixCiphertext],
- prefix_sum_arr: &[RadixCiphertext],
-) {
- orders
- .into_par_iter()
- .enumerate()
- .for_each(move |(i, order)| {
- // (total_orders - previous_prefix_sum).max(0)
- let mut diff = if i == 0 {
- total_orders.clone()
- } else {
- let previous_prefix_sum = &prefix_sum_arr[i - 1];
-
- // total_orders - previous_prefix_sum
- let mut diff = server_key.smart_sub_parallelized(
- &mut total_orders.clone(),
- &mut previous_prefix_sum.clone(),
- );
-
- // total_orders > prefix_sum
- let mut cond = server_key.smart_gt_parallelized(
- &mut total_orders.clone(),
- &mut previous_prefix_sum.clone(),
- );
-
- // (total_orders - previous_prefix_sum) * (total_orders > previous_prefix_sum)
- // = (total_orders - previous_prefix_sum).max(0)
- server_key.smart_mul_parallelized(&mut cond, &mut diff)
- };
-
- // (total_orders - previous_prefix_sum).max(0).min(*order);
- *order = server_key.smart_min_parallelized(&mut diff, order);
- });
-}
-
-/// FHE implementation of the volume matching algorithm.
-///
-/// In this function, the implemented algorithm is modified to utilize more concurrency.
-///
-/// Matches the given encrypted [sell_orders] with encrypted [buy_orders] using the given
-/// [server_key]. The amount of the orders that are successfully filled is written over the original
-/// order count.
-pub fn volume_match(
- sell_orders: &mut [RadixCiphertext],
- buy_orders: &mut [RadixCiphertext],
- server_key: &ServerKey,
-) {
- println!("Creating prefix sum arrays...");
- let time = Instant::now();
- let (prefix_sum_sell_orders, prefix_sum_buy_orders) = rayon::join(
- || compute_prefix_sum(server_key, sell_orders),
- || compute_prefix_sum(server_key, buy_orders),
- );
- println!("Created prefix sum arrays in {:?}", time.elapsed());
-
- let zero = server_key.create_trivial_zero_radix(NUMBER_OF_BLOCKS);
-
- let total_buy_orders = prefix_sum_buy_orders.last().unwrap_or(&zero);
-
- let total_sell_orders = prefix_sum_sell_orders.last().unwrap_or(&zero);
-
- println!("Matching orders...");
- let time = Instant::now();
- rayon::join(
- || {
- fill_orders(
- server_key,
- total_sell_orders,
- buy_orders,
- &prefix_sum_buy_orders,
- )
- },
- || {
- fill_orders(
- server_key,
- total_buy_orders,
- sell_orders,
- &prefix_sum_sell_orders,
- )
- },
- );
- println!("Matched orders in {:?}", time.elapsed());
-}
-```
-
-## Running the tutorial
-
-The plain, FHE and parallel FHE implementations can be run by providing respective arguments as described below.
-
-```bash
-# Runs FHE implementation
-cargo run --release --package tfhe --example dark_market --features="integer internal-keycache" -- fhe
-
-# Runs parallelized FHE implementation
-cargo run --release --package tfhe --example dark_market --features="integer internal-keycache" -- fhe-parallel
-
-# Runs modified FHE implementation
-cargo run --release --package tfhe --example dark_market --features="integer internal-keycache" -- fhe-modified
-
-# Runs plain implementation
-cargo run --release --package tfhe --example dark_market --features="integer internal-keycache" -- plain
-
-# Multiple implementations can be run within same instance
-cargo run --release --package tfhe --example dark_market --features="integer internal-keycache" -- plain fhe-parallel
-```
-
-## Conclusion
-
-In this tutorial, we've learned how to implement the volume matching algorithm described [in this paper](https://eprint.iacr.org/2022/923.pdf) in plain Rust and in TFHE-rs.
-We've identified the right bit size for our problem at hand, used operations defined in `TFHE-rs`, and introduced concurrency to the algorithm to increase its performance.
diff --git a/tfhe/docs/application_tutorials/regex.md b/tfhe/docs/application_tutorials/regex.md
deleted file mode 100644
index 177c21782..000000000
--- a/tfhe/docs/application_tutorials/regex.md
+++ /dev/null
@@ -1,512 +0,0 @@
-# FHE Regex Pattern Matching Tutorial
-
-This tutorial explains how to build a regex Pattern Matching Engine (PME) where ciphertext is the
-content that is evaluated.
-
-A regex PME is an essential tool for programmers. It allows you to perform complex searches on content.
-A less powerful simple search on string can only find matches of the exact given sequence of
-characters (e.g., your browser's default search function). Regex PMEs
-are more powerful, allowing searches on certain structures of text, where a
-structure may take any form in multiple possible sequences of characters. The
-structure to be searched is defined with the regex, a very concise
-language.
-
-Here are some example regexes to give you an idea of what is possible:
-
-Regex | Semantics
---- | ---
-/abc/ | Searches for the sequence `abc` (equivalent to a simple text search)
-/^abc/ | Searches for the sequence `abc` at the beginning of the content
-/a?bc/ | Searches for sequences `abc`, `bc`
-/ab\|c+d/ | Searches for sequences of `ab`, `c` repeated 1 or more times, followed by `d`
-
-Regexes are powerful enough to be able to express structures like email address
-formats. This capability is what makes regexes useful for many programming
-solutions.
-
-There are two main components identifiable in a PME:
-1. The pattern that is to be matched has to be parsed, translated from a
- textual representation into a recursively structured object (an Abstract
- Syntax Tree, or AST).
-2. This AST must then be applied to the text that it is to be matched against,
- resulting in a 'yes' or 'no' to whether the pattern has matched (in the case of
- our FHE implementation, this result is an encrypted 'yes' or an encrypted 'no').
-
-Parsing is a well understood problem. There are a couple of different
-approaches possible here. Regardless of the approach chosen, it starts with
-figuring out what language we want to support. That is, what are
-the kinds of sentences we want our regex language to include? A few
-example sentences we definitely want to support are, for example: `/a/`,
-`/a?bc/`, `/^ab$/`, `/ab|cd/`, however example sentences don't suffice as
-a specification because they can never be exhaustive (they're endless). We need
-something to specify _exactly_ the full set of sentences our language supports.
-There exists a language that can help us describe our own language's structure exactly:
-Grammar.
-
-## The Grammar and datastructure
-
-It is useful to start with defining the Grammar before starting to write
-code for the parser because the code structure follows directly from the
-Grammar. A Grammar consists of a generally small set of rules. For example,
-a very basic Grammar could look like this:
-```
-Start := 'a'
-```
-This describes a language that only contains the sentence "a". Not a very interesting language.
-
-We can make it more interesting though by introducing choice into the Grammar
-with \| (called a 'pipe') operators. If we want the above Grammar to accept
-either "a" or "b":
-```
-Start := 'a' | 'b'
-```
-
-So far, only Grammars with a single rule have been shown. However, a Grammar can
-consist of multiple rules. Most languages require it. So let's consider a more meaningful language,
-one that accepts sentences consisting of one or more digits. We could describe such a language
-with the following Grammar:
-```
-Start := Digit+
-
-Digit := '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
-```
-
-The `+` after `Digit` is another Grammar operator. With it, we specify that
-Digit must be matched one or more times. Here are all the Grammar operators that
-are relevant for this tutorial:
-
-Operator | Example | Semantics
---- | --- | ---
-`\|` | a \| b | we first try matching on 'a' - if no match, we try to match on 'b'
-`+` | a+ | match 'a' one or more times
-`*` | a* | match 'a' any amount of times (including zero times)
-`?` | a? | optionally match 'a' (match zero or one time)
-`.` | . | match any character
-`..` | a .. b | match on a range of alphabetically ordered characters from 'a', up to and including 'b'
-` ` | a b | sequencing; match on 'a' and then on 'b'
-
-In the case of the example PME, the Grammar is as follows (notice the unquoted ? and quoted ?, etc. The unquoted characters are Grammar operators, and the quoted are characters we are matching in the parsing).
-```
-Start := '/' '^'? Regex '$'? '/' Modifier?
-
-Regex := Term '|' Term
- | Term
-
-Term := Factor*
-
-Factor := Atom '?'
- | Repeated
- | Atom
-
-Repeated := Atom '*'
- | Atom '+'
- | Atom '{' Digit* ','? '}'
- | Atom '{' Digit+ ',' Digit* '}'
-
-Atom := '.'
- | '\' .
- | Character
- | '[' Range ']'
- | '(' Regex ')'
-
-Range := '^' Range
- | AlphaNum '-' AlphaNum
- | AlphaNum+
-
-Digit := '0' .. '9'
-
-Character := AlphaNum
- | '&' | ';' | ':' | ',' | '`' | '~' | '-' | '_' | '!' | '@' | '#' | '%' | '\'' | '\"'
-
-AlphaNum := 'a' .. 'z'
- | 'A' .. 'Z'
- | '0' .. '9'
-
-Modifier := 'i'
-```
-We will refer occasionally to specific parts in the Grammar listed above by \.\ (where the first rule variant has index 1).
-
-With the Grammar defined, we can start defining a type to parse into. In Rust, we
-have the `enum` kind of type that is perfect for this, as it allows you to define
-multiple variants that may recurse. I prefer to start by defining variants that
-do not recurse (i.e., that don't contain nested regex expressions):
-```rust
-enum RegExpr {
- Char { c: char }, // matching against a single character (Atom.2 and Atom.3)
- AnyChar, // matching _any_ character (Atom.1)
- SOF, // matching only at the beginning of the content ('^' in Start.1)
- EOF, // matching only at the end of the content (the '$' in Start.1)
- Range { cs: Vec }, // matching on a list of characters (Range.3, eg '[acd]')
- Between { from: char, to: char }, // matching between 2 characters based on ascii ordering (Range.2, eg '[a-g]')
-}
-```
-
-With this, we can translate the following basic regexes:
-
-Pattern | RegExpr value
---- | ---
-`/a/` | `RegExpr::Char { c: 'a' }`
-`/\\^/` | `RegExpr::Char { c: '^' }`
-`/./` | `RegExpr::AnyChar`
-`/^/` | `RegExpr::SOF`
-`/$/` | `RegExpr::EOF`
-`/[acd]/` | `RegExpr::Range { vec!['a', 'c', 'd'] }`
-`/[a-g]/` | `RegExpr::Between { from: 'a', to: 'g' }`
-
-Notice we're not yet able to sequence multiple components together. Let's define
-the first variant that captures recursive RegExpr for this:
-```rust
-enum RegExpr {
- ...
- Seq { re_xs: Vec }, // matching sequences of RegExpr components (Term.1)
-}
-```
-With this Seq (short for sequence) variant, we allow translating patterns that
-contain multiple components:
-
-Pattern | RegExpr value
---- | ---
-`/ab/` | `RegExpr::Seq { re_xs: vec![RegExpr::Char { c: 'a' }, RegExpr::Char { c: 'b' }] }`
-`/^a.$/` | `RegExpr::Seq { re_xs: vec![RegExpr::SOF, RexExpr::Char { 'a' }, RegExpr::AnyChar, RegExpr::EOF] }`
-`/a[f-l]/` | `RegExpr::Seq { re_xs: vec![RegExpr::Char { c: 'a' }, RegExpr::Between { from: 'f', to: 'l' }] }`
-
-Let's finish the RegExpr datastructure by adding variants for 'Optional' matching,
-'Not' logic in a range, and 'Either' left or right matching:
-```rust
-enum RegExpr {
- ...
- Optional { opt_re: Box }, // matching optionally (Factor.1)
- Not { not_re: Box }, // matching inversely on a range (Range.1)
- Either { l_re: Box, r_re: Box }, // matching the left or right regex (Regex.1)
-}
-```
-
-Some features may make the most sense being implemented during post-processing of
-the parsed datastructure. For example, the case insensitivity feature (the `i`
-Modifier) is implemented in the example implementation by taking the parsed
-RegExpr and mutating every character mentioned inside to cover both the lower
-case as well as the upper case variant (see function `case_insensitive` in
-`parser.rs` for the example implementation).
-
-The modifier `i` in our Grammar (for enabling case insensitivity) was easiest
-to implement by applying a post-processing step to the parser.
-
-We are now able to translate any complex regex into a RegExpr value. For example:
-
-Pattern | RegExpr value
---- | ---
-`/a?/` | `RegExpr::Optional { opt_re: Box::new(RegExpr::Char { c: 'a' }) }`
-`/[a-d]?/` | `RegExpr::Optional { opt_re: Box::new(RegExpr::Between { from: 'a', to: 'd' }) }`
-`/[^ab]/` | `RegExpr::Not { not_re: Box::new(RegExpr::Range { cs: vec!['a', 'b'] }) }`
-`/av\|d?/` | `RegExpr::Either { l_re: Box::new(RegExpr::Seq { re_xs: vec![RegExpr::Char { c: 'a' }, RegExpr::Char { c: 'v' }] }), r_re: Box::new(RegExpr::Optional { opt_re: Box::new(RegExpr::Char { c: 'd' }) }) }`
-`/(av\|d)?/` | `RegExpr::Optional { opt_re: Box::new(RegExpr::Either { l_re: Box::new(RegExpr::Seq { re_xs: vec![RegExpr::Char { c: 'a' }, RegExpr::Char { c: 'v' }] }), r_re: Box::new(RegExpr::Char { c: 'd' }) }) }`
-
-With both the Grammar and the datastructure to parse into defined, we can now
-start implementing the actual parsing logic. There are multiple ways this can
-be done. For example, there exist tools that can automatically generate parser
-code by giving it the Grammar definition (these are called parser generators).
-However, you might prefer to write parsers with a parser combinator library.
-This may be the better option for you because the behavior in runtime is easier to understand
-for parsers constructed with a parser combinator library than of parsers that were
-generated with a parser generator tool.
-
-Rust offers a number of popular parser combinator libraries. This tutorial used
-`combine`, but any other library would work just as well. Choose whichever appeals
-the most to you (including any parser generator tool). The implementation of
-our regex parser will differ significantly depending on the approach you choose,
-so we will not cover this in detail here. You may look at the parser code in the example
-implementation to get an idea of how this could be done. In general though, the Grammar and the
-datastructure are the important components, while the parser code follows directly from these.
-
-## Matching the RegExpr to encrypted content
-
-The next challenge is to build the execution engine, where we take a RegExpr
-value and recurse into it to apply the necessary actions on the encrypted
-content. We first have to define how we actually encode our content into an
-encrypted state. Once that is defined, we can start working on how we will
-execute our RegExpr onto the encrypted content.
-
-### Encoding and encrypting the content.
-
-It is not possible to encrypt the entire content into a single encrypted value.
-We can only encrypt numbers and perform operations on those encrypted numbers with
-FHE. Therefore, we have to find a scheme where we encode the content into a
-sequence of numbers that are then encrypted individually to form a sequence of
-encrypted numbers.
-
-We recommend the following two strategies:
-1. to map each character of the content into the u8 ascii value, and then encrypt
- each bit of these u8 values individually.
-2. to, instead of encrypting each bit individually, encrypt each u8 ascii value in
- its entirety.
-
-Strategy 1 requires more high-level TFHE-rs operations to check for
-a simple character match (we have to check each bit individually for
-equality as opposed to checking the entire byte in one, high-level TFHE-rs
-operation), though some experimentation did show that both options performed
-equally well on a regex like `/a/`. This is likely because bitwise FHE
-operations are relatively cheap compared to u8 FHE operations. However,
-option 1 falls apart as soon as you introduce '[a-z]' regex logic.
-With option 2, it is possible to complete this match with just three TFHE-rs
-operations: `ge`, `le`, and `bitand`.
-```rust
-// note: this is pseudocode
-c = ;
-sk =
-
-ge_from = sk.ge(c, 'a');
-le_to = sk.le(c, 'z');
-result = sk.bitand(ge_from, le_to);
-```
-
-If, on the other hand, we had encrypted the content with the first strategy,
-there would be no way to test for `greater/equal than from` and `less/equal
-than to`. We'd have to check for the potential equality of each character between
-`from` and `to`, and then join the results together with a sequence of
-`sk.bitor`; that would require far more cryptographic operations than in strategy 2.
-
-Because FHE operations are computationally expensive, and strategy 1 requires
-significantly more FHE operations for matching on `[a-z]` regex logic, we
-should opt for strategy 2.
-
-### Matching with the AST versus matching with a derived DFA.
-
-There are a lot of regex PMEs. It's been built many times and it's been
-researched thoroughly. There are different strategies possible here.
-A straight forward strategy is to directly recurse into our RegExpr
-value and apply the necessary matching operations onto the content. In a way,
-this is nice because it allows us to link the RegExpr structure directly to
-the matching semantics, resulting in code that is easier to
-understand, maintain, etc.
-
-Alternatively, there exists an algorithm that transforms the AST (i.e., the
-RegExpr, in our case) into a Deterministic Finite Automata (DFA). Normally, this
-is a favorable approach in terms of efficiency because the derived DFA can be
-walked over without needing to backtrack (whereas the former strategy cannot
-prevent backtracking). This means that the content can be walked over from
-character to character, and depending on what the character is at this
-cursor, the DFA is conjunctively traveled in a definite direction which
-ultimately leads us to the `yes, there is a match` or the `no, there is no
-match`. There is a small upfront cost of having to translate the AST into the
-DFA, but the lack of backtracking during matching generally makes up for
-this, especially if the content that it is matched against is significantly big.
-
-In our case though, we are matching on encrypted content. We have no way to know
-what the character at our cursor is, and therefore no way to find this definite
-direction to go forward in the DFA. Therefore, translating the AST into the DFA does
-not help us as it does in normal regex PMEs. For this reason, consider opting for the
-former strategy because it allows for matching logic that is easier to understand.
-
-### Matching.
-
-In the previous section, we decided we'll match by traversing into the RegExpr
-value. This section will explain exactly how to do that. Similarly to defining
-the Grammar, it is often best to start with working out the non-recursive
-RegExpr variants.
-
-We'll start by defining the function that will recursively traverse into the RegExpr value:
-```rust
-
-type StringCiphertext = Vec;
-type ResultCiphertext = RadixCiphertext;
-
-fn match(
- sk: &ServerKey,
- content: &StringCipherText,
- re: &RegExpr,
- content_pos: usize,
-) -> Vec<(ResultCiphertext, usize)> {
- let content_char = &content[c_pos];
- match re {
- ...
- }
-}
-```
-
-`sk` is the server key (aka, public key),`content` is what we'll be matching
-against, `re` is the RegExpr value we built when parsing the regex, and `c_pos`
-is the cursor position (the index in content we are currently matching
-against).
-
-The result is a vector of tuples, with the first value of the tuple being the computed
-ciphertext result, and the second value being the content position after the
-regex components were applied. It's a vector because certain RegExpr variants
-require the consideration of a list of possible execution paths. For example,
-RegExpr::Optional might succeed by applying _or_ and *not* applying the optional
-regex (notice that in the former case, `c_pos` moves forward whereas in the
-latter case it stays put).
-
-On first call, a `match` of the entire regex pattern starts with `c_pos=0`.
-Then `match` is called again for the entire regex pattern with `c_pos=1`, etc. until
-`c_pos` exceeds the length of the content. Each of these alternative match results
-are then joined together with `sk.bitor` operations (this works because if one of them results
-in 'true' then, in general, our matching algorithm should return 'true').
-
-The `...` within the match statement above is what we will be working out for
-some of the RegExpr variants now. Starting with `RegExpr::Char`:
-```rust
-case RegExpr::Char { c } => {
- vec![(sk.eq(content_char, c), c_pos + 1)]
-},
-```
-
-Let's consider an example of the variant above. If we apply `/a/` to content
-`bac`, we'll have the following list of `match` calls `re` and `c_pos` values
-(for simplicity, `re` is denoted in regex pattern instead of in RegExpr value):
-
-re | c\_pos | Ciphertext operation
---- | --- | ---
-/a/ | 0 | sk.eq(content[0], a)
-/a/ | 1 | sk.eq(content[1], a)
-/a/ | 2 | sk.eq(content[2], a)
-
-And we would arrive at the following sequence of ciphertext operations:
-```
-sk.bitor(sk.eq(content[0], a), sk.bitor(sk.eq(content[1], a), sk.eq(content[2], a)))
-```
-
-AnyChar is a no operation:
-```rust
-case RegExpr::AnyChar => {
- // note: ct_true is just some constant representing True that is trivially encoded into ciphertext
- return vec![(ct_true, c_pos + 1)];
-}
-```
-
-The sequence iterates over its `re_xs`, increasing the content position
-accordingly, and joins the results with `bitand` operations:
-```rust
-case RegExpr::Seq { re_xs } => {
- re_xs.iter().fold(|prev_results, re_x| {
- prev_results.iter().flat_map(|(prev_res, prev_c_pos)| {
- (x_res, new_c_pos) = match(sk, content, re_x, prev_c_pos);
- (sk.bitand(prev_res, x_res), new_c_pos)
- })
- }, (ct_true, c_pos))
-},
-```
-
-Other variants are similar, as they recurse and manipulate `re` and `c_pos`
-accordingly. Hopefully, the general idea is already clear.
-
-Ultimately the entire pattern-matching logic unfolds into a sequence of
-the following set of FHE operations:
-1. eq (tests for an exact character match)
-2. ge (tests for 'greater than' or 'equal to' a character)
-3. le (tests for 'less than' or 'equal to' a character)
-4. bitand (bitwise AND, used for sequencing multiple regex components)
-5. bitor (bitwise OR, used for folding multiple possible execution variants'
- results into a single result)
-6. bitxor (bitwise XOR, used for the 'not' logic in ranges)
-
-### Optimizations.
-
-Generally, the included example PME follows the approach outlined above. However, there were
-two additional optimizations applied. Both of these optimizations involved
-reducing the number of unnecessary FHE operations. Given how computationally expensive
-these operations are, it makes sense to optimize for this (and to ignore any suboptimal
-memory usage of our PME, etc.).
-
-The first optimization involved delaying the execution of FHE operations to _after_
-the generation of all possible execution paths to be considered. This optimization
-allows us to prune execution paths during execution path construction that are provably
-going to result in an encrypted false value, without having already performed the FHE
-operations up to the point of pruning. Consider the regex `/^a+b$/`, and we are applying
-this to a content of size 4. If we are executing execution paths naively, we would go ahead
-and check for all possible amounts of `a` repetitions: `ab`, `aab`, `aaab`.
-However, while building the execution paths, we can use the fact that `a+` must
-begin at the beginning of the content, and that `b` must be the final character
-of the content. From this follows that we only have to check for the following
-sentence: `aaab`. Delaying execution of the FHE operations until after we've
-built the possible execution paths in this example reduced the number of FHE
-operations applied by approximately half.
-
-The second optimization involved preventing the same FHE conditions to be
-re-evaluated. Consider the regex `/^a?ab/`. This would give us the following
-possible execution paths to consider:
-1. `content[0] == a && content[1] == a && content[2] == b` (we match the `a` in
- `a?`)
-2. `content[0] == a && content[1] == b` (we don't match the `a` in `a?`)
-
-Notice that, for both execution paths, we are checking for `content[0] == a`.
-Even though we cannot see what the encrypted result is, we do know that it's
-either going to be an encrypted false for both cases or an encrypted true for
-both cases. Therefore, we can skip the re-evaluation of `content[0] == a` and
-simply copy the result from the first evaluation over. This optimization
-involves maintaining a cache of known expression evaluation results and
-reusing those where possible.
-
-## Trying out the example implementation
-
-The implementation that guided the writing of this tutorial can be found
-under `tfhe/examples/regex_engine`.
-
-When compiling with `--example regex_engine`, a binary is produced that serves
-as a basic demo. Simply call it with the content string as a first argument and
-the pattern string as a second argument. For example,
-`cargo run --release --features=x86_64-unix,integer --example regex_engine -- 'this is the content' '/^pattern$/'`;
-note it's advised to compile the executable with `--release` flag as the key
-generation and homomorphic operations otherwise seem to experience a heavy
-performance penalty.
-
-On execution, a private and public key pair are created. Then, the content is
-encrypted with the client key, and the regex pattern is applied onto the
-encrypted content string - with access given only to the server key. Finally, it
-decrypts the resulting encrypted result using the client key and prints the
-verdict to the console.
-
-To get more information on exact computations and performance, set the `RUST_LOG`
-environment variable to `debug` or to `trace`.
-
-
-### Supported regex patterns
-
-This section specifies the supported set of regex patterns in the regex engine.
-
-#### Components
-
-A regex is described by a sequence of components surrounded by `/`, the
-following components are supported:
-
-Name | Notation | Examples
---- | --- | ---
-Character | Simply the character itself | `/a/`, `/b/`, `/Z/`, `/5/`
-Character range | `[-` | `/\^/`, `/\$/`
-Parenthesis | `()` | `/(abc)*/`, `/d(ab)?/`
-Optional | `?` | `/a?/`, `/(az)?/`
-Zero or more | `*` | `/a*/`, `/ab*c/`
-One or more | `+` | `/a+/`, `/ab+c/`
-Exact repeat | `}>` | `/ab{2}c/`
-At least repeat | `,}>` | `/ab{2,}c/`
-At most repeat | `}>` | `/ab{,2}c/`
-Repeat between | `,}>` | `/ab{2,4}c/`
-Either | `\|` | `/a\|b/`, `/ab\|cd/`
-Start matching | `/^` | `/^abc/`
-End matching | `$/` | `/abc$/`
-
-#### Modifiers
-
-Modifiers are mode selectors that affect the entire regex behavior. One modifier is
-currently supported:
-
-- Case insensitive matching, by appending an `i` after the regex pattern. For example: `/abc/i`
-
-#### General examples
-
-These components and modifiers can be combined to form any desired regex
-pattern. To give some idea of what is possible, here is a non-exhaustive list of
-supported regex patterns:
-
-Pattern | Description
---- | ---
-`/^abc$/` | Matches with content that equals exactly `abc` (case sensitive)
-`/^abc$/i` | Matches with content that equals `abc` (case insensitive)
-`/abc/` | Matches with content that contains somewhere `abc`
-`/ab?c/` | Matches with content that contains somewhere `abc` or somewhere `ab`
-`/^ab*c$/` | For example, matches with: `ac`, `abc`, `abbbbc`
-`/^[a-c]b\|cd$/` | Matches with: `ab`, `bb`, `cb`, `cd`
-`/^[a-c]b\|cd$/i` | Matches with: `ab`, `Ab`, `aB`, ..., `cD`, `CD`
-`/^d(abc)+d$/` | For example, matches with: `dabcd`, `dabcabcd`, `dabcabcabcd`
-`/^a.*d$/` | Matches with any content that starts with `a` and ends with `d`
diff --git a/tfhe/docs/explanations/tfhe-deep-dive.md b/tfhe/docs/explanations/tfhe-deep-dive.md
new file mode 100644
index 000000000..be35e558a
--- /dev/null
+++ b/tfhe/docs/explanations/tfhe-deep-dive.md
@@ -0,0 +1,18 @@
+# TFHE deep dive
+
+TFHE is a fully homomorphic encryption scheme that enables fast homomorphic operations on booleans, integers and reals.
+
+By enabling both leveled and bootstrapped operations, TFHE can be used for a wide range of usecases, from homomorphic boolean circuits to homomorphic neural networks.
+
+Here are a series of articles that guide you to go deeper into the understanding of the scheme:
+
+* [TFHE Deep Dive - Part I - Ciphertext types](https://www.zama.ai/post/tfhe-deep-dive-part-1)
+* [TFHE Deep Dive - Part II - Encodings and linear leveled operations](https://www.zama.ai/post/tfhe-deep-dive-part-2)
+* [TFHE Deep Dive - Part III - Key switching and leveled multiplications](https://www.zama.ai/post/tfhe-deep-dive-part-3)
+* [TFHE Deep Dive - Part IV - Programmable Bootstrapping](https://www.zama.ai/post/tfhe-deep-dive-part-4)
+
+The article [Guide to Fully Homomorphic Encryption over the Discretized Torus](https://eprint.iacr.org/2021/1402.pdf) gives more mathematical details about the TFHE scheme.
+
+You can also watch the video record of the original talk by Ilaria Chillotti for FHE.org:
+
+{% embed url="https://youtu.be/npoHSR6-oRw" %}
diff --git a/tfhe/docs/how_to/compress.md b/tfhe/docs/fundamentals/compress.md
similarity index 100%
rename from tfhe/docs/how_to/compress.md
rename to tfhe/docs/fundamentals/compress.md
diff --git a/tfhe/docs/fundamentals/compute.md b/tfhe/docs/fundamentals/compute.md
new file mode 100644
index 000000000..11729e13d
--- /dev/null
+++ b/tfhe/docs/fundamentals/compute.md
@@ -0,0 +1,131 @@
+# Compute on encrypted data
+
+Computations on encrypted data should be as easy to write as normal Rust, thanks to the usage of operator overloading.
+
+In the following example, the full encryption, computation with Rust's built-in operators and decryption flow is described:
+
+```rust
+use tfhe::prelude::*;
+use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8};
+
+fn main() {
+ let config = ConfigBuilder::default().build();
+
+ let (client_key, server_key) = generate_keys(config);
+
+ set_server_key(server_key);
+
+ let clear_a = 35u8;
+ let clear_b = 7u8;
+
+ // Encryption
+ let a = FheUint8::encrypt(clear_a, &client_key);
+ let b = FheUint8::encrypt(clear_b, &client_key);
+
+ // Take a reference to avoid moving data when doing the computation
+ let a = &a;
+ let b = &b;
+
+ // Computation using Rust's built-in operators
+ let add = a + b;
+ let sub = a - b;
+ let mul = a * b;
+ let div = a / b;
+ let rem = a % b;
+ let and = a & b;
+ let or = a | b;
+ let xor = a ^ b;
+ let neg = -a;
+ let not = !a;
+ let shl = a << b;
+ let shr = a >> b;
+
+ // Comparison operations need to use specific functions as the definition of the operators in
+ // rust require to return a boolean which we cannot do in FHE
+ let eq = a.eq(b);
+ let ne = a.ne(b);
+ let gt = a.gt(b);
+ let lt = a.lt(b);
+
+ // Decryption and verification of proper execution
+ let decrypted_add: u8 = add.decrypt(&client_key);
+
+ let clear_add = clear_a + clear_b;
+ assert_eq!(decrypted_add, clear_add);
+
+ let decrypted_sub: u8 = sub.decrypt(&client_key);
+
+ let clear_sub = clear_a - clear_b;
+ assert_eq!(decrypted_sub, clear_sub);
+
+ let decrypted_mul: u8 = mul.decrypt(&client_key);
+
+ let clear_mul = clear_a * clear_b;
+ assert_eq!(decrypted_mul, clear_mul);
+
+ let decrypted_div: u8 = div.decrypt(&client_key);
+
+ let clear_div = clear_a / clear_b;
+ assert_eq!(decrypted_div, clear_div);
+
+ let decrypted_rem: u8 = rem.decrypt(&client_key);
+
+ let clear_rem = clear_a % clear_b;
+ assert_eq!(decrypted_rem, clear_rem);
+
+ let decrypted_and: u8 = and.decrypt(&client_key);
+
+ let clear_and = clear_a & clear_b;
+ assert_eq!(decrypted_and, clear_and);
+
+ let decrypted_or: u8 = or.decrypt(&client_key);
+
+ let clear_or = clear_a | clear_b;
+ assert_eq!(decrypted_or, clear_or);
+
+ let decrypted_xor: u8 = xor.decrypt(&client_key);
+
+ let clear_xor = clear_a ^ clear_b;
+ assert_eq!(decrypted_xor, clear_xor);
+
+ let decrypted_neg: u8 = neg.decrypt(&client_key);
+
+ let clear_neg = clear_a.wrapping_neg();
+ assert_eq!(decrypted_neg, clear_neg);
+
+ let decrypted_not: u8 = not.decrypt(&client_key);
+
+ let clear_not = !clear_a;
+ assert_eq!(decrypted_not, clear_not);
+
+ let decrypted_shl: u8 = shl.decrypt(&client_key);
+
+ let clear_shl = clear_a << clear_b;
+ assert_eq!(decrypted_shl, clear_shl);
+
+ let decrypted_shr: u8 = shr.decrypt(&client_key);
+
+ let clear_shr = clear_a >> clear_b;
+ assert_eq!(decrypted_shr, clear_shr);
+
+ let decrypted_eq = eq.decrypt(&client_key);
+
+ let eq = clear_a == clear_b;
+ assert_eq!(decrypted_eq, eq);
+
+ let decrypted_ne = ne.decrypt(&client_key);
+
+ let ne = clear_a != clear_b;
+ assert_eq!(decrypted_ne, ne);
+
+ let decrypted_gt = gt.decrypt(&client_key);
+
+ let gt = clear_a > clear_b;
+ assert_eq!(decrypted_gt, gt);
+
+ let decrypted_lt = lt.decrypt(&client_key);
+
+ let lt = clear_a < clear_b;
+ assert_eq!(decrypted_lt, lt);
+}
+```
diff --git a/tfhe/docs/fundamentals/configure-and-generate-keys.md b/tfhe/docs/fundamentals/configure-and-generate-keys.md
new file mode 100644
index 000000000..647c63b59
--- /dev/null
+++ b/tfhe/docs/fundamentals/configure-and-generate-keys.md
@@ -0,0 +1,24 @@
+# Configure and create keys
+
+The first step is the creation of the configuration. The configuration is used to declare which type you will (or will not) use, as well as enabling you to use custom crypto-parameters for these types. Custom parameters should only be used for more advanced usage and/or testing.
+
+A configuration can be created by using the ConfigBuilder type.
+
+In this example, 8-bit unsigned integers with default parameters are used. The `integers` feature must also be enabled, as per the table on [this page](broken-reference).
+
+The config is generated by first creating a builder with all types deactivated. Then, the integer types with default parameters are activated, since we are going to use FheUint8 values.
+
+```rust
+use tfhe::{ConfigBuilder, generate_keys};
+
+fn main() {
+ let config = ConfigBuilder::default().build();
+
+
+ let (client_key, server_key) = generate_keys(config);
+}
+```
+
+The `generate_keys` command returns a client key and a server key.
+
+The `client_key` is meant to stay private and not leave the client, whereas the `server_key` can be made public and sent to a server for it to enable FHE computations.
diff --git a/tfhe/docs/how_to/debug.md b/tfhe/docs/fundamentals/debug.md
similarity index 69%
rename from tfhe/docs/how_to/debug.md
rename to tfhe/docs/fundamentals/debug.md
index c6b942052..a7a1d7c13 100644
--- a/tfhe/docs/how_to/debug.md
+++ b/tfhe/docs/fundamentals/debug.md
@@ -1,21 +1,16 @@
-# Debugging FHE Code
+# Debug
-Since tfhe-rs 0.5, [trivial ciphertexts](./trivial_ciphertext.md) have another application.
-They can be used to allow debugging via a debugger or print statements as well as speeding-up execution time
-so that you won't have to spend minutes waiting for execution to progress.
+Since tfhe-rs 0.5, [trivial ciphertexts](../fundamentals/trivial\_ciphertext.md) have another application. They can be used to allow debugging via a debugger or print statements as well as speeding-up execution time so that you won't have to spend minutes waiting for execution to progress.
This can greatly improve the pace at which one develops FHE applications.
{% hint style="warning" %}
-Keep in mind that trivial ciphertexts are not secure at all, thus an application released/deployed in production
-must never receive trivial ciphertext from a client.
+Keep in mind that trivial ciphertexts are not secure at all, thus an application released/deployed in production must never receive trivial ciphertext from a client.
{% endhint %}
-
## Example
-To use this feature, simply call your circuits/functions with trivially encrypted values (made using `encrypt_trivial`)
-instead of real encryptions (made using `encrypt`)
+To use this feature, simply call your circuits/functions with trivially encrypted values (made using `encrypt_trivial`) instead of real encryptions (made using `encrypt`)
```rust
use tfhe::prelude::*;
@@ -55,18 +50,17 @@ fn main() {
```
This example is going to print.
-```text
+
+```console
a: Ok(1234), b: Ok(4567), c: Ok(89101112)
a * b = Ok(5635678)
```
-If any input to `mul_all` is not a trivial ciphertexts, the computations would be done 100% in FHE, and the program
-would output:
+If any input to `mul_all` is not a trivial ciphertexts, the computations would be done 100% in FHE, and the program would output:
-```text
+```console
a: Err(NotTrivialCiphertextError), b: Err(NotTrivialCiphertextError), c: Err(NotTrivialCiphertextError)
a * b = Err(NotTrivialCiphertextError)
```
-Using trivial encryptions as input, the example runs in **980 ms** on a standard 12 cores laptop, using real encryptions
-it would run in **7.5 seconds** on a 128-core machine.
+Using trivial encryptions as input, the example runs in **980 ms** on a standard 12 cores laptop, using real encryptions it would run in **7.5 seconds** on a 128-core machine.
diff --git a/tfhe/docs/fundamentals/decrypt-data.md b/tfhe/docs/fundamentals/decrypt-data.md
new file mode 100644
index 000000000..bbf6a2da1
--- /dev/null
+++ b/tfhe/docs/fundamentals/decrypt-data.md
@@ -0,0 +1,26 @@
+# Decrypt data
+
+Decrypting data is achieved by using the `decrypt` method, which comes from the FheDecrypt trait.
+
+```rust
+use tfhe::prelude::*;
+use tfhe::{generate_keys, ConfigBuilder, FheUint8};
+
+fn main() {
+ let config = ConfigBuilder::default().build();
+
+ let (client_key, server_key) = generate_keys(config);
+
+ let clear_a = 27u8;
+ let clear_b = 128u8;
+
+ let a = FheUint8::encrypt(clear_a, &client_key);
+ let b = FheUint8::encrypt(clear_b, &client_key);
+
+ let decrypted_a: u8 = a.decrypt(&client_key);
+ let decrypted_b: u8 = b.decrypt(&client_key);
+
+ assert_eq!(decrypted_a, clear_a);
+ assert_eq!(decrypted_b, clear_b);
+}
+```
diff --git a/tfhe/docs/fundamentals/encrypt-data.md b/tfhe/docs/fundamentals/encrypt-data.md
new file mode 100644
index 000000000..b49a6d68a
--- /dev/null
+++ b/tfhe/docs/fundamentals/encrypt-data.md
@@ -0,0 +1,22 @@
+# Encrypt data
+
+Encrypting data is achieved via the `encrypt` associated function of the FheEncrypt trait.
+
+Types exposed by this crate implement at least one of FheEncrypt or FheTryEncrypt to allow encryption.
+
+```rust
+use tfhe::prelude::*;
+use tfhe::{generate_keys, ConfigBuilder, FheUint8};
+
+fn main() {
+ let config = ConfigBuilder::default().build();
+
+ let (client_key, server_key) = generate_keys(config);
+
+ let clear_a = 27u8;
+ let clear_b = 128u8;
+
+ let a = FheUint8::encrypt(clear_a, &client_key);
+ let b = FheUint8::encrypt(clear_b, &client_key);
+}
+```
diff --git a/tfhe/docs/fundamentals/encrypted-prf.md b/tfhe/docs/fundamentals/encrypted-prf.md
new file mode 100644
index 000000000..a63be24fb
--- /dev/null
+++ b/tfhe/docs/fundamentals/encrypted-prf.md
@@ -0,0 +1,25 @@
+# Generate encrypted pseudo random values
+
+TFHE-rs supports generating pseudo random values in FHE that are not known by the server.
+
+```rust
+use tfhe::prelude::FheDecrypt;
+use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8, Seed};
+
+pub fn main() {
+ let config = ConfigBuilder::default().build();
+ let (client_key, server_key) = generate_keys(config);
+
+ set_server_key(server_key);
+
+ let random_bits_count = 3;
+
+ // You can pass a 128 bits Seed here
+ // The generated values will always be the same for a given server key
+ // The server cannot know what value was generated
+ let ct_res = FheUint8::generate_oblivious_pseudo_random(Seed(0), random_bits_count);
+
+ let dec_result: u8 = ct_res.decrypt(&client_key);
+ assert!(dec_result < (1 << random_bits_count));
+}
+```
diff --git a/tfhe/docs/how_to/serialization.md b/tfhe/docs/fundamentals/serialization.md
similarity index 100%
rename from tfhe/docs/how_to/serialization.md
rename to tfhe/docs/fundamentals/serialization.md
diff --git a/tfhe/docs/fundamentals/set-the-server-key.md b/tfhe/docs/fundamentals/set-the-server-key.md
new file mode 100644
index 000000000..c552ac6a9
--- /dev/null
+++ b/tfhe/docs/fundamentals/set-the-server-key.md
@@ -0,0 +1,17 @@
+# Set the server key
+
+The next step is to call `set_server_key`
+
+This function will **move** the server key to an internal state of the crate and manage the details to give a simpler interface.
+
+```rust
+use tfhe::{ConfigBuilder, generate_keys, set_server_key};
+
+fn main() {
+ let config = ConfigBuilder::default().build();
+
+ let (client_key, server_key) = generate_keys(config);
+
+ set_server_key(server_key);
+}
+```
diff --git a/tfhe/docs/getting_started/benchmarks.md b/tfhe/docs/getting_started/benchmarks.md
index 7933054e1..0ecd778b8 100644
--- a/tfhe/docs/getting_started/benchmarks.md
+++ b/tfhe/docs/getting_started/benchmarks.md
@@ -13,36 +13,36 @@ This measures the execution time for some operation sets of tfhe-rs::integer (th
The table below reports the timing when the inputs of the benchmarked operation are encrypted.
| Operation \ Size | `FheUint8` | `FheUint16` | `FheUint32` | `FheUint64` | `FheUint128` | `FheUint256` |
-|--------------------------------------------------------|------------|-------------|-------------|-------------|--------------|--------------|
-| Negation (`-`) | 55.4 ms | 79.7 ms | 105 ms | 133 ms | 163 ms | 199 ms |
-| Add / Sub (`+`,`-`) | 58.9 ms | 86.0 ms | 106 ms | 124 ms | 151 ms | 193 ms |
-| Mul (`x`) | 122 ms | 164 ms | 227 ms | 410 ms | 1,04 s | 3,41 s |
-| Equal / Not Equal (`eq`, `ne`) | 32.0 ms | 32.0 ms | 50.4 ms | 50.9 ms | 53.1 ms | 54.6 ms |
-| Comparisons (`ge`, `gt`, `le`, `lt`) | 43.7 ms | 65.2 ms | 84.3 ms | 107 ms | 132 ms | 159 ms |
-| Max / Min (`max`,`min`) | 68.4 ms | 86.8 ms | 106 ms | 132 ms | 160 ms | 200 ms |
-| Bitwise operations (`&`, `\|`, `^`) | 17.1 ms | 17.3 ms | 17.8 ms | 18.8 ms | 20.2 ms | 22.2 ms |
-| Div / Rem (`/`, `%`) | 631 ms | 1.59 s | 3.77 s | 8,64 s | 20,3 s | 53,4 s |
-| Left / Right Shifts (`<<`, `>>`) | 82.8 ms | 99.2 ms | 121 ms | 149 ms | 194 ms | 401 ms |
-| Left / Right Rotations (`left_rotate`, `right_rotate`) | 82.1 ms | 99.4 ms | 120 ms | 149 ms | 194 ms | 402 ms |
+| ------------------------------------------------------ | ---------- | ----------- | ----------- | ----------- | ------------ | ------------ |
+| Negation (`-`) | 55.2 ms | 80.4 ms | 104 ms | 130 ms | 161 ms | 202 ms |
+| Add / Sub (`+`,`-`) | 57.7 ms | 82.1 ms | 105 ms | 128 ms | 155 ms | 195 ms |
+| Mul (`x`) | 80.8 ms | 149 ms | 211 ms | 366 ms | 961 ms | 3.2 s |
+| Equal / Not Equal (`eq`, `ne`) | 31.9 ms | 31.3 ms | 48.7 ms | 50.9 ms | 51.4 ms | 52.8 ms |
+| Comparisons (`ge`, `gt`, `le`, `lt`) | 48.1 ms | 68.4 ms | 83.2 ms | 102 ms | 121 ms | 145 ms |
+| Max / Min (`max`,`min`) | 81.1 ms | 96.4 ms | 114 ms | 133 ms | 154 ms | 198 ms |
+| Bitwise operations (`&`, `\|`, `^`) | 15.9 ms | 16.1 ms | 16.7 ms | 17.8 ms | 19.1 ms | 21.9 ms |
+| Div / Rem (`/`, `%`) | 613 ms | 1.56 s | 3.73 s | 8.83 s | 20.6 s | 53.8 s |
+| Left / Right Shifts (`<<`, `>>`) | 88.1 ms | 108 ms | 133 ms | 160 ms | 199 ms | 403 ms |
+| Left / Right Rotations (`left_rotate`, `right_rotate`) | 83.6 ms | 101 ms | 127 ms | 158 ms | 198 ms | 402 ms |
+| Leading / Trailing zeros/ones | 85.7 ms | 135 ms | 151 ms | 206 ms | 250 ms | 308 ms |
+| Log2 | 98.0 ms | 151 ms | 173 ms | 231 ms | 279 ms | 333 ms |
The table below reports the timing when the left input of the benchmarked operation is encrypted and the other is a clear scalar of the same size.
| Operation \ Size | `FheUint8` | `FheUint16` | `FheUint32` | `FheUint64` | `FheUint128` | `FheUint256` |
-|--------------------------------------------------------|------------|-------------|-------------|-------------|--------------|--------------|
-| Add / Sub (`+`,`-`) | 68.3 ms | 82.4 ms | 102 ms | 122 ms | 151 ms | 191 ms |
-| Mul (`x`) | 93.7 ms | 139 ms | 178 ms | 242 ms | 516 ms | 1.02 s |
-| Equal / Not Equal (`eq`, `ne`) | 30.2 ms | 30.8 ms | 32.7 ms | 50.4 ms | 51.2 ms | 54.8 ms |
-| Comparisons (`ge`, `gt`, `le`, `lt`) | 47.3 ms | 69.9 ms | 96.3 ms | 102 ms | 138 ms | 141 ms |
-| Max / Min (`max`,`min`) | 75.4 ms | 99.7 ms | 120 ms | 126 ms | 150 ms | 186 ms |
-| Bitwise operations (`&`, `\|`, `^`) | 17.1 ms | 17.4 ms | 18.2 ms | 19.2 ms | 19.7 ms | 22.6 ms |
-| Div (`/`) | 160 ms | 212 ms | 272 ms | 402 ms | 796 ms | 2.27 s |
-| Rem (`%`) | 315 ms | 428 ms | 556 ms | 767 ms | 1.27 s | 2.86 s |
-| Left / Right Shifts (`<<`, `>>`) | 16.8 ms | 16.8 ms | 17.3 ms | 18.0 ms | 18.9 ms | 22.6 ms |
-| Left / Right Rotations (`left_rotate`, `right_rotate`) | 16.8 ms | 16.9 ms | 17.3 ms | 18.3 ms | 19.0 ms | 22.8 ms |
-
-All timings are related to parallelized Radix-based integer operations, where each block is encrypted using the default parameters (i.e., PARAM\_MESSAGE\_2\_CARRY\_2\_KS\_PBS, more information about parameters can be found [here](../fine_grained_api/shortint/parameters.md)).
-To ensure predictable timings, the operation flavor is the `default` one: the carry is propagated if needed. The operation costs may be reduced by using `unchecked`, `checked`, or `smart`.
+| ------------------------------------------------------ | ---------- | ----------- | ----------- | ----------- | ------------ | ------------ |
+| Add / Sub (`+`,`-`) | 61.7 ms | 81.0 ms | 99.3 ms | 117 ms | 144 ms | 189 ms |
+| Mul (`x`) | 62.7 ms | 133 ms | 173 ms | 227 ms | 371 ms | 917 ms |
+| Equal / Not Equal (`eq`, `ne`) | 33.2 ms | 32.2 ms | 31.4 ms | 49.1 ms | 49.8 ms | 51.6 ms |
+| Comparisons (`ge`, `gt`, `le`, `lt`) | 32.1 ms | 51.9 ms | 70.8 ms | 89.2 ms | 110 ms | 130 ms |
+| Max / Min (`max`,`min`) | 69.9 ms | 88.8 ms | 107 ms | 130 ms | 153 ms | 188 ms |
+| Bitwise operations (`&`, `\|`, `^`) | 16.1 ms | 16.3 ms | 17.2 ms | 18.2 ms | 19.6 ms | 22.1 ms |
+| Div (`/`) | 160 ms | 194 ms | 275 ms | 391 ms | 749 ms | 2.02 s |
+| Rem (`%`) | 281 ms | 404 ms | 533 ms | 719 ms | 1.18 s | 2.76 s |
+| Left / Right Shifts (`<<`, `>>`) | 16.0 ms | 16.2 ms | 16.7 ms | 17.9 ms | 19.2 ms | 21.8 ms |
+| Left / Right Rotations (`left_rotate`, `right_rotate`) | 16.4 ms | 16.6 ms | 17.2 ms | 18.4 ms | 19.7 ms | 22.2 ms |
+All timings are related to parallelized Radix-based integer operations, where each block is encrypted using the default parameters (i.e., PARAM\_MESSAGE\_2\_CARRY\_2\_KS\_PBS, more information about parameters can be found [here](../references/fine-grained-apis/shortint/parameters.md)). To ensure predictable timings, the operation flavor is the `default` one: the carry is propagated if needed. The operation costs may be reduced by using `unchecked`, `checked`, or `smart`.
## Shortint
@@ -51,13 +51,12 @@ This measures the execution time for some operations using various parameter set
This uses the Concrete FFT + AVX-512 configuration.
| Parameter set | PARAM\_MESSAGE\_1\_CARRY\_1 | PARAM\_MESSAGE\_2\_CARRY\_2 | PARAM\_MESSAGE\_3\_CARRY\_3 | PARAM\_MESSAGE\_4\_CARRY\_4 |
-|------------------------------------|-----------------------------|-----------------------------|-----------------------------|-----------------------------|
+| ---------------------------------- | --------------------------- | --------------------------- | --------------------------- | --------------------------- |
| unchecked\_add | 341 ns | 555 ns | 2.47 µs | 9.77 µs |
| add | 5.96 ms | 12.6 ms | 102 ms | 508 ms |
| mul\_lsb | 5.99 ms | 12.3 ms | 101 ms | 500 ms |
| keyswitch\_programmable\_bootstrap | 6.40 ms | 12.9 ms | 104 ms | 489 ms |
-
## Boolean
This measures the execution time of a single binary Boolean gate.
@@ -65,26 +64,25 @@ This measures the execution time of a single binary Boolean gate.
### tfhe-rs::boolean.
| Parameter set | Concrete FFT + AVX-512 |
-|------------------------------------------------------|------------------------|
+| ---------------------------------------------------- | ---------------------- |
| DEFAULT\_PARAMETERS\_KS\_PBS | 8.49 ms |
| PARAMETERS\_ERROR\_PROB\_2\_POW\_MINUS\_165\_KS\_PBS | 13.7 ms |
| TFHE\_LIB\_PARAMETERS | 9.90 ms |
-
### tfhe-lib.
Using the same hpc7a.96xlarge machine as the one for tfhe-rs, the timings are:
| Parameter set | spqlios-fma |
-|--------------------------------------------------|-------------|
+| ------------------------------------------------ | ----------- |
| default\_128bit\_gate\_bootstrapping\_parameters | 13.5 ms |
### OpenFHE (v1.1.2).
-Following the official instructions from OpenFHE, `clang14` and the following command are used to setup the project:
-`cmake -DNATIVE_SIZE=32 -DWITH_NATIVEOPT=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DWITH_OPENMP=OFF ..`
+Following the official instructions from OpenFHE, `clang14` and the following command are used to setup the project: `cmake -DNATIVE_SIZE=32 -DWITH_NATIVEOPT=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DWITH_OPENMP=OFF ..`
To use the HEXL library, the configuration used is as follows:
+
```bash
export CXX=clang++
export CC=clang
@@ -98,11 +96,10 @@ scripts/build-openfhe-development-hexl.sh
Using the same hpc7a.96xlarge machine as the one for tfhe-rs, the timings are:
-| Parameter set | GINX | GINX w/ Intel HEXL |
-|----------------------------------|---------|--------------------|
-| FHEW\_BINGATE/STD128\_OR | 25.5 ms | 21,6 ms |
-| FHEW\_BINGATE/STD128\_LMKCDEY_OR | 25.4 ms | 19.9 ms |
-
+| Parameter set | GINX | GINX w/ Intel HEXL |
+| --------------------------------- | ------- | ------------------ |
+| FHEW\_BINGATE/STD128\_OR | 25.5 ms | 21,6 ms |
+| FHEW\_BINGATE/STD128\_LMKCDEY\_OR | 25.4 ms | 19.9 ms |
## How to reproduce TFHE-rs benchmarks
diff --git a/tfhe/docs/getting_started/quick_start.md b/tfhe/docs/getting_started/quick_start.md
index fa09b4661..7089ac039 100644
--- a/tfhe/docs/getting_started/quick_start.md
+++ b/tfhe/docs/getting_started/quick_start.md
@@ -1,15 +1,13 @@
-# Tutorial
-
-## Quick Start
+# Quick start
The basic steps for using the high-level API of TFHE-rs are:
-1. Importing the TFHE-rs prelude;
-2. Client-side: Configuring and creating keys;
-3. Client-side: Encrypting data;
-4. Server-side: Setting the server key;
-5. Server-side: Computing over encrypted data;
-6. Client-side: Decrypting data.
+1. [Importing the TFHE-rs prelude](quick\_start.md#imports);
+2. Client-side: [Configuring and creating keys](../fundamentals/configure-and-create-keys.md);
+3. Client-side: [Encrypting data](../fundamentals/encrypt-data.md);
+4. Server-side: [Setting the server key](../fundamentals/set-the-server-key.md);
+5. Server-side: [Computing over encrypted data](../fundamentals/compute.md);
+6. Client-side: [Decrypting data](../fundamentals/decrypt-data.md).
Here is a full example (combining the client and server parts):
@@ -43,13 +41,14 @@ fn main() {
```
The default configuration for x86 Unix machines:
+
```toml
tfhe = { version = "0.6.0", features = ["integer", "x86_64-unix"]}
```
-Configuration options for different platforms can be found [here](../getting_started/installation.md). Other rust and homomorphic types features can be found [here](../how_to/rust_configuration.md).
+Configuration options for different platforms can be found [here](installation.md). Other rust and homomorphic types features can be found [here](../guides/rust\_configuration.md).
-### Imports.
+### Imports
`tfhe` uses `traits` to have a consistent API for creating FHE types and enable users to write generic functions. To be able to use associated functions and methods of a trait, the trait has to be in scope.
@@ -58,79 +57,3 @@ To make it easier, the `prelude` 'pattern' is used. All of the important `tfhe`
```rust
use tfhe::prelude::*;
```
-
-### 1. Configuring and creating keys.
-
-The first step is the creation of the configuration. The configuration is used to declare which type you will (or will not) use, as well as enabling you to use custom crypto-parameters for these types. Custom parameters should only be used for more advanced usage and/or testing.
-
-A configuration can be created by using the ConfigBuilder type.
-
-In this example, 8-bit unsigned integers with default parameters are used. The `integers`
-feature must also be enabled, as per the table on [this page](../how_to/rust_configuration.md#choosing-your-features).
-
-The config is generated by first creating a builder with all types deactivated. Then, the integer types with default parameters are activated, since we are going to use FheUint8 values.
-
-```rust
-use tfhe::{ConfigBuilder, generate_keys};
-
-fn main() {
- let config = ConfigBuilder::default().build();
-
-
- let (client_key, server_key) = generate_keys(config);
-}
-```
-
-The `generate_keys` command returns a client key and a server key.
-
-The `client_key` is meant to stay private and not leave the client, whereas the `server_key` can be made public and sent to a server for it to enable FHE computations.
-
-### 2. Setting the server key.
-
-The next step is to call `set_server_key`
-
-This function will **move** the server key to an internal state of the crate and manage the details to give a simpler interface.
-
-```rust
-use tfhe::{ConfigBuilder, generate_keys, set_server_key};
-
-fn main() {
- let config = ConfigBuilder::default().build();
-
- let (client_key, server_key) = generate_keys(config);
-
- set_server_key(server_key);
-}
-```
-
-### 3. Encrypting data.
-
-Encrypting data is achieved via the `encrypt` associated function of the FheEncrypt trait.
-
-Types exposed by this crate implement at least one of FheEncrypt or FheTryEncrypt to allow encryption.
-
-```Rust
-let clear_a = 27u8;
-let clear_b = 128u8;
-
-let a = FheUint8::encrypt(clear_a, &client_key);
-let b = FheUint8::encrypt(clear_b, &client_key);
-```
-
-### 4. Computation and decryption.
-
-Computations should be as easy as normal Rust to write, thanks to the usage of operator overloading.
-
-```Rust
-let result = a + b;
-```
-
-The decryption is achieved by using the `decrypt` method, which comes from the FheDecrypt trait.
-
-```Rust
-let decrypted_result: u8 = result.decrypt(&client_key);
-
-let clear_result = clear_a + clear_b;
-
-assert_eq!(decrypted_result, clear_result);
-```
diff --git a/tfhe/docs/getting_started/readme.md b/tfhe/docs/getting_started/readme.md
new file mode 100644
index 000000000..7c4f90f45
--- /dev/null
+++ b/tfhe/docs/getting_started/readme.md
@@ -0,0 +1,34 @@
+# What is TFHE-rs?
+
+
+
+TFHE-rs is a pure Rust implementation of TFHE for Boolean and integer arithmetics over encrypted data. It includes a Rust and C API, as well as a client-side WASM API.
+
+TFHE-rs is meant for developers and researchers who want full control over what they can do with TFHE, while not worrying about the low level implementation.
+
+The goal is to have a stable, simple, high-performance, and production-ready library for all the advanced features of TFHE.
+
+## Key cryptographic concepts
+
+The TFHE-rs library implements Zama’s variant of Fully Homomorphic Encryption over the Torus (TFHE). TFHE is based on Learning With Errors (LWE), a well-studied cryptographic primitive believed to be secure even against quantum computers.
+
+In cryptography, a raw value is called a message (also sometimes called a cleartext), while an encoded message is called a plaintext and an encrypted plaintext is called a ciphertext.
+
+The idea of homomorphic encryption is that you can compute on ciphertexts while not knowing messages encrypted within them. A scheme is said to be _fully homomorphic_, meaning any program can be evaluated with it, if at least two of the following operations are supported ($$x$$ is a plaintext and $$E[x]$$ is the corresponding ciphertext):
+
+* homomorphic univariate function evaluation: $$f(E[x]) = E[f(x)]$$
+* homomorphic addition: $$E[x] + E[y] = E[x + y]$$
+* homomorphic multiplication: $$E[x] * E[y] = E[x * y]$$
+
+Zama's variant of TFHE is fully homomorphic and deals with fixed-precision numbers as messages. It implements all needed homomorphic operations, such as addition and function evaluation via **Programmable Bootstrapping**. You can read more about Zama's TFHE variant in the [preliminary whitepaper](https://whitepaper.zama.ai/).
+
+Using FHE in a Rust program with TFHE-rs consists in:
+
+* generating a client key and a server key using secure parameters:
+ * a client key encrypts/decrypts data and must be kept secret
+ * a server key is used to perform operations on encrypted data and could be public (also called an evaluation key)
+* encrypting plaintexts using the client key to produce ciphertexts
+* operating homomorphically on ciphertexts with the server key
+* decrypting the resulting ciphertexts into plaintexts using the client key
+
+If you would like to know more about the problems that FHE solves, we suggest you review our [6 minute introduction to homomorphic encryption](https://6min.zama.ai/).
diff --git a/tfhe/docs/how_to/c_api.md b/tfhe/docs/guides/c_api.md
similarity index 98%
rename from tfhe/docs/how_to/c_api.md
rename to tfhe/docs/guides/c_api.md
index 348324c17..7743d8d5e 100644
--- a/tfhe/docs/how_to/c_api.md
+++ b/tfhe/docs/guides/c_api.md
@@ -13,7 +13,7 @@ RUSTFLAGS="-C target-cpu=native" cargo +nightly build --release --features=x86_6
or on a Unix aarch64 machine using the following command:
```shell
-RUSTFLAGS="-C target-cpu=native" cargo build +nightly --release --features=aarch64-unix,high-level-c-api -p tfhe && make symlink_c_libs_without_fingerprint
+RUSTFLAGS="-C target-cpu=native" cargo +nightly build --release --features=aarch64-unix,high-level-c-api -p tfhe && make symlink_c_libs_without_fingerprint
```
The `tfhe.h` header as well as the static (.a) and dynamic (.so) `libtfhe` binaries can then be found in "${REPO\_ROOT}/target/release/".
diff --git a/tfhe/docs/how_to/js_on_wasm_api.md b/tfhe/docs/guides/js_on_wasm_api.md
similarity index 99%
rename from tfhe/docs/how_to/js_on_wasm_api.md
rename to tfhe/docs/guides/js_on_wasm_api.md
index 71c347490..1ce998495 100644
--- a/tfhe/docs/how_to/js_on_wasm_api.md
+++ b/tfhe/docs/guides/js_on_wasm_api.md
@@ -114,7 +114,7 @@ Some parameter sets lead to FHE keys that are too big to fit in the 2GB memory s
### Setting-up TFHE-rs JS on WASM API for use in nodejs programs.
-To build the JS on WASM bindings for TFHE-rs, you need to install [`wasm-pack`](https://rustwasm.github.io/wasm-pack/) in addition to a compatible (>= 1.67) [`rust toolchain`](https://rustup.rs/).
+To build the JS on WASM bindings for TFHE-rs, you need to install [`wasm-pack`](https://rustwasm.github.io/wasm-pack/) in addition to a compatible [`rust toolchain`](https://rustup.rs/).
In a shell, then run the following to clone the TFHE-rs repo (one may want to checkout a specific tag, here the default branch is used for the build):
diff --git a/tfhe/docs/how_to/migrate_data.md b/tfhe/docs/guides/migrate_data.md
similarity index 100%
rename from tfhe/docs/how_to/migrate_data.md
rename to tfhe/docs/guides/migrate_data.md
diff --git a/tfhe/docs/how_to/overflow_operations.md b/tfhe/docs/guides/overflow_operations.md
similarity index 100%
rename from tfhe/docs/how_to/overflow_operations.md
rename to tfhe/docs/guides/overflow_operations.md
diff --git a/tfhe/docs/how_to/parallelized_pbs.md b/tfhe/docs/guides/parallelized_pbs.md
similarity index 100%
rename from tfhe/docs/how_to/parallelized_pbs.md
rename to tfhe/docs/guides/parallelized_pbs.md
diff --git a/tfhe/docs/guides/pbs-stats.md b/tfhe/docs/guides/pbs-stats.md
new file mode 100644
index 000000000..e8591244f
--- /dev/null
+++ b/tfhe/docs/guides/pbs-stats.md
@@ -0,0 +1,44 @@
+# PBS Statistics
+
+The `shortint` API now keeps track of how many PBS were executed with a global counter when enabling the `pbs-stats` feature.
+
+This allows knowing precisely how many PBS are executed in a circuit and estimate the overall compute intensity of FHE code using either the `shortint`, `integer` or High-Level APIs.
+
+You can query how many PBSes were executed by calling `get_pbs_count`. You can reset the PBS count by calling `reset_pbs_count` to more easily know how many PBSes were executed by each part of your code.
+
+Combined with the [`debug mode`](`debug.md`) it can allow to have estimations very quickly while iterating on the FHE code.
+
+Here is an example of how to use the PBS counter:
+
+```rust
+use tfhe::prelude::*;
+use tfhe::*;
+
+pub fn main() {
+ // Config and key generation
+ let config = ConfigBuilder::default().build();
+
+ let (cks, sks) = generate_keys(config);
+
+ // Encryption
+ let a = FheUint32::encrypt(42u32, &cks);
+ let b = FheUint32::encrypt(16u32, &cks);
+
+ // Set the server key
+ set_server_key(sks);
+
+ // Compute and get the PBS count for the 32 bits multiplication
+ let c = &a * &b;
+ let mul_32_count = get_pbs_count();
+
+ // Reset the PBS count, and get the PBS count for a 32 bits bitwise AND
+ reset_pbs_count();
+ let d = &a & &b;
+ let and_32_count = get_pbs_count();
+
+ // Display the result
+ println!("mul_32_count: {mul_32_count}");
+ println!("and_32_count: {and_32_count}");
+}
+
+```
diff --git a/tfhe/docs/how_to/public_key.md b/tfhe/docs/guides/public_key.md
similarity index 76%
rename from tfhe/docs/how_to/public_key.md
rename to tfhe/docs/guides/public_key.md
index 3a0e2101e..cd6128673 100644
--- a/tfhe/docs/how_to/public_key.md
+++ b/tfhe/docs/guides/public_key.md
@@ -1,10 +1,11 @@
+# Use public key encryption
-# Public Key Encryption
-Public key encryption refers to the cryptographic paradigm where the encryption key can be publicly distributed, whereas the decryption key remains secret to the owner. This differs from usual case where the same secret key is used to encrypt and decrypt the data. In TFHE-rs, there exists two methods for public key encryptions. First, the usual one, where the public key contains many encryptions of zero. More details can be found in [Guide to Fully Homomorphic Encryption over the [Discretized] Torus, Appendix A.](https://eprint.iacr.org/2021/1402). The second method is based on the paper entitled [TFHE Public-Key Encryption Revisited](https://eprint.iacr.org/2023/603). The main advantage of the latter method in comparison with the former lies into the key sizes, which are drastically reduced.
+Public key encryption refers to the cryptographic paradigm where the encryption key can be publicly distributed, whereas the decryption key remains secret to the owner. This differs from usual case where the same secret key is used to encrypt and decrypt the data. In TFHE-rs, there exists two methods for public key encryptions. First, the usual one, where the public key contains many encryptions of zero. More details can be found in [Guide to Fully Homomorphic Encryption over the \[Discretized\] Torus, Appendix A.](https://eprint.iacr.org/2021/1402). The second method is based on the paper entitled [TFHE Public-Key Encryption Revisited](https://eprint.iacr.org/2023/603). The main advantage of the latter method in comparison with the former lies into the key sizes, which are drastically reduced.
-Note that public keys can be [compressed](./compress.md)
+Note that public keys can be [compressed](../fundamentals/compress.md)
+
+## Classical public key
-## classical public key
This example shows how to use public keys.
```rust
@@ -23,9 +24,9 @@ fn main() {
}
```
-## compact public key
-This example shows how to use compact public keys. The main difference is in the ConfigBuilder, where the parameter set has been changed.
+## Compact public key
+This example shows how to use compact public keys. The main difference is in the ConfigBuilder, where the parameter set has been changed.
```rust
use tfhe::prelude::*;
diff --git a/tfhe/docs/how_to/rayon_crate.md b/tfhe/docs/guides/rayon_crate.md
similarity index 100%
rename from tfhe/docs/how_to/rayon_crate.md
rename to tfhe/docs/guides/rayon_crate.md
diff --git a/tfhe/docs/how_to/run_on_gpu.md b/tfhe/docs/guides/run_on_gpu.md
similarity index 98%
rename from tfhe/docs/how_to/run_on_gpu.md
rename to tfhe/docs/guides/run_on_gpu.md
index dfaf6d82d..590bab9b6 100644
--- a/tfhe/docs/how_to/run_on_gpu.md
+++ b/tfhe/docs/guides/run_on_gpu.md
@@ -120,7 +120,12 @@ Finally, the client gets the decrypted results by computing:
```
## Improving performance.
TFHE-rs includes the possibility to leverage the high number of threads given by a GPU.
-To do so, the configuration should be updated with ```Rust let config = ConfigBuilder::with_custom_parameters(PARAM_MULTI_BIT_MESSAGE_2_CARRY_2_GROUP_3_KS_PBS, None).build();```
+To do so, the configuration should be updated with
+
+```Rust
+let config = ConfigBuilder::with_custom_parameters(PARAM_MULTI_BIT_MESSAGE_2_CARRY_2_GROUP_3_KS_PBS, None).build();
+```
+
The complete example becomes:
```rust
diff --git a/tfhe/docs/how_to/rust_configuration.md b/tfhe/docs/guides/rust_configuration.md
similarity index 90%
rename from tfhe/docs/how_to/rust_configuration.md
rename to tfhe/docs/guides/rust_configuration.md
index d7688e4de..6b568d3f0 100644
--- a/tfhe/docs/how_to/rust_configuration.md
+++ b/tfhe/docs/guides/rust_configuration.md
@@ -16,11 +16,11 @@ Then, you can either:
```shell
# By default the +stable should not be needed, but we add it here for completeness
-cargo +stable build
-cargo +stable test
+cargo +stable build --release
+cargo +stable test --release
# Or
-cargo +nightly build
-cargo +nightly test
+cargo +nightly build --release
+cargo +nightly test --release
```
* Or override the toolchain to use for the current project:
@@ -30,11 +30,11 @@ cargo +nightly test
# correct you can still set the overridden toolchain to stable
rustup override set stable
# cargo will use the `stable` toolchain.
-cargo build
+cargo build --release
# Or
rustup override set nightly
# cargo will use the `nightly` toolchain.
-cargo build
+cargo build --release
```
To check the toolchain that Cargo will use by default, you can use the following command:
@@ -64,5 +64,5 @@ This crate exposes two kinds of data types. Each kind is enabled by activating i
In general, the library automatically chooses the best instruction sets available by the host. However, in the case of 'AVX-512', this has to be explicitly chosen as a feature. This requires to use a [nightly toolchain](#using-tfhe-rs-with-nightly-toolchain) along with the feature `nightly-avx512`.
```shell
-cargo +nightly build --features=nightly-avx512
+cargo +nightly build --release --features=nightly-avx512
```
diff --git a/tfhe/docs/how_to/trait_bounds.md b/tfhe/docs/guides/trait_bounds.md
similarity index 100%
rename from tfhe/docs/how_to/trait_bounds.md
rename to tfhe/docs/guides/trait_bounds.md
diff --git a/tfhe/docs/how_to/trivial_ciphertext.md b/tfhe/docs/guides/trivial_ciphertext.md
similarity index 100%
rename from tfhe/docs/how_to/trivial_ciphertext.md
rename to tfhe/docs/guides/trivial_ciphertext.md
diff --git a/tfhe/docs/references/core-crypto-api/README.md b/tfhe/docs/references/core-crypto-api/README.md
new file mode 100644
index 000000000..f324b9523
--- /dev/null
+++ b/tfhe/docs/references/core-crypto-api/README.md
@@ -0,0 +1,4 @@
+# Core crypto API
+
+* [Quick start](presentation.md)
+* [Tutorial](tutorial.md)
diff --git a/tfhe/docs/core_crypto/presentation.md b/tfhe/docs/references/core-crypto-api/presentation.md
similarity index 93%
rename from tfhe/docs/core_crypto/presentation.md
rename to tfhe/docs/references/core-crypto-api/presentation.md
index 3f94bc178..d762114f2 100644
--- a/tfhe/docs/core_crypto/presentation.md
+++ b/tfhe/docs/references/core-crypto-api/presentation.md
@@ -1,6 +1,6 @@
# Quick Start
-The `core_crypto` module from `TFHE-rs` is dedicated to the implementation of the cryptographic tools related to TFHE. To construct an FHE application, the [shortint](../fine_grained_api/shortint/tutorial.md) and/or [Boolean](../fine_grained_api/Boolean/tutorial.md) modules (based on `core_crypto`) are recommended.
+The `core_crypto` module from `TFHE-rs` is dedicated to the implementation of the cryptographic tools related to TFHE. To construct an FHE application, the [shortint](../../fine\_grained\_api/shortint/tutorial.md) and/or [Boolean](../../fine\_grained\_api/Boolean/tutorial.md) modules (based on `core_crypto`) are recommended.
The `core_crypto` module offers an API to low-level cryptographic primitives and objects, like `lwe_encryption` or `rlwe_ciphertext`. The goal is to propose an easy-to-use API for cryptographers.
diff --git a/tfhe/docs/core_crypto/tutorial.md b/tfhe/docs/references/core-crypto-api/tutorial.md
similarity index 100%
rename from tfhe/docs/core_crypto/tutorial.md
rename to tfhe/docs/references/core-crypto-api/tutorial.md
diff --git a/tfhe/docs/references/fine-grained-apis/README.md b/tfhe/docs/references/fine-grained-apis/README.md
new file mode 100644
index 000000000..36314140c
--- /dev/null
+++ b/tfhe/docs/references/fine-grained-apis/README.md
@@ -0,0 +1,6 @@
+# Fine-grained APIs
+
+* [Quick start](quick\_start.md)
+* [Boolean](boolean/)
+* [Shortint](shortint/)
+* [Integer](integer/)
diff --git a/tfhe/docs/fine_grained_api/Boolean/readme.md b/tfhe/docs/references/fine-grained-apis/boolean/README.md
similarity index 100%
rename from tfhe/docs/fine_grained_api/Boolean/readme.md
rename to tfhe/docs/references/fine-grained-apis/boolean/README.md
diff --git a/tfhe/docs/fine_grained_api/Boolean/operations.md b/tfhe/docs/references/fine-grained-apis/boolean/operations.md
similarity index 100%
rename from tfhe/docs/fine_grained_api/Boolean/operations.md
rename to tfhe/docs/references/fine-grained-apis/boolean/operations.md
diff --git a/tfhe/docs/fine_grained_api/Boolean/parameters.md b/tfhe/docs/references/fine-grained-apis/boolean/parameters.md
similarity index 100%
rename from tfhe/docs/fine_grained_api/Boolean/parameters.md
rename to tfhe/docs/references/fine-grained-apis/boolean/parameters.md
diff --git a/tfhe/docs/fine_grained_api/Boolean/serialization.md b/tfhe/docs/references/fine-grained-apis/boolean/serialization.md
similarity index 100%
rename from tfhe/docs/fine_grained_api/Boolean/serialization.md
rename to tfhe/docs/references/fine-grained-apis/boolean/serialization.md
diff --git a/tfhe/docs/fine_grained_api/integer/readme.md b/tfhe/docs/references/fine-grained-apis/integer/README.md
similarity index 98%
rename from tfhe/docs/fine_grained_api/integer/readme.md
rename to tfhe/docs/references/fine-grained-apis/integer/README.md
index 625466f3b..1334d32eb 100644
--- a/tfhe/docs/fine_grained_api/integer/readme.md
+++ b/tfhe/docs/references/fine-grained-apis/integer/README.md
@@ -111,7 +111,7 @@ fn main() {
let ct_2 = client_key.encrypt(msg2);
// We use the server public key to execute an integer circuit:
- let ct_3 = server_key.unchecked_add(&ct_1, &ct_2);
+ let ct_3 = server_key.add_parallelized(&ct_1, &ct_2);
// We use the client key to decrypt the output of the circuit:
let output: u64 = client_key.decrypt(&ct_3);
diff --git a/tfhe/docs/fine_grained_api/integer/operations.md b/tfhe/docs/references/fine-grained-apis/integer/operations.md
similarity index 88%
rename from tfhe/docs/fine_grained_api/integer/operations.md
rename to tfhe/docs/references/fine-grained-apis/integer/operations.md
index 951ac5f6f..030350543 100644
--- a/tfhe/docs/fine_grained_api/integer/operations.md
+++ b/tfhe/docs/references/fine-grained-apis/integer/operations.md
@@ -6,7 +6,7 @@ The structure and operations related to integers are described in this section.
In `integer`, the encrypted data is split amongst many ciphertexts encrypted with the `shortint` library. Below is a scheme representing an integer composed by k shortint ciphertexts.
-
+
This crate implements two ways to represent an integer:
@@ -48,8 +48,7 @@ fn main() {
}
```
-This representation has many advantages: no carry propagation is required, cleaning the carry buffer of each ciphertext block is enough. This implies that operations can easily be
-parallelized. It also allows the efficient computation of PBS in the case where the function is CRT-compliant.
+This representation has many advantages: no carry propagation is required, cleaning the carry buffer of each ciphertext block is enough. This implies that operations can easily be parallelized. It also allows the efficient computation of PBS in the case where the function is CRT-compliant.
A variant of the CRT is proposed where each block might be associated to a different key couple. Here, a keychain to the computations is required, but this may result in a performance improvement.
@@ -57,20 +56,20 @@ A variant of the CRT is proposed where each block might be associated to a diffe
The list of operations available in `integer` depends on the type of representation:
-| Operation name | Radix-based | CRT-based |
-| ------------------------------ | -------------------- | -------------------------- |
-| Negation | :heavy\_check\_mark: | :heavy\_check\_mark: |
-| Addition | :heavy\_check\_mark: | :heavy\_check\_mark: |
-| Scalar Addition | :heavy\_check\_mark: | :heavy\_check\_mark: |
-| Subtraction | :heavy\_check\_mark: | :heavy\_check\_mark: |
-| Scalar Subtraction | :heavy\_check\_mark: | :heavy\_check\_mark: |
-| Multiplication | :heavy\_check\_mark: | :heavy\_check\_mark: |
-| Scalar Multiplication | :heavy\_check\_mark: | :heavy\_check\_mark: |
-| Bitwise OR, AND, XOR | :heavy\_check\_mark: | :heavy\_check\_mark: |
-| Equality | :heavy\_check\_mark: | :heavy\_check\_mark: |
-| Left/Right Shift | :heavy\_check\_mark: | :heavy\_multiplication\_x: |
-| Comparisons `<`,`<=`,`>`, `>=` | :heavy\_check\_mark: | :heavy\_multiplication\_x: |
-| Min, Max | :heavy\_check\_mark: | :heavy\_multiplication\_x: |
+| Operation name | Radix-based | CRT-based |
+| ------------------------------ | -------------------- | -------------------------- |
+| Negation | :heavy\_check\_mark: | :heavy\_check\_mark: |
+| Addition | :heavy\_check\_mark: | :heavy\_check\_mark: |
+| Scalar Addition | :heavy\_check\_mark: | :heavy\_check\_mark: |
+| Subtraction | :heavy\_check\_mark: | :heavy\_check\_mark: |
+| Scalar Subtraction | :heavy\_check\_mark: | :heavy\_check\_mark: |
+| Multiplication | :heavy\_check\_mark: | :heavy\_check\_mark: |
+| Scalar Multiplication | :heavy\_check\_mark: | :heavy\_check\_mark: |
+| Bitwise OR, AND, XOR | :heavy\_check\_mark: | :heavy\_check\_mark: |
+| Equality | :heavy\_check\_mark: | :heavy\_check\_mark: |
+| Left/Right Shift | :heavy\_check\_mark: | :heavy\_multiplication\_x: |
+| Comparisons `<`,`<=`,`>`, `>=` | :heavy\_check\_mark: | :heavy\_multiplication\_x: |
+| Min, Max | :heavy\_check\_mark: | :heavy\_multiplication\_x: |
## Types of operations
@@ -215,12 +214,12 @@ fn main() {
{% hint style="warning" %}
You must avoid cloning the inputs when calling `smart` operations to preserve performance. For instance, you SHOULD NOT have these kind of patterns in the code:
+
```Rust
sks.smart_add(&mut a.clone(), &mut b.clone());
```
{% endhint %}
-
The main advantage of the default flavor is to ensure predictable timings, as long as only this kind of operation is used. Only the parallelized version of the operations is provided.
{% hint style="warning" %}
diff --git a/tfhe/docs/fine_grained_api/integer/parameters.md b/tfhe/docs/references/fine-grained-apis/integer/parameters.md
similarity index 100%
rename from tfhe/docs/fine_grained_api/integer/parameters.md
rename to tfhe/docs/references/fine-grained-apis/integer/parameters.md
diff --git a/tfhe/docs/fine_grained_api/integer/serialization.md b/tfhe/docs/references/fine-grained-apis/integer/serialization.md
similarity index 100%
rename from tfhe/docs/fine_grained_api/integer/serialization.md
rename to tfhe/docs/references/fine-grained-apis/integer/serialization.md
diff --git a/tfhe/docs/fine_grained_api/quick_start.md b/tfhe/docs/references/fine-grained-apis/quick_start.md
similarity index 97%
rename from tfhe/docs/fine_grained_api/quick_start.md
rename to tfhe/docs/references/fine-grained-apis/quick_start.md
index 9188142f3..a88c492eb 100644
--- a/tfhe/docs/fine_grained_api/quick_start.md
+++ b/tfhe/docs/references/fine-grained-apis/quick_start.md
@@ -121,7 +121,7 @@ fn main() {
let ct_2 = client_key.encrypt(msg2);
// We use the server public key to execute an integer circuit:
- let ct_3 = server_key.unchecked_add(&ct_1, &ct_2);
+ let ct_3 = server_key.add(&ct_1, &ct_2);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_3);
@@ -140,7 +140,7 @@ use tfhe::integer::gen_keys_radix;
use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
fn main() {
- // We create keys for radix representation to create 16 bits integers
+ // We generate keys to encrypt 16 bits radix-encoded integers
// using 8 blocks of 2 bits
let (cks, sks) = gen_keys_radix(PARAM_MESSAGE_2_CARRY_2, 8);
diff --git a/tfhe/docs/fine_grained_api/shortint/readme.md b/tfhe/docs/references/fine-grained-apis/shortint/README.md
similarity index 98%
rename from tfhe/docs/fine_grained_api/shortint/readme.md
rename to tfhe/docs/references/fine-grained-apis/shortint/README.md
index 54f79f2ea..4c74e2f38 100644
--- a/tfhe/docs/fine_grained_api/shortint/readme.md
+++ b/tfhe/docs/references/fine-grained-apis/shortint/README.md
@@ -89,7 +89,7 @@ fn main() {
let ct_2 = client_key.encrypt(msg2);
// We use the server public key to execute an integer circuit:
- let ct_3 = server_key.unchecked_add(&ct_1, &ct_2);
+ let ct_3 = server_key.add(&ct_1, &ct_2);
// We use the client key to decrypt the output of the circuit:
let output = client_key.decrypt(&ct_3);
diff --git a/tfhe/docs/fine_grained_api/shortint/operations.md b/tfhe/docs/references/fine-grained-apis/shortint/operations.md
similarity index 98%
rename from tfhe/docs/fine_grained_api/shortint/operations.md
rename to tfhe/docs/references/fine-grained-apis/shortint/operations.md
index e468f74e0..0287cbbbd 100644
--- a/tfhe/docs/fine_grained_api/shortint/operations.md
+++ b/tfhe/docs/references/fine-grained-apis/shortint/operations.md
@@ -8,7 +8,7 @@ In `shortint`, the encrypted data is stored in an LWE ciphertext.
Conceptually, the message stored in an LWE ciphertext is divided into a **carry buffer** and a **message buffer**.
-
+
The message buffer is the space where the actual message is stored. This represents the modulus of the input messages (denoted by `MessageModulus` in the code). When doing computations on a ciphertext, the encrypted message can overflow the message modulus. The part of the message which exceeds the message modulus is stored in the carry buffer. The size of the carry buffer is defined by another modulus, called `CarryModulus`.
@@ -41,10 +41,8 @@ Not all operations have these 4 flavors, as some of them are implemented in a wa
If you don't know which flavor to use, you should use the `default` one.
{% endhint %}
-
## How to use operation types
-
Let's try to do a circuit evaluation using the different flavors of operations that we have already introduced. For a very small circuit, the `unchecked` flavour may be enough to do the computation correctly. Otherwise,`checked` and `smart` are the best options.
Let's do a scalar multiplication, a subtraction, and a multiplication.
@@ -152,7 +150,7 @@ fn main() {
}
```
-The main advantage of the default flavor is to ensure predictable timings as long as this is the only kind of operation which is used.
+The main advantage of the default flavor is to ensure predictable timings as long as this is the only kind of operation which is used.
{% hint style="warning" %}
Using `default` could **slow-down** computations.
diff --git a/tfhe/docs/fine_grained_api/shortint/parameters.md b/tfhe/docs/references/fine-grained-apis/shortint/parameters.md
similarity index 93%
rename from tfhe/docs/fine_grained_api/shortint/parameters.md
rename to tfhe/docs/references/fine-grained-apis/shortint/parameters.md
index 78d09cb9e..1b09f2d57 100644
--- a/tfhe/docs/fine_grained_api/shortint/parameters.md
+++ b/tfhe/docs/references/fine-grained-apis/shortint/parameters.md
@@ -1,6 +1,6 @@
# Cryptographic Parameters
-All parameter sets provide at least 128-bits of security according to the [Lattice-Estimator](https://github.com/malb/lattice-estimator), with an error probability equal to $$2^{-40}$$ when using programmable bootstrapping. This error probability is due to the randomness added at each encryption (see [here](../../getting_started/security_and_cryptography.md) for more details about the encryption process).
+All parameter sets provide at least 128-bits of security according to the [Lattice-Estimator](https://github.com/malb/lattice-estimator), with an error probability equal to $$2^{-40}$$ when using programmable bootstrapping. This error probability is due to the randomness added at each encryption (see [here](../../../getting\_started/security\_and\_cryptography.md) for more details about the encryption process).
## Parameters and message precision
@@ -34,7 +34,7 @@ fn main() {
## Impact of parameters on the operations
-As shown [here](../../getting_started/benchmarks.md), the choice of the parameter set impacts the operations available and their efficiency.
+As shown [here](../../../getting\_started/benchmarks.md), the choice of the parameter set impacts the operations available and their efficiency.
### Generic bi-variate functions.
diff --git a/tfhe/docs/fine_grained_api/shortint/serialization.md b/tfhe/docs/references/fine-grained-apis/shortint/serialization.md
similarity index 100%
rename from tfhe/docs/fine_grained_api/shortint/serialization.md
rename to tfhe/docs/references/fine-grained-apis/shortint/serialization.md
diff --git a/tfhe/docs/tutorials/see-all-tutorials.md b/tfhe/docs/tutorials/see-all-tutorials.md
new file mode 100644
index 000000000..9122b95e2
--- /dev/null
+++ b/tfhe/docs/tutorials/see-all-tutorials.md
@@ -0,0 +1,18 @@
+# See all tutorials
+
+### Start here
+
+* [Homomorphic parity bit](parity\_bit.md)
+* [Homomorphic case changing on Ascii string](ascii\_fhe\_string.md)
+* [SHA 256 with Boolean API](sha256\_bool.md)
+
+### Go further
+
+#### Blog tutorials and articles
+
+* [Dark Market with TFHE-rs](https://www.zama.ai/post/dark-market-tfhe-rs) - July 7, 2023
+* [Regular Expression Engine with TFHE-rs](https://www.zama.ai/post/regex-engine-tfhe-rs) - June 30, 2023
+
+#### Video tutorials
+
+* [Implement signed integers using TFHE-rs](https://www.youtube.com/watch?v=O0aGj\_xUo40) - Nov 8, 2023
diff --git a/tfhe/docs/application_tutorials/sha256_bool.md b/tfhe/docs/tutorials/sha256_bool.md
similarity index 100%
rename from tfhe/docs/application_tutorials/sha256_bool.md
rename to tfhe/docs/tutorials/sha256_bool.md
diff --git a/tfhe/src/high_level_api/integers/oprf.rs b/tfhe/src/high_level_api/integers/oprf.rs
index a0d137cff..6dbe1c774 100644
--- a/tfhe/src/high_level_api/integers/oprf.rs
+++ b/tfhe/src/high_level_api/integers/oprf.rs
@@ -11,8 +11,6 @@ impl FheUint {
/// It can be useful to make server random generation deterministic
///
/// ```rust
- /// use tfhe::integer::gen_keys_radix;
- /// use tfhe::integer::oprf::SignedRandomizationSpec;
/// use tfhe::prelude::FheDecrypt;
/// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8, Seed};
///
@@ -54,10 +52,10 @@ impl FheInt {
/// It can be useful to make server random generation deterministic
///
/// ```rust
- /// use tfhe::integer::gen_keys_radix;
- /// use tfhe::integer::oprf::SignedRandomizationSpec;
/// use tfhe::prelude::FheDecrypt;
- /// use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheInt8, Seed};
+ /// use tfhe::{
+ /// generate_keys, set_server_key, ConfigBuilder, FheInt8, Seed, SignedRandomizationSpec,
+ /// };
///
/// let config = ConfigBuilder::default().build();
/// let (client_key, server_key) = generate_keys(config);
diff --git a/tfhe/src/test_user_docs.rs b/tfhe/src/test_user_docs.rs
index d087f9389..052bd00a3 100644
--- a/tfhe/src/test_user_docs.rs
+++ b/tfhe/src/test_user_docs.rs
@@ -5,6 +5,35 @@ mod test_cpu_doc {
// README
doctest!("../../README.md", readme);
+ // FUNDAMENTALS
+ doctest!("../docs/fundamentals/compress.md", fundamentals_compress);
+ doctest!("../docs/fundamentals/compute.md", fundamentals_compute);
+ doctest!(
+ "../docs/fundamentals/configure-and-generate-keys.md",
+ fundamentals_configure_and_generate_keys
+ );
+ doctest!("../docs/fundamentals/debug.md", fundamentals_debug);
+ doctest!(
+ "../docs/fundamentals/decrypt-data.md",
+ fundamentals_decrypt_data
+ );
+ doctest!(
+ "../docs/fundamentals/encrypt-data.md",
+ fundamentals_encrypt_data
+ );
+ doctest!(
+ "../docs/fundamentals/encrypted-prf.md",
+ fundamentals_encrypted_prf
+ );
+ doctest!(
+ "../docs/fundamentals/serialization.md",
+ fundamentals_serialization
+ );
+ doctest!(
+ "../docs/fundamentals/set-the-server-key.md",
+ fundamentals_set_the_server_key
+ );
+
// GETTING STARTED
doctest!(
"../docs/getting_started/operations.md",
@@ -15,101 +44,103 @@ mod test_cpu_doc {
getting_started_quick_start
);
- // HOW TO
- doctest!("../docs/how_to/compress.md", how_to_compress);
+ // GUIDES
doctest!(
- "../docs/how_to/parallelized_pbs.md",
- how_to_parallelized_pbs
- );
- doctest!("../docs/how_to/public_key.md", how_to_public_key);
- doctest!("../docs/how_to/serialization.md", how_to_serialize);
- doctest!("../docs/how_to/trait_bounds.md", how_to_trait_bounds);
- doctest!(
- "../docs/how_to/trivial_ciphertext.md",
- how_to_trivial_ciphertext
+ "../docs/guides/overflow_operations.md",
+ guides_overflow_operations
);
doctest!(
- "../docs/how_to/overflow_operations.md",
- how_to_detect_overflow
+ "../docs/guides/parallelized_pbs.md",
+ guides_parallelized_pbs
);
- doctest!("../docs/how_to/rayon_crate.md", how_to_rayon_crate);
- doctest!("../docs/how_to/debug.md", how_to_debug);
-
- //FINE GRAINED API
+ doctest!("../docs/guides/pbs-stats.md", guides_pbs_stats);
+ doctest!("../docs/guides/public_key.md", guides_public_key);
+ doctest!("../docs/guides/rayon_crate.md", guides_rayon_crate);
+ doctest!("../docs/guides/trait_bounds.md", guides_trait_bounds);
doctest!(
- "../docs/fine_grained_api/quick_start.md",
- fine_grained_api_quick_start
+ "../docs/guides/trivial_ciphertext.md",
+ guides_trivial_ciphertext
);
- // fine_grained_api/Boolean
+ // REFERENCES
+
+ // FINE GRAINED API
doctest!(
- "../docs/fine_grained_api/Boolean/operations.md",
- booleans_operations
- );
- doctest!(
- "../docs/fine_grained_api/Boolean/parameters.md",
- booleans_parameters
- );
- doctest!(
- "../docs/fine_grained_api/Boolean/serialization.md",
- booleans_serialization
- );
- doctest!(
- "../docs/fine_grained_api/Boolean/readme.md",
- booleans_tutorial
+ "../docs/references/fine-grained-apis/quick_start.md",
+ references_fine_grained_apis_quick_start
);
- // fine_grained_api/shortint
+ // fine-grained-apis/boolean
doctest!(
- "../docs/fine_grained_api/shortint/operations.md",
- shortint_operations
+ "../docs/references/fine-grained-apis/boolean/operations.md",
+ references_fine_grained_apis_boolean_operations
+ );
+ doctest!(
+ "../docs/references/fine-grained-apis/boolean/parameters.md",
+ references_fine_grained_apis_boolean_parameters
+ );
+ doctest!(
+ "../docs/references/fine-grained-apis/boolean/serialization.md",
+ references_fine_grained_apis_boolean_serialization
+ );
+ doctest!(
+ "../docs/references/fine-grained-apis/boolean/README.md",
+ references_fine_grained_apis_boolean_readme
);
+ // fine-grained-apis/shortint
doctest!(
- "../docs/fine_grained_api/shortint/parameters.md",
- shortint_parameters
+ "../docs/references/fine-grained-apis/shortint/operations.md",
+ references_fine_grained_apis_shortint_operations
);
doctest!(
- "../docs/fine_grained_api/shortint/serialization.md",
- shortint_serialization
+ "../docs/references/fine-grained-apis/shortint/parameters.md",
+ references_fine_grained_apis_shortint_parameters
);
doctest!(
- "../docs/fine_grained_api/shortint/readme.md",
- shortint_tutorial
+ "../docs/references/fine-grained-apis/shortint/serialization.md",
+ references_fine_grained_apis_shortint_serialization
+ );
+ doctest!(
+ "../docs/references/fine-grained-apis/shortint/README.md",
+ references_fine_grained_apis_shortint_readme
);
- // fine_grained_api/integer
+ // fine-grained-apis/integer
doctest!(
- "../docs/fine_grained_api/integer/operations.md",
- integer_operations
+ "../docs/references/fine-grained-apis/integer/operations.md",
+ references_fine_grained_apis_integer_operations
);
doctest!(
- "../docs/fine_grained_api/integer/serialization.md",
- integer_serialization_tuto
+ "../docs/references/fine-grained-apis/integer/serialization.md",
+ references_fine_grained_apis_integer_serialization_tuto
);
doctest!(
- "../docs/fine_grained_api/integer/readme.md",
- integer_first_circuit
+ "../docs/references/fine-grained-apis/integer/README.md",
+ references_fine_grained_apis_integer_readme
);
- // core_crypto
+ // references/core-crypto-api
doctest!(
- "../docs/core_crypto/presentation.md",
- core_crypto_presentation
+ "../docs/references/core-crypto-api/presentation.md",
+ references_core_crypto_api_presentation
+ );
+ doctest!(
+ "../docs/references/core-crypto-api/tutorial.md",
+ references_core_crypto_api_tutorial
);
- doctest!("../docs/core_crypto/tutorial.md", core_crypto_tutorial);
// Tutorials
doctest!(
"../docs/tutorials/ascii_fhe_string.md",
- ascii_fhe_string_tutorial
+ tutorials_ascii_fhe_string
);
- doctest!("../docs/tutorials/parity_bit.md", parity_bit_tutorial);
+ doctest!("../docs/tutorials/parity_bit.md", tutorials_parity_bit);
}
#[cfg(feature = "gpu")]
mod test_gpu_doc {
use doc_comment::doctest;
- doctest!("../docs/how_to/run_on_gpu.md", how_to_run_on_gpu);
+ doctest!("../docs/guides/run_on_gpu.md", guides_run_on_gpu);
}