mirror of
https://github.com/zama-ai/tfhe-rs.git
synced 2026-01-11 15:48:20 -05:00
Compare commits
90 Commits
ntt-experi
...
am/refacto
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58169f9365 | ||
|
|
e14be3b41a | ||
|
|
f1c21888a7 | ||
|
|
2624beb7fa | ||
|
|
e44c38a102 | ||
|
|
4535230874 | ||
|
|
a7b2d9b228 | ||
|
|
ab923a3ebc | ||
|
|
a0e85fb355 | ||
|
|
ecee305340 | ||
|
|
f08ea8cf85 | ||
|
|
096e320b97 | ||
|
|
95aac64c1c | ||
|
|
76aaa56691 | ||
|
|
a40489bdd2 | ||
|
|
4bf617eb10 | ||
|
|
070073d229 | ||
|
|
6c1ca8e32b | ||
|
|
6523610ca4 | ||
|
|
41c20e22f5 | ||
|
|
4a00d25cb1 | ||
|
|
8c9ee64612 | ||
|
|
bfdfbfac0f | ||
|
|
dbe7bdcd5c | ||
|
|
6d77ff18ad | ||
|
|
7d4d0e0b16 | ||
|
|
b27762232c | ||
|
|
f597d0f06f | ||
|
|
ee188448f3 | ||
|
|
ee49f048c7 | ||
|
|
a9b09ecc45 | ||
|
|
efc243edc9 | ||
|
|
bc34411d3f | ||
|
|
c7923ff3ed | ||
|
|
7534b68e5c | ||
|
|
655f7e6214 | ||
|
|
b8556ddbd4 | ||
|
|
cab7439064 | ||
|
|
f8a8780651 | ||
|
|
bb3c8e7d5d | ||
|
|
69536960c3 | ||
|
|
52a7c52a49 | ||
|
|
751c407ba5 | ||
|
|
492d348138 | ||
|
|
e7df7eb5ef | ||
|
|
380ee52986 | ||
|
|
439a28f68b | ||
|
|
2eb1e37ca7 | ||
|
|
eb1b136c45 | ||
|
|
1376bcba7c | ||
|
|
b5b4e54b9b | ||
|
|
23c2bd790a | ||
|
|
251ee9aa0e | ||
|
|
fad066a996 | ||
|
|
6ef1f22b33 | ||
|
|
8cc8dba1ab | ||
|
|
082328c91a | ||
|
|
fdb6faa0a8 | ||
|
|
856440386f | ||
|
|
2e8189514c | ||
|
|
29b2454cce | ||
|
|
9ed2589c7a | ||
|
|
36b71529e6 | ||
|
|
b738946d72 | ||
|
|
62f1425257 | ||
|
|
44e491b93f | ||
|
|
a470b26672 | ||
|
|
015409424c | ||
|
|
37be751188 | ||
|
|
2580a834af | ||
|
|
a029bd878e | ||
|
|
400e7930b6 | ||
|
|
40d07c6bc3 | ||
|
|
9dd2d39f1c | ||
|
|
4045a3bc2f | ||
|
|
b4ffeccd46 | ||
|
|
7fe3ad3b6e | ||
|
|
7fdd4f9532 | ||
|
|
81eef39ddb | ||
|
|
b6459e3cda | ||
|
|
f2ef78c348 | ||
|
|
aef8f31621 | ||
|
|
df78d178da | ||
|
|
9297a886a4 | ||
|
|
28b4f91a32 | ||
|
|
04fb46e41b | ||
|
|
53da809f37 | ||
|
|
723910c669 | ||
|
|
8ecf8879fb | ||
|
|
2427f744f8 |
6
.github/workflows/aws_tfhe_fast_tests.yml
vendored
6
.github/workflows/aws_tfhe_fast_tests.yml
vendored
@@ -51,7 +51,7 @@ jobs:
|
||||
echo "Fork git sha: ${{ inputs.fork_git_sha }}"
|
||||
|
||||
- name: Checkout tfhe-rs
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
repository: ${{ inputs.fork_repo }}
|
||||
ref: ${{ inputs.fork_git_sha }}
|
||||
@@ -110,6 +110,10 @@ jobs:
|
||||
run: |
|
||||
make test_high_level_api
|
||||
|
||||
- name: Run safe deserialization tests
|
||||
run: |
|
||||
make test_safe_deserialization
|
||||
|
||||
- name: Slack Notification
|
||||
if: ${{ always() }}
|
||||
continue-on-error: true
|
||||
|
||||
4
.github/workflows/aws_tfhe_integer_tests.yml
vendored
4
.github/workflows/aws_tfhe_integer_tests.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
||||
echo "Fork git sha: ${{ inputs.fork_git_sha }}"
|
||||
|
||||
- name: Checkout tfhe-rs
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
repository: ${{ inputs.fork_repo }}
|
||||
ref: ${{ inputs.fork_git_sha }}
|
||||
@@ -71,7 +71,7 @@ jobs:
|
||||
|
||||
- name: Run integer tests
|
||||
run: |
|
||||
BIG_TESTS_INSTANCE=TRUE make test_integer_ci
|
||||
AVX512_SUPPORT=ON BIG_TESTS_INSTANCE=TRUE make test_integer_ci
|
||||
|
||||
- name: Slack Notification
|
||||
if: ${{ always() }}
|
||||
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
echo "Fork git sha: ${{ inputs.fork_git_sha }}"
|
||||
|
||||
- name: Checkout tfhe-rs
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
repository: ${{ inputs.fork_repo }}
|
||||
ref: ${{ inputs.fork_git_sha }}
|
||||
@@ -75,7 +75,7 @@ jobs:
|
||||
|
||||
- name: Run integer multi-bit tests
|
||||
run: |
|
||||
make test_integer_multi_bit_ci
|
||||
AVX512_SUPPORT=ON make test_integer_multi_bit_ci
|
||||
|
||||
- name: Slack Notification
|
||||
if: ${{ always() }}
|
||||
|
||||
2
.github/workflows/aws_tfhe_tests.yml
vendored
2
.github/workflows/aws_tfhe_tests.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
||||
echo "Fork git sha: ${{ inputs.fork_git_sha }}"
|
||||
|
||||
- name: Checkout tfhe-rs
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
repository: ${{ inputs.fork_repo }}
|
||||
ref: ${{ inputs.fork_git_sha }}
|
||||
|
||||
2
.github/workflows/aws_tfhe_wasm_tests.yml
vendored
2
.github/workflows/aws_tfhe_wasm_tests.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
||||
echo "Fork git sha: ${{ inputs.fork_git_sha }}"
|
||||
|
||||
- name: Checkout tfhe-rs
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
repository: ${{ inputs.fork_repo }}
|
||||
ref: ${{ inputs.fork_git_sha }}
|
||||
|
||||
12
.github/workflows/boolean_benchmark.yml
vendored
12
.github/workflows/boolean_benchmark.yml
vendored
@@ -19,6 +19,14 @@ on:
|
||||
request_id:
|
||||
description: "Slab request ID"
|
||||
type: string
|
||||
# This input is not used in this workflow but still mandatory since a calling workflow could
|
||||
# use it. If a triggering command include a user_inputs field, then the triggered workflow
|
||||
# must include this very input, otherwise the workflow won't be called.
|
||||
# See start_full_benchmarks.yml as example.
|
||||
user_inputs:
|
||||
description: "Type of benchmarks to run"
|
||||
type: string
|
||||
default: "weekly_benchmarks"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -43,7 +51,7 @@ jobs:
|
||||
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Checkout tfhe-rs repo with tags
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -94,7 +102,7 @@ jobs:
|
||||
path: ${{ env.RESULTS_FILENAME }}
|
||||
|
||||
- name: Checkout Slab repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
repository: zama-ai/slab
|
||||
path: slab
|
||||
|
||||
12
.github/workflows/cargo_build.yml
vendored
12
.github/workflows/cargo_build.yml
vendored
@@ -21,7 +21,17 @@ jobs:
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
|
||||
- name: Install and run newline linter checks
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
wget https://github.com/fernandrone/linelint/releases/download/0.0.6/linelint-linux-amd64
|
||||
echo "16b70fb7b471d6f95cbdc0b4e5dc2b0ac9e84ba9ecdc488f7bdf13df823aca4b linelint-linux-amd64" > checksum
|
||||
sha256sum -c checksum || exit 1
|
||||
chmod +x linelint-linux-amd64
|
||||
mv linelint-linux-amd64 /usr/local/bin/linelint
|
||||
make check_newline
|
||||
|
||||
- name: Run pcc checks
|
||||
run: |
|
||||
|
||||
9
.github/workflows/code_coverage.yml
vendored
9
.github/workflows/code_coverage.yml
vendored
@@ -50,7 +50,7 @@ jobs:
|
||||
echo "Fork git sha: ${{ inputs.fork_git_sha }}"
|
||||
|
||||
- name: Checkout tfhe-rs
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
repository: ${{ inputs.fork_repo }}
|
||||
ref: ${{ inputs.fork_git_sha }}
|
||||
@@ -67,7 +67,7 @@ jobs:
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@6ee9cdc5816333acda68e01cf12eedc619e28316
|
||||
uses: tj-actions/changed-files@408093d9ff9c134c33b974e0722ce06b9d6e8263
|
||||
with:
|
||||
files_yaml: |
|
||||
tfhe:
|
||||
@@ -76,15 +76,16 @@ jobs:
|
||||
- concrete-csprng/src/**
|
||||
|
||||
- name: Generate Keys
|
||||
if: steps.changed-files.outputs.tfhe_any_changed == 'true'
|
||||
run: |
|
||||
make GEN_KEY_CACHE_COVERAGE_ONLY=TRUE gen_key_cache
|
||||
|
||||
- name: Run boolean coverage
|
||||
- name: Run coverage for boolean
|
||||
if: steps.changed-files.outputs.tfhe_any_changed == 'true'
|
||||
run: |
|
||||
make test_boolean_cov
|
||||
|
||||
- name: Run shortint coverage
|
||||
- name: Run coverage for shortint
|
||||
if: steps.changed-files.outputs.tfhe_any_changed == 'true'
|
||||
run: |
|
||||
make test_shortint_cov
|
||||
|
||||
@@ -42,7 +42,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout tfhe-rs
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
repository: ${{ inputs.fork_repo }}
|
||||
ref: ${{ inputs.fork_git_sha }}
|
||||
|
||||
4
.github/workflows/integer_benchmark.yml
vendored
4
.github/workflows/integer_benchmark.yml
vendored
@@ -44,7 +44,7 @@ jobs:
|
||||
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Checkout tfhe-rs repo with tags
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
path: ${{ env.RESULTS_FILENAME }}
|
||||
|
||||
- name: Checkout Slab repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
repository: zama-ai/slab
|
||||
path: slab
|
||||
|
||||
35
.github/workflows/integer_full_benchmark.yml
vendored
35
.github/workflows/integer_full_benchmark.yml
vendored
@@ -19,6 +19,10 @@ on:
|
||||
request_id:
|
||||
description: "Slab request ID"
|
||||
type: string
|
||||
user_inputs:
|
||||
description: "Type of benchmarks to run"
|
||||
type: string
|
||||
default: "weekly_benchmarks"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -26,8 +30,33 @@ env:
|
||||
ACTION_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||
|
||||
jobs:
|
||||
prepare-matrix:
|
||||
name: Prepare operations matrix
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
op_flavor: ${{ steps.set_op_flavor.outputs.op_flavor }}
|
||||
steps:
|
||||
- name: Weekly benchmarks
|
||||
if: ${{ github.event.inputs.user_inputs == 'weekly_benchmarks' }}
|
||||
run: |
|
||||
echo "OP_FLAVOR=[\"default\", \"default_comp\", \"default_scalar\", \"default_scalar_comp\"]" >> ${GITHUB_ENV}
|
||||
|
||||
- name: Quarterly benchmarks
|
||||
if: ${{ github.event.inputs.user_inputs == 'quarterly_benchmarks' }}
|
||||
run: |
|
||||
echo "OP_FLAVOR=[\"default\", \"default_comp\", \"default_scalar\", \"default_scalar_comp\", \
|
||||
\"smart\", \"smart_comp\", \"smart_scalar\", \"smart_parallelized\", \"smart_parallelized_comp\", \"smart_scalar_parallelized\", \"smart_scalar_parallelized_comp\", \
|
||||
\"unchecked\", \"unchecked_comp\", \"unchecked_scalar\", \"unchecked_scalar_comp\", \
|
||||
\"misc\"]" >> ${GITHUB_ENV}
|
||||
|
||||
- name: Set operation flavor output
|
||||
id: set_op_flavor
|
||||
run: |
|
||||
echo "op_flavor=${{ toJSON(env.OP_FLAVOR) }}" >> ${GITHUB_OUTPUT}
|
||||
|
||||
integer-benchmarks:
|
||||
name: Execute integer benchmarks for all operations flavor
|
||||
needs: prepare-matrix
|
||||
runs-on: ${{ github.event.inputs.runner_name }}
|
||||
if: ${{ !cancelled() }}
|
||||
continue-on-error: true
|
||||
@@ -35,7 +64,7 @@ jobs:
|
||||
max-parallel: 1
|
||||
matrix:
|
||||
command: [ integer, integer_multi_bit]
|
||||
op_flavor: [ default, default_comp, default_scalar, default_scalar_comp, smart, smart_comp, smart_scalar, smart_parallelized, smart_parallelized_comp, smart_scalar_parallelized, unchecked, unchecked_comp, unchecked_scalar, unchecked_scalar_comp, misc ]
|
||||
op_flavor: ${{ fromJson(needs.prepare-matrix.outputs.op_flavor) }}
|
||||
steps:
|
||||
- name: Instance configuration used
|
||||
run: |
|
||||
@@ -45,7 +74,7 @@ jobs:
|
||||
echo "Request ID: ${{ inputs.request_id }}"
|
||||
|
||||
- name: Checkout tfhe-rs repo with tags
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -67,7 +96,7 @@ jobs:
|
||||
override: true
|
||||
|
||||
- name: Checkout Slab repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
repository: zama-ai/slab
|
||||
path: slab
|
||||
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Checkout tfhe-rs repo with tags
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -96,7 +96,7 @@ jobs:
|
||||
path: ${{ env.RESULTS_FILENAME }}
|
||||
|
||||
- name: Checkout Slab repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
repository: zama-ai/slab
|
||||
path: slab
|
||||
|
||||
2
.github/workflows/m1_tests.yml
vendored
2
.github/workflows/m1_tests.yml
vendored
@@ -28,7 +28,7 @@ jobs:
|
||||
runs-on: ["self-hosted", "m1mac"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
- uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
|
||||
- name: Install latest stable
|
||||
uses: actions-rs/toolchain@16499b5e05bf2e26879000db0c1d13f7e13fa3af
|
||||
|
||||
2
.github/workflows/make_release.yml
vendored
2
.github/workflows/make_release.yml
vendored
@@ -30,7 +30,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
6
.github/workflows/parameters_check.yml
vendored
6
.github/workflows/parameters_check.yml
vendored
@@ -17,10 +17,10 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout tfhe-rs
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
|
||||
- name: Checkout lattice-estimator
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
repository: malb/lattice-estimator
|
||||
path: lattice_estimator
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
|
||||
- name: Collect parameters
|
||||
run: |
|
||||
make write_params_to_file
|
||||
CARGO_PROFILE=devo make write_params_to_file
|
||||
|
||||
- name: Perform security check
|
||||
run: |
|
||||
|
||||
12
.github/workflows/pbs_benchmark.yml
vendored
12
.github/workflows/pbs_benchmark.yml
vendored
@@ -19,6 +19,14 @@ on:
|
||||
request_id:
|
||||
description: "Slab request ID"
|
||||
type: string
|
||||
# This input is not used in this workflow but still mandatory since a calling workflow could
|
||||
# use it. If a triggering command include a user_inputs field, then the triggered workflow
|
||||
# must include this very input, otherwise the workflow won't be called.
|
||||
# See start_full_benchmarks.yml as example.
|
||||
user_inputs:
|
||||
description: "Type of benchmarks to run"
|
||||
type: string
|
||||
default: "weekly_benchmarks"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -43,7 +51,7 @@ jobs:
|
||||
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Checkout tfhe-rs repo with tags
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -84,7 +92,7 @@ jobs:
|
||||
path: ${{ env.RESULTS_FILENAME }}
|
||||
|
||||
- name: Checkout Slab repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
repository: zama-ai/slab
|
||||
path: slab
|
||||
|
||||
4
.github/workflows/shortint_benchmark.yml
vendored
4
.github/workflows/shortint_benchmark.yml
vendored
@@ -43,7 +43,7 @@ jobs:
|
||||
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Checkout tfhe-rs repo with tags
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -94,7 +94,7 @@ jobs:
|
||||
path: ${{ env.RESULTS_FILENAME }}
|
||||
|
||||
- name: Checkout Slab repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
repository: zama-ai/slab
|
||||
path: slab
|
||||
|
||||
12
.github/workflows/shortint_full_benchmark.yml
vendored
12
.github/workflows/shortint_full_benchmark.yml
vendored
@@ -19,6 +19,14 @@ on:
|
||||
request_id:
|
||||
description: "Slab request ID"
|
||||
type: string
|
||||
# This input is not used in this workflow but still mandatory since a calling workflow could
|
||||
# use it. If a triggering command include a user_inputs field, then the triggered workflow
|
||||
# must include this very input, otherwise the workflow won't be called.
|
||||
# See start_full_benchmarks.yml as example.
|
||||
user_inputs:
|
||||
description: "Type of benchmarks to run"
|
||||
type: string
|
||||
default: "weekly_benchmarks"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -43,7 +51,7 @@ jobs:
|
||||
echo "Request ID: ${{ inputs.request_id }}"
|
||||
|
||||
- name: Checkout tfhe-rs repo with tags
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -65,7 +73,7 @@ jobs:
|
||||
override: true
|
||||
|
||||
- name: Checkout Slab repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
repository: zama-ai/slab
|
||||
path: slab
|
||||
|
||||
6
.github/workflows/start_benchmarks.yml
vendored
6
.github/workflows/start_benchmarks.yml
vendored
@@ -42,13 +42,13 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout tfhe-rs
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Check for file changes
|
||||
id: changed-files
|
||||
uses: tj-actions/changed-files@6ee9cdc5816333acda68e01cf12eedc619e28316
|
||||
uses: tj-actions/changed-files@408093d9ff9c134c33b974e0722ce06b9d6e8263
|
||||
with:
|
||||
files_yaml: |
|
||||
common_benches:
|
||||
@@ -85,7 +85,7 @@ jobs:
|
||||
- .github/workflows/wasm_client_benchmark.yml
|
||||
|
||||
- name: Checkout Slab repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
repository: zama-ai/slab
|
||||
path: slab
|
||||
|
||||
30
.github/workflows/start_full_benchmarks.yml
vendored
30
.github/workflows/start_full_benchmarks.yml
vendored
@@ -3,9 +3,21 @@ name: Start full suite benchmarks
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Job will be triggered each Saturday at 1a.m.
|
||||
# Weekly benchmarks will be triggered each Saturday at 1a.m.
|
||||
- cron: '0 1 * * 6'
|
||||
# Quarterly benchmarks will be triggered right before end of quarter, the 25th of the current month at 4a.m.
|
||||
# These benchmarks are far longer to execute hence the reason to run them only four time a year.
|
||||
- cron: '0 4 25 MAR,JUN,SEP,DEC *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
benchmark_type:
|
||||
description: 'Benchmark type'
|
||||
required: true
|
||||
default: 'weekly'
|
||||
type: choice
|
||||
options:
|
||||
- weekly
|
||||
- quarterly
|
||||
|
||||
jobs:
|
||||
start-benchmarks:
|
||||
@@ -16,21 +28,31 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout tfhe-rs
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Checkout Slab repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
repository: zama-ai/slab
|
||||
path: slab
|
||||
token: ${{ secrets.CONCRETE_ACTIONS_TOKEN }}
|
||||
|
||||
- name: Set benchmarks type as weekly
|
||||
if: (github.event_name == 'workflow_dispatch' && inputs.benchmark_type == 'weekly') || github.event.schedule == '0 1 * * 6'
|
||||
run: |
|
||||
echo "BENCH_TYPE=weekly_benchmarks" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Set benchmarks type as quarterly
|
||||
if: (github.event_name == 'workflow_dispatch' && inputs.benchmark_type == 'quarterly') || github.event.schedule == '0 4 25 MAR,JUN,SEP,DEC *'
|
||||
run: |
|
||||
echo "BENCH_TYPE=quarterly_benchmarks" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Start AWS job in Slab
|
||||
shell: bash
|
||||
run: |
|
||||
echo -n '{"command": "${{ matrix.command }}", "git_ref": "${{ github.ref }}", "sha": "${{ github.sha }}"}' > command.json
|
||||
echo -n '{"command": "${{ matrix.command }}", "git_ref": "${{ github.ref }}", "sha": "${{ github.sha }}", "user_inputs": "${{ env.BENCH_TYPE }}"}' > command.json
|
||||
SIGNATURE="$(slab/scripts/hmac_calculator.sh command.json '${{ secrets.JOB_SECRET }}')"
|
||||
curl -v -k \
|
||||
--fail-with-body \
|
||||
|
||||
2
.github/workflows/sync_on_push.yml
vendored
2
.github/workflows/sync_on_push.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Save repo
|
||||
|
||||
12
.github/workflows/wasm_client_benchmark.yml
vendored
12
.github/workflows/wasm_client_benchmark.yml
vendored
@@ -19,6 +19,14 @@ on:
|
||||
request_id:
|
||||
description: "Slab request ID"
|
||||
type: string
|
||||
# This input is not used in this workflow but still mandatory since a calling workflow could
|
||||
# use it. If a triggering command include a user_inputs field, then the triggered workflow
|
||||
# must include this very input, otherwise the workflow won't be called.
|
||||
# See start_full_benchmarks.yml as example.
|
||||
user_inputs:
|
||||
description: "Type of benchmarks to run"
|
||||
type: string
|
||||
default: "weekly_benchmarks"
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -43,7 +51,7 @@ jobs:
|
||||
echo "BENCH_DATE=$(date --iso-8601=seconds)" >> "${GITHUB_ENV}"
|
||||
|
||||
- name: Checkout tfhe-rs repo with tags
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -95,7 +103,7 @@ jobs:
|
||||
path: ${{ env.RESULTS_FILENAME }}
|
||||
|
||||
- name: Checkout Slab repo
|
||||
uses: actions/checkout@3df4ab11eba7bda6032a0b82a6bb43b11571feac
|
||||
uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608
|
||||
with:
|
||||
repository: zama-ai/slab
|
||||
path: slab
|
||||
|
||||
14
.linelint.yml
Normal file
14
.linelint.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
ignore:
|
||||
- .git
|
||||
- target
|
||||
- tfhe/benchmarks_parameters
|
||||
- tfhe/web_wasm_parallel_tests/node_modules
|
||||
- tfhe/web_wasm_parallel_tests/dist
|
||||
- keys
|
||||
- coverage
|
||||
|
||||
rules:
|
||||
# checks if file ends in a newline character
|
||||
end-of-file:
|
||||
enable: true
|
||||
single-new-line: true
|
||||
72
Makefile
72
Makefile
@@ -16,7 +16,6 @@ PARSE_INTEGER_BENCH_CSV_FILE?=tfhe_rs_integer_benches.csv
|
||||
FAST_TESTS?=FALSE
|
||||
FAST_BENCH?=FALSE
|
||||
BENCH_OP_FLAVOR?=DEFAULT
|
||||
COVERAGE_EXCLUDED_FILES = tfhe/benches/*,apps/trivium/src/*,tfhe/examples/*,tasks/src/*
|
||||
# This is done to avoid forgetting it, we still precise the RUSTFLAGS in the commands to be able to
|
||||
# copy paste the command in the terminal and change them if required without forgetting the flags
|
||||
export RUSTFLAGS?=-C target-cpu=native
|
||||
@@ -43,6 +42,22 @@ endif
|
||||
REGEX_STRING?=''
|
||||
REGEX_PATTERN?=''
|
||||
|
||||
# Exclude these files from coverage reports
|
||||
define COVERAGE_EXCLUDED_FILES
|
||||
--exclude-files apps/trivium/src/trivium/* \
|
||||
--exclude-files apps/trivium/src/kreyvium/* \
|
||||
--exclude-files apps/trivium/src/static_deque/* \
|
||||
--exclude-files apps/trivium/src/trans_ciphering/* \
|
||||
--exclude-files tasks/src/* \
|
||||
--exclude-files tfhe/benches/boolean/* \
|
||||
--exclude-files tfhe/benches/core_crypto/* \
|
||||
--exclude-files tfhe/benches/shortint/* \
|
||||
--exclude-files tfhe/benches/integer/* \
|
||||
--exclude-files tfhe/benches/* \
|
||||
--exclude-files tfhe/examples/regex_engine/* \
|
||||
--exclude-files tfhe/examples/utilities/*
|
||||
endef
|
||||
|
||||
.PHONY: rs_check_toolchain # Echo the rust toolchain used for checks
|
||||
rs_check_toolchain:
|
||||
@echo $(RS_CHECK_TOOLCHAIN)
|
||||
@@ -102,14 +117,27 @@ install_tarpaulin: install_rs_build_toolchain
|
||||
cargo $(CARGO_RS_BUILD_TOOLCHAIN) install cargo-tarpaulin --locked || \
|
||||
( echo "Unable to install cargo tarpaulin, unknown error." && exit 1 )
|
||||
|
||||
.PHONY: check_linelint_installed # Check if linelint newline linter is installed
|
||||
check_linelint_installed:
|
||||
@printf "\n" | linelint - > /dev/null 2>&1 || \
|
||||
( echo "Unable to locate linelint. Try installing it: https://github.com/fernandrone/linelint/releases" && exit 1 )
|
||||
|
||||
.PHONY: fmt # Format rust code
|
||||
fmt: install_rs_check_toolchain
|
||||
cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" fmt
|
||||
|
||||
.PHONT: check_fmt # Check rust code format
|
||||
.PHONY: check_fmt # Check rust code format
|
||||
check_fmt: install_rs_check_toolchain
|
||||
cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" fmt --check
|
||||
|
||||
.PHONY: fix_newline # Fix newline at end of file issues to be UNIX compliant
|
||||
fix_newline: check_linelint_installed
|
||||
linelint -a .
|
||||
|
||||
.PHONY: check_newline # Check for newline at end of file to be UNIX compliant
|
||||
check_newline: check_linelint_installed
|
||||
linelint .
|
||||
|
||||
.PHONY: clippy_core # Run clippy lints on core_crypto with and without experimental features
|
||||
clippy_core: install_rs_check_toolchain
|
||||
RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy \
|
||||
@@ -169,7 +197,7 @@ clippy_trivium: install_rs_check_toolchain
|
||||
.PHONY: clippy_all_targets # Run clippy lints on all targets (benches, examples, etc.)
|
||||
clippy_all_targets:
|
||||
RUSTFLAGS="$(RUSTFLAGS)" cargo "$(CARGO_RS_CHECK_TOOLCHAIN)" clippy --all-targets \
|
||||
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,integer,internal-keycache \
|
||||
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,integer,internal-keycache,safe-deserialization \
|
||||
-p tfhe -- --no-deps -D warnings
|
||||
|
||||
.PHONY: clippy_concrete_csprng # Run clippy lints on concrete-csprng
|
||||
@@ -234,13 +262,13 @@ build_tfhe_full: install_rs_build_toolchain
|
||||
.PHONY: build_c_api # Build the C API for boolean, shortint and integer
|
||||
build_c_api: install_rs_check_toolchain
|
||||
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) build --profile $(CARGO_PROFILE) \
|
||||
--features=$(TARGET_ARCH_FEATURE),boolean-c-api,shortint-c-api,high-level-c-api, \
|
||||
--features=$(TARGET_ARCH_FEATURE),boolean-c-api,shortint-c-api,high-level-c-api,safe-deserialization \
|
||||
-p tfhe
|
||||
|
||||
.PHONY: build_c_api_experimental_deterministic_fft # Build the C API for boolean, shortint and integer with experimental deterministic FFT
|
||||
build_c_api_experimental_deterministic_fft: install_rs_check_toolchain
|
||||
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) build --profile $(CARGO_PROFILE) \
|
||||
--features=$(TARGET_ARCH_FEATURE),boolean-c-api,shortint-c-api,high-level-c-api,experimental-force_fft_algo_dif4 \
|
||||
--features=$(TARGET_ARCH_FEATURE),boolean-c-api,shortint-c-api,high-level-c-api,safe-deserialization,experimental-force_fft_algo_dif4 \
|
||||
-p tfhe
|
||||
|
||||
.PHONY: build_web_js_api # Build the js API targeting the web browser
|
||||
@@ -288,15 +316,15 @@ test_boolean: install_rs_build_toolchain
|
||||
.PHONY: test_boolean_cov # Run the tests of the boolean module with code coverage
|
||||
test_boolean_cov: install_rs_check_toolchain install_tarpaulin
|
||||
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) tarpaulin --profile $(CARGO_PROFILE) \
|
||||
--out Xml --output-dir coverage/boolean --line --engine Llvm --timeout 500 \
|
||||
--exclude-files $(COVERAGE_EXCLUDED_FILES) \
|
||||
--out xml --output-dir coverage/boolean --line --engine llvm --timeout 500 \
|
||||
$(COVERAGE_EXCLUDED_FILES) \
|
||||
--features=$(TARGET_ARCH_FEATURE),boolean,internal-keycache,__coverage \
|
||||
-p tfhe -- boolean::
|
||||
|
||||
.PHONY: test_c_api_rs # Run the rust tests for the C API
|
||||
test_c_api_rs: install_rs_check_toolchain
|
||||
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) test --profile $(CARGO_PROFILE) \
|
||||
--features=$(TARGET_ARCH_FEATURE),boolean-c-api,shortint-c-api,high-level-c-api \
|
||||
--features=$(TARGET_ARCH_FEATURE),boolean-c-api,shortint-c-api,high-level-c-api,safe-deserialization \
|
||||
-p tfhe \
|
||||
c_api
|
||||
|
||||
@@ -329,24 +357,29 @@ test_shortint: install_rs_build_toolchain
|
||||
.PHONY: test_shortint_cov # Run the tests of the shortint module with code coverage
|
||||
test_shortint_cov: install_rs_check_toolchain install_tarpaulin
|
||||
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_CHECK_TOOLCHAIN) tarpaulin --profile $(CARGO_PROFILE) \
|
||||
--out Xml --output-dir coverage/shortint --line --engine Llvm --timeout 500 \
|
||||
--exclude-files $(COVERAGE_EXCLUDED_FILES) \
|
||||
--out xml --output-dir coverage/shortint --line --engine llvm --timeout 500 \
|
||||
$(COVERAGE_EXCLUDED_FILES) \
|
||||
--features=$(TARGET_ARCH_FEATURE),shortint,internal-keycache,__coverage \
|
||||
-p tfhe -- shortint::
|
||||
|
||||
.PHONY: test_integer_ci # Run the tests for integer ci
|
||||
test_integer_ci: install_rs_build_toolchain install_cargo_nextest
|
||||
test_integer_ci: install_rs_check_toolchain install_cargo_nextest
|
||||
BIG_TESTS_INSTANCE="$(BIG_TESTS_INSTANCE)" \
|
||||
FAST_TESTS="$(FAST_TESTS)" \
|
||||
./scripts/integer-tests.sh --rust-toolchain $(CARGO_RS_BUILD_TOOLCHAIN) \
|
||||
--cargo-profile "$(CARGO_PROFILE)"
|
||||
./scripts/integer-tests.sh --rust-toolchain $(CARGO_RS_CHECK_TOOLCHAIN) \
|
||||
--cargo-profile "$(CARGO_PROFILE)" --avx512-support "$(AVX512_SUPPORT)"
|
||||
|
||||
.PHONY: test_integer_multi_bit_ci # Run the tests for integer ci running only multibit tests
|
||||
test_integer_multi_bit_ci: install_rs_build_toolchain install_cargo_nextest
|
||||
test_integer_multi_bit_ci: install_rs_check_toolchain install_cargo_nextest
|
||||
BIG_TESTS_INSTANCE="$(BIG_TESTS_INSTANCE)" \
|
||||
FAST_TESTS="$(FAST_TESTS)" \
|
||||
./scripts/integer-tests.sh --rust-toolchain $(CARGO_RS_BUILD_TOOLCHAIN) \
|
||||
--cargo-profile "$(CARGO_PROFILE)" --multi-bit
|
||||
./scripts/integer-tests.sh --rust-toolchain $(CARGO_RS_CHECK_TOOLCHAIN) \
|
||||
--cargo-profile "$(CARGO_PROFILE)" --multi-bit --avx512-support "$(AVX512_SUPPORT)"
|
||||
|
||||
.PHONY: test_safe_deserialization # Run the tests for safe deserialization
|
||||
test_safe_deserialization: install_rs_build_toolchain install_cargo_nextest
|
||||
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --profile $(CARGO_PROFILE) \
|
||||
--features=$(TARGET_ARCH_FEATURE),boolean,shortint,integer,internal-keycache,safe-deserialization -p tfhe -- safe_deserialization::
|
||||
|
||||
.PHONY: test_integer # Run all the tests for integer
|
||||
test_integer: install_rs_build_toolchain
|
||||
@@ -425,7 +458,7 @@ format_doc_latex:
|
||||
.PHONY: check_compile_tests # Build tests in debug without running them
|
||||
check_compile_tests:
|
||||
RUSTFLAGS="$(RUSTFLAGS)" cargo $(CARGO_RS_BUILD_TOOLCHAIN) test --no-run \
|
||||
--features=$(TARGET_ARCH_FEATURE),experimental,boolean,shortint,integer,internal-keycache \
|
||||
--features=$(TARGET_ARCH_FEATURE),experimental,boolean,shortint,integer,internal-keycache,safe-deserialization \
|
||||
-p tfhe
|
||||
|
||||
@if [[ "$(OS)" == "Linux" || "$(OS)" == "Darwin" ]]; then \
|
||||
@@ -460,7 +493,8 @@ test_web_js_api_parallel: build_web_js_api_parallel
|
||||
.PHONY: ci_test_web_js_api_parallel # Run tests for the web wasm api
|
||||
ci_test_web_js_api_parallel: build_web_js_api_parallel
|
||||
source ~/.nvm/nvm.sh && \
|
||||
nvm use node && \
|
||||
nvm install 20 && \
|
||||
nvm use 20 && \
|
||||
$(MAKE) -C tfhe/web_wasm_parallel_tests test-ci
|
||||
|
||||
.PHONY: no_tfhe_typo # Check we did not invert the h and f in tfhe
|
||||
@@ -604,7 +638,7 @@ pcc: no_tfhe_typo no_dbg_log check_fmt lint_doc clippy_all check_compile_tests
|
||||
fpcc: no_tfhe_typo no_dbg_log check_fmt lint_doc clippy_fast check_compile_tests
|
||||
|
||||
.PHONY: conformance # Automatically fix problems that can be fixed
|
||||
conformance: fmt
|
||||
conformance: fix_newline fmt
|
||||
|
||||
.PHONY: help # Generate list of targets with descriptions
|
||||
help:
|
||||
|
||||
@@ -17,7 +17,7 @@ path = "../../tfhe"
|
||||
features = [ "boolean", "shortint", "integer", "aarch64-unix" ]
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { version = "0.4", features = [ "html_reports" ]}
|
||||
criterion = { version = "0.5.1", features = [ "html_reports" ]}
|
||||
|
||||
[[bench]]
|
||||
name = "trivium"
|
||||
|
||||
@@ -120,7 +120,7 @@ fn main() {
|
||||
|
||||
# FHE byte Trivium implementation
|
||||
|
||||
The same objects have also been implemented to stream bytes insead of booleans. They can be constructed and used in the same way via the functions `TriviumStreamByte::<u8>::new` and
|
||||
The same objects have also been implemented to stream bytes instead of booleans. They can be constructed and used in the same way via the functions `TriviumStreamByte::<u8>::new` and
|
||||
`TriviumStreamByte::<FheUint8>::new` with the same arguments as before. The `FheUint8` version is significantly slower than the `FheBool` version, because not running
|
||||
with the same cryptographic parameters. Its interest lie in its trans-ciphering capabilities: `TriviumStreamByte<FheUint8>` implements the trait `TransCiphering`,
|
||||
meaning it implements the functions `trans_encrypt_64`. This function takes as input a `FheUint64` and outputs a `FheUint64`, the output being
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! This module implements the Kreyvium stream cipher, using booleans or FheBool
|
||||
//! for the representaion of the inner bits.
|
||||
//! for the representation of the inner bits.
|
||||
|
||||
use crate::static_deque::StaticDeque;
|
||||
|
||||
@@ -35,7 +35,7 @@ pub struct KreyviumStream<T> {
|
||||
}
|
||||
|
||||
impl KreyviumStream<bool> {
|
||||
/// Contructor for `KreyviumStream<bool>`: arguments are the secret key and the input vector.
|
||||
/// Constructor for `KreyviumStream<bool>`: arguments are the secret key and the input vector.
|
||||
/// Outputs a KreyviumStream object already initialized (1152 steps have been run before
|
||||
/// returning)
|
||||
pub fn new(mut key: [bool; 128], mut iv: [bool; 128]) -> KreyviumStream<bool> {
|
||||
@@ -118,7 +118,7 @@ where
|
||||
T: KreyviumBoolInput<T> + std::marker::Send + std::marker::Sync,
|
||||
for<'a> &'a T: KreyviumBoolInput<T>,
|
||||
{
|
||||
/// Internal generic contructor: arguments are already prepared registers, and an optional FHE
|
||||
/// Internal generic constructor: arguments are already prepared registers, and an optional FHE
|
||||
/// server key
|
||||
fn new_from_registers(
|
||||
a_register: [T; 93],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! This module implements the Kreyvium stream cipher, using u8 or FheUint8
|
||||
//! for the representaion of the inner bits.
|
||||
//! for the representation of the inner bits.
|
||||
|
||||
use crate::static_deque::{StaticByteDeque, StaticByteDequeInput};
|
||||
|
||||
@@ -31,7 +31,7 @@ impl KreyviumByteInput<FheUint8> for &FheUint8 {}
|
||||
/// representation of bits (u8 or FheUint8). To be able to compute FHE operations, it also owns
|
||||
/// an Option for a ServerKey.
|
||||
/// Since the original Kreyvium registers' sizes are not a multiple of 8, these registers (which
|
||||
/// store byte-like objects) have a size that is the eigth of the closest multiple of 8 above the
|
||||
/// store byte-like objects) have a size that is the eighth of the closest multiple of 8 above the
|
||||
/// originals' sizes.
|
||||
pub struct KreyviumStreamByte<T> {
|
||||
a_byte: StaticByteDeque<12, T>,
|
||||
@@ -43,7 +43,7 @@ pub struct KreyviumStreamByte<T> {
|
||||
}
|
||||
|
||||
impl KreyviumStreamByte<u8> {
|
||||
/// Contructor for `KreyviumStreamByte<u8>`: arguments are the secret key and the input vector.
|
||||
/// Constructor for `KreyviumStreamByte<u8>`: arguments are the secret key and the input vector.
|
||||
/// Outputs a KreyviumStream object already initialized (1152 steps have been run before
|
||||
/// returning)
|
||||
pub fn new(key_bytes: [u8; 16], iv_bytes: [u8; 16]) -> KreyviumStreamByte<u8> {
|
||||
@@ -146,7 +146,7 @@ where
|
||||
T: KreyviumByteInput<T> + Send,
|
||||
for<'a> &'a T: KreyviumByteInput<T>,
|
||||
{
|
||||
/// Internal generic contructor: arguments are already prepared registers, and an optional FHE
|
||||
/// Internal generic constructor: arguments are already prepared registers, and an optional FHE
|
||||
/// server key
|
||||
fn new_from_registers(
|
||||
a_register: [T; 12],
|
||||
|
||||
@@ -19,7 +19,7 @@ pub struct KreyviumStreamShortint {
|
||||
}
|
||||
|
||||
impl KreyviumStreamShortint {
|
||||
/// Contructor for KreyviumStreamShortint: arguments are the secret key and the input vector,
|
||||
/// Constructor for KreyviumStreamShortint: arguments are the secret key and the input vector,
|
||||
/// and a ServerKey reference. Outputs a KreyviumStream object already initialized (1152
|
||||
/// steps have been run before returning)
|
||||
pub fn new(
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! This module implements the StaticByteDeque struct: a deque of bytes. The idea
|
||||
//! is that this is a wrapper around StaticDeque, but StaticByteDeque has an additional
|
||||
//! functionnality: it can construct the "intermediate" bytes, made of parts of other bytes.
|
||||
//! functionality: it can construct the "intermediate" bytes, made of parts of other bytes.
|
||||
//! This is pretending to store bits, and allows accessing bits in chunks of 8 consecutive.
|
||||
|
||||
use crate::static_deque::StaticDeque;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
use core::ops::{Index, IndexMut};
|
||||
|
||||
/// StaticDeque: a struct implementing a deque whose size is known at compile time.
|
||||
/// It has 2 members: the static array conatining the data (never empty), and a cursor
|
||||
/// It has 2 members: the static array containing the data (never empty), and a cursor
|
||||
/// equal to the index of the oldest element (and the next one to be overwritten).
|
||||
#[derive(Clone)]
|
||||
pub struct StaticDeque<const N: usize, T> {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! This module implements the Trivium stream cipher, using booleans or FheBool
|
||||
//! for the representaion of the inner bits.
|
||||
//! for the representation of the inner bits.
|
||||
|
||||
use crate::static_deque::StaticDeque;
|
||||
|
||||
@@ -33,7 +33,7 @@ pub struct TriviumStream<T> {
|
||||
}
|
||||
|
||||
impl TriviumStream<bool> {
|
||||
/// Contructor for `TriviumStream<bool>`: arguments are the secret key and the input vector.
|
||||
/// Constructor for `TriviumStream<bool>`: arguments are the secret key and the input vector.
|
||||
/// Outputs a TriviumStream object already initialized (1152 steps have been run before
|
||||
/// returning)
|
||||
pub fn new(key: [bool; 80], iv: [bool; 80]) -> TriviumStream<bool> {
|
||||
@@ -94,7 +94,7 @@ where
|
||||
T: TriviumBoolInput<T> + std::marker::Send + std::marker::Sync,
|
||||
for<'a> &'a T: TriviumBoolInput<T>,
|
||||
{
|
||||
/// Internal generic contructor: arguments are already prepared registers, and an optional FHE
|
||||
/// Internal generic constructor: arguments are already prepared registers, and an optional FHE
|
||||
/// server key
|
||||
fn new_from_registers(
|
||||
a_register: [T; 93],
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//! This module implements the Trivium stream cipher, using u8 or FheUint8
|
||||
//! for the representaion of the inner bits.
|
||||
//! for the representation of the inner bits.
|
||||
|
||||
use crate::static_deque::{StaticByteDeque, StaticByteDequeInput};
|
||||
|
||||
@@ -31,7 +31,7 @@ impl TriviumByteInput<FheUint8> for &FheUint8 {}
|
||||
/// representation of bits (u8 or FheUint8). To be able to compute FHE operations, it also owns
|
||||
/// an Option for a ServerKey.
|
||||
/// Since the original Trivium registers' sizes are not a multiple of 8, these registers (which
|
||||
/// store byte-like objects) have a size that is the eigth of the closest multiple of 8 above the
|
||||
/// store byte-like objects) have a size that is the eighth of the closest multiple of 8 above the
|
||||
/// originals' sizes.
|
||||
pub struct TriviumStreamByte<T> {
|
||||
a_byte: StaticByteDeque<12, T>,
|
||||
@@ -41,7 +41,7 @@ pub struct TriviumStreamByte<T> {
|
||||
}
|
||||
|
||||
impl TriviumStreamByte<u8> {
|
||||
/// Contructor for `TriviumStreamByte<u8>`: arguments are the secret key and the input vector.
|
||||
/// Constructor for `TriviumStreamByte<u8>`: arguments are the secret key and the input vector.
|
||||
/// Outputs a TriviumStream object already initialized (1152 steps have been run before
|
||||
/// returning)
|
||||
pub fn new(key: [u8; 10], iv: [u8; 10]) -> TriviumStreamByte<u8> {
|
||||
@@ -111,7 +111,7 @@ where
|
||||
T: TriviumByteInput<T> + Send,
|
||||
for<'a> &'a T: TriviumByteInput<T>,
|
||||
{
|
||||
/// Internal generic contructor: arguments are already prepared registers, and an optional FHE
|
||||
/// Internal generic constructor: arguments are already prepared registers, and an optional FHE
|
||||
/// server key
|
||||
fn new_from_registers(
|
||||
a_register: [T; 12],
|
||||
|
||||
@@ -17,9 +17,9 @@ pub struct TriviumStreamShortint {
|
||||
}
|
||||
|
||||
impl TriviumStreamShortint {
|
||||
/// Contructor for TriviumStreamShortint: arguments are the secret key and the input vector, and
|
||||
/// a ServerKey reference. Outputs a TriviumStream object already initialized (1152 steps
|
||||
/// have been run before returning)
|
||||
/// Constructor for TriviumStreamShortint: arguments are the secret key and the input vector,
|
||||
/// and a ServerKey reference. Outputs a TriviumStream object already initialized (1152
|
||||
/// steps have been run before returning)
|
||||
pub fn new(
|
||||
key: [Ciphertext; 80],
|
||||
iv: [u64; 80],
|
||||
|
||||
@@ -36,23 +36,24 @@ def check_security(filename):
|
||||
try:
|
||||
# The lattice estimator is not able to manage such large dimension.
|
||||
# If we have the security for smaller `n` then we have security for larger ones.
|
||||
if param.n >= 32768:
|
||||
if param.n > 16384:
|
||||
param = param.updated(n = 16384)
|
||||
|
||||
usvp_level = LWE.primal_usvp(param, red_cost_model = model)
|
||||
dual_level = LWE.dual_hybrid(param, red_cost_model = model)
|
||||
|
||||
estimator_level = log(min(usvp_level["rop"], dual_level["rop"]),2 )
|
||||
security_level = f"security level = {estimator_level} bits"
|
||||
if estimator_level < 127:
|
||||
print("FAIL")
|
||||
reason = f"attained security level = {estimator_level} bits target is 128 bits"
|
||||
print("FAIL\t({security_level})")
|
||||
reason = f"attained {security_level} target is 128 bits"
|
||||
to_update.append((param, reason))
|
||||
continue
|
||||
except Exception as err:
|
||||
print("FAIL")
|
||||
to_update.append((param, f"{repr(err)}"))
|
||||
else:
|
||||
print("OK")
|
||||
print(f"OK\t({security_level})")
|
||||
|
||||
return to_update
|
||||
|
||||
@@ -72,4 +73,4 @@ if __name__ == "__main__":
|
||||
print(f"[{param.tag}] reason: {reason} (param)")
|
||||
sys.exit(int(1)) # Explicit conversion is needed to make this call work
|
||||
else:
|
||||
print("All parameters passed the security check")
|
||||
print("All parameters passed the security check")
|
||||
|
||||
@@ -9,6 +9,7 @@ documentation = "https://docs.zama.ai/tfhe-rs"
|
||||
repository = "https://github.com/zama-ai/tfhe-rs"
|
||||
readme = "README.md"
|
||||
keywords = ["fully", "homomorphic", "encryption", "fhe", "cryptography"]
|
||||
rust-version = "1.72"
|
||||
|
||||
[dependencies]
|
||||
aes = "0.8.2"
|
||||
@@ -19,8 +20,8 @@ libc = "0.2.133"
|
||||
|
||||
[dev-dependencies]
|
||||
rand = "0.8.3"
|
||||
criterion = "0.3"
|
||||
clap = "=4.2.7"
|
||||
criterion = "0.5.1"
|
||||
clap = "=4.4.4"
|
||||
|
||||
[features]
|
||||
parallel = ["rayon"]
|
||||
|
||||
@@ -17,7 +17,7 @@ pub struct BytesPerChild(pub usize);
|
||||
#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq)]
|
||||
pub struct ByteCount(pub u128);
|
||||
|
||||
/// An error occuring during a generator fork.
|
||||
/// An error occurring during a generator fork.
|
||||
#[derive(Debug)]
|
||||
pub enum ForkError {
|
||||
ForkTooLarge,
|
||||
@@ -145,7 +145,7 @@ pub mod generator_generic_test {
|
||||
|
||||
/// Checks that the PRNG roughly generates uniform numbers.
|
||||
///
|
||||
/// To do that, we perform an histogram of the occurences of each byte value, over a fixed
|
||||
/// To do that, we perform an histogram of the occurrences of each byte value, over a fixed
|
||||
/// number of samples and check that the empirical probabilities of the bins are close to
|
||||
/// the theoretical probabilities.
|
||||
pub fn test_roughly_uniform<G: RandomGenerator>() {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//! When initializing a generator, one needs to provide a [`Seed`], which is then used as key to the
|
||||
//! AES blockcipher. As a consequence, the quality of the outputs of the generator is directly
|
||||
//! conditioned by the quality of this seed. This module proposes different mechanisms to deliver
|
||||
//! seeds that can accomodate varying scenarios.
|
||||
//! seeds that can accommodate varying scenarios.
|
||||
|
||||
/// A seed value, used to initialize a generator.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
@@ -15,7 +15,7 @@ pub trait Seeder {
|
||||
fn seed(&mut self) -> Seed;
|
||||
|
||||
/// Check whether the seeder can be used on the current machine. This function may check if some
|
||||
/// required CPU features are available or if some OS features are availble for example.
|
||||
/// required CPU features are available or if some OS features are available for example.
|
||||
fn is_available() -> bool
|
||||
where
|
||||
Self: Sized;
|
||||
|
||||
@@ -34,6 +34,6 @@ RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > install-rustup.s
|
||||
chmod +x install-node.sh && \
|
||||
./install-node.sh && \
|
||||
. "$HOME/.nvm/nvm.sh" && \
|
||||
bash -i -c 'nvm install node && nvm use node'
|
||||
bash -i -c 'nvm install 20 && nvm use 20'
|
||||
|
||||
WORKDIR /tfhe-wasm-tests/tfhe-rs/
|
||||
|
||||
@@ -9,6 +9,7 @@ function usage() {
|
||||
echo "--rust-toolchain The toolchain to run the tests with default: stable"
|
||||
echo "--multi-bit Run multi-bit tests only: default off"
|
||||
echo "--cargo-profile The cargo profile used to build tests"
|
||||
echo "--avx512-support Set to ON to enable avx512"
|
||||
echo
|
||||
}
|
||||
|
||||
@@ -16,6 +17,7 @@ RUST_TOOLCHAIN="+stable"
|
||||
multi_bit=""
|
||||
not_multi_bit="_multi_bit"
|
||||
cargo_profile="release"
|
||||
avx512_feature=""
|
||||
|
||||
while [ -n "$1" ]
|
||||
do
|
||||
@@ -40,6 +42,13 @@ do
|
||||
cargo_profile="$1"
|
||||
;;
|
||||
|
||||
"--avx512-support" )
|
||||
shift
|
||||
if [[ "$1" == "ON" ]]; then
|
||||
avx512_feature=nightly-avx512
|
||||
fi
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Unknown param : $1"
|
||||
exit 1
|
||||
@@ -104,7 +113,7 @@ and not test(/.*default_add_sequence_multi_thread_param_message_3_carry_3_ks_pbs
|
||||
--cargo-profile "${cargo_profile}" \
|
||||
--package tfhe \
|
||||
--profile ci \
|
||||
--features="${ARCH_FEATURE}",integer,internal-keycache \
|
||||
--features="${ARCH_FEATURE}",integer,internal-keycache,"${avx512_feature}" \
|
||||
--test-threads "${n_threads}" \
|
||||
-E "$filter_expression"
|
||||
|
||||
@@ -112,7 +121,7 @@ and not test(/.*default_add_sequence_multi_thread_param_message_3_carry_3_ks_pbs
|
||||
cargo "${RUST_TOOLCHAIN}" test \
|
||||
--profile "${cargo_profile}" \
|
||||
--package tfhe \
|
||||
--features="${ARCH_FEATURE}",integer,internal-keycache \
|
||||
--features="${ARCH_FEATURE}",integer,internal-keycache,"${avx512_feature}" \
|
||||
--doc \
|
||||
-- integer::
|
||||
fi
|
||||
@@ -148,7 +157,7 @@ and not test(/.*default_add_sequence_multi_thread_param_message_3_carry_3_ks_pbs
|
||||
--cargo-profile "${cargo_profile}" \
|
||||
--package tfhe \
|
||||
--profile ci \
|
||||
--features="${ARCH_FEATURE}",integer,internal-keycache \
|
||||
--features="${ARCH_FEATURE}",integer,internal-keycache,"${avx512_feature}" \
|
||||
--test-threads $num_threads \
|
||||
-E "$filter_expression"
|
||||
|
||||
@@ -156,7 +165,7 @@ and not test(/.*default_add_sequence_multi_thread_param_message_3_carry_3_ks_pbs
|
||||
cargo "${RUST_TOOLCHAIN}" test \
|
||||
--profile "${cargo_profile}" \
|
||||
--package tfhe \
|
||||
--features="${ARCH_FEATURE}",integer,internal-keycache \
|
||||
--features="${ARCH_FEATURE}",integer,internal-keycache,"${avx512_feature}" \
|
||||
--doc \
|
||||
-- --test-threads="$(${nproc_bin})" integer::
|
||||
fi
|
||||
|
||||
@@ -6,7 +6,7 @@ edition = "2021"
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = "3.1"
|
||||
clap = "=4.4.4"
|
||||
lazy_static = "1.4"
|
||||
log = "0.4"
|
||||
simplelog = "0.12"
|
||||
|
||||
2
tfhe/.gitignore
vendored
2
tfhe/.gitignore
vendored
@@ -1 +1 @@
|
||||
build/
|
||||
build/
|
||||
|
||||
@@ -26,16 +26,16 @@ rand = "0.8.5"
|
||||
rand_distr = "0.4.3"
|
||||
paste = "1.0.7"
|
||||
lazy_static = { version = "1.4.0" }
|
||||
criterion = "0.4.0"
|
||||
criterion = "0.5.1"
|
||||
doc-comment = "0.3.3"
|
||||
serde_json = "1.0.94"
|
||||
# clap has to be pinned as its minimum supported rust version
|
||||
# changes often between minor releases, which breaks our CI
|
||||
clap = { version = "=4.2.7", features = ["derive"] }
|
||||
clap = { version = "=4.4.4", features = ["derive"] }
|
||||
# Used in user documentation
|
||||
bincode = "1.3.3"
|
||||
fs2 = { version = "0.4.3" }
|
||||
itertools = "0.10.5"
|
||||
itertools = "0.11.0"
|
||||
# For erf and normality test
|
||||
libm = "0.2.6"
|
||||
# Begin regex-engine deps
|
||||
@@ -46,7 +46,7 @@ log = "0.4.19"
|
||||
# End regex-engine deps
|
||||
|
||||
[build-dependencies]
|
||||
cbindgen = { version = "0.24.3", optional = true }
|
||||
cbindgen = { version = "0.26.0", optional = true }
|
||||
|
||||
[dependencies]
|
||||
concrete-csprng = { version = "0.4.0", path= "../concrete-csprng", features = [
|
||||
@@ -64,7 +64,7 @@ dyn-stack = { version = "0.9" }
|
||||
paste = { version = "1.0.7", optional = true }
|
||||
fs2 = { version = "0.4.3", optional = true }
|
||||
# While we wait for repeat_n in rust standard library
|
||||
itertools = "0.10.5"
|
||||
itertools = "0.11.0"
|
||||
|
||||
# wasm deps
|
||||
wasm-bindgen = { version = "0.2.86", features = [
|
||||
@@ -73,7 +73,7 @@ wasm-bindgen = { version = "0.2.86", features = [
|
||||
wasm-bindgen-rayon = { version = "1.0", optional = true }
|
||||
js-sys = { version = "0.3", optional = true }
|
||||
console_error_panic_hook = { version = "0.1.7", optional = true }
|
||||
serde-wasm-bindgen = { version = "0.4", optional = true }
|
||||
serde-wasm-bindgen = { version = "0.6.0", optional = true }
|
||||
getrandom = { version = "0.2.8", optional = true }
|
||||
bytemuck = "1.13.1"
|
||||
|
||||
@@ -82,14 +82,15 @@ bytemuck = "1.13.1"
|
||||
boolean = ["dep:paste"]
|
||||
shortint = ["dep:paste"]
|
||||
integer = ["shortint", "dep:paste"]
|
||||
internal-keycache = ["lazy_static", "dep:fs2", "bincode", "dep:paste"]
|
||||
internal-keycache = ["lazy_static", "dep:fs2", "dep:bincode", "dep:paste"]
|
||||
safe-deserialization = ["dep:bincode"]
|
||||
|
||||
# Experimental section
|
||||
experimental = []
|
||||
experimental-force_fft_algo_dif4 = []
|
||||
# End experimental section
|
||||
|
||||
__c_api = ["cbindgen", "bincode", "dep:paste"]
|
||||
__c_api = ["cbindgen", "dep:bincode", "dep:paste"]
|
||||
boolean-c-api = ["boolean", "__c_api"]
|
||||
shortint-c-api = ["shortint", "__c_api"]
|
||||
high-level-c-api = ["boolean-c-api", "shortint-c-api", "integer", "__c_api"]
|
||||
@@ -101,7 +102,8 @@ __wasm_api = [
|
||||
"serde-wasm-bindgen",
|
||||
"getrandom",
|
||||
"getrandom/js",
|
||||
"bincode",
|
||||
"dep:bincode",
|
||||
"safe-deserialization",
|
||||
]
|
||||
boolean-client-js-wasm-api = ["boolean", "__wasm_api"]
|
||||
shortint-client-js-wasm-api = ["shortint", "__wasm_api"]
|
||||
@@ -182,6 +184,12 @@ path = "benches/integer/bench.rs"
|
||||
harness = false
|
||||
required-features = ["integer", "internal-keycache"]
|
||||
|
||||
[[bench]]
|
||||
name = "integer-signed-bench"
|
||||
path = "benches/integer/signed_bench.rs"
|
||||
harness = false
|
||||
required-features = ["integer", "internal-keycache"]
|
||||
|
||||
[[bench]]
|
||||
name = "keygen"
|
||||
path = "benches/keygen/bench.rs"
|
||||
|
||||
@@ -43,7 +43,7 @@ pub fn write_to_json_boolean<T: Into<CryptoParametersRecord<u32>>>(
|
||||
|
||||
// Put all `bench_function` in one place
|
||||
// so the keygen is only run once per parameters saving time.
|
||||
fn benchs(c: &mut Criterion, params: BooleanParameters, parameter_name: &str) {
|
||||
fn benches(c: &mut Criterion, params: BooleanParameters, parameter_name: &str) {
|
||||
let mut bench_group = c.benchmark_group("gates_benches");
|
||||
|
||||
let cks = ClientKey::new(¶ms);
|
||||
@@ -83,15 +83,15 @@ fn benchs(c: &mut Criterion, params: BooleanParameters, parameter_name: &str) {
|
||||
}
|
||||
|
||||
fn bench_default_parameters(c: &mut Criterion) {
|
||||
benchs(c, DEFAULT_PARAMETERS, "DEFAULT_PARAMETERS");
|
||||
benches(c, DEFAULT_PARAMETERS, "DEFAULT_PARAMETERS");
|
||||
}
|
||||
|
||||
fn bench_default_parameters_ks_pbs(c: &mut Criterion) {
|
||||
benchs(c, DEFAULT_PARAMETERS_KS_PBS, "DEFAULT_PARAMETERS_KS_PBS");
|
||||
benches(c, DEFAULT_PARAMETERS_KS_PBS, "DEFAULT_PARAMETERS_KS_PBS");
|
||||
}
|
||||
|
||||
fn bench_low_prob_parameters(c: &mut Criterion) {
|
||||
benchs(
|
||||
benches(
|
||||
c,
|
||||
PARAMETERS_ERROR_PROB_2_POW_MINUS_165,
|
||||
"PARAMETERS_ERROR_PROB_2_POW_MINUS_165",
|
||||
@@ -99,7 +99,7 @@ fn bench_low_prob_parameters(c: &mut Criterion) {
|
||||
}
|
||||
|
||||
fn bench_low_prob_parameters_ks_pbs(c: &mut Criterion) {
|
||||
benchs(
|
||||
benches(
|
||||
c,
|
||||
PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS,
|
||||
"PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS",
|
||||
@@ -107,5 +107,5 @@ fn bench_low_prob_parameters_ks_pbs(c: &mut Criterion) {
|
||||
}
|
||||
|
||||
fn bench_tfhe_lib_parameters(c: &mut Criterion) {
|
||||
benchs(c, TFHE_LIB_PARAMETERS, " TFHE_LIB_PARAMETERS");
|
||||
benches(c, TFHE_LIB_PARAMETERS, " TFHE_LIB_PARAMETERS");
|
||||
}
|
||||
|
||||
@@ -650,15 +650,23 @@ define_server_key_bench_fn!(method_name: smart_bitxor, display_name: bitxor);
|
||||
define_server_key_bench_fn!(method_name: smart_add_parallelized, display_name: add);
|
||||
define_server_key_bench_fn!(method_name: smart_sub_parallelized, display_name: sub);
|
||||
define_server_key_bench_fn!(method_name: smart_mul_parallelized, display_name: mul);
|
||||
define_server_key_bench_fn!(method_name: smart_div_parallelized, display_name: div);
|
||||
define_server_key_bench_fn!(method_name: smart_div_rem_parallelized, display_name: div_mod);
|
||||
define_server_key_bench_fn!(method_name: smart_rem_parallelized, display_name: rem);
|
||||
define_server_key_bench_fn!(method_name: smart_bitand_parallelized, display_name: bitand);
|
||||
define_server_key_bench_fn!(method_name: smart_bitxor_parallelized, display_name: bitxor);
|
||||
define_server_key_bench_fn!(method_name: smart_bitor_parallelized, display_name: bitor);
|
||||
define_server_key_bench_fn!(method_name: smart_rotate_right_parallelized, display_name: rotate_right);
|
||||
define_server_key_bench_fn!(method_name: smart_rotate_left_parallelized, display_name: rotate_left);
|
||||
define_server_key_bench_fn!(method_name: smart_right_shift_parallelized, display_name: right_shift);
|
||||
define_server_key_bench_fn!(method_name: smart_left_shift_parallelized, display_name: left_shift);
|
||||
|
||||
define_server_key_bench_default_fn!(method_name: add_parallelized, display_name: add);
|
||||
define_server_key_bench_default_fn!(method_name: sub_parallelized, display_name: sub);
|
||||
define_server_key_bench_default_fn!(method_name: mul_parallelized, display_name: mul);
|
||||
define_server_key_bench_default_fn!(method_name: div_parallelized, display_name: div);
|
||||
define_server_key_bench_default_fn!(method_name: rem_parallelized, display_name: modulo);
|
||||
define_server_key_bench_default_fn!(method_name: div_rem_parallelized, display_name: div_mod);
|
||||
define_server_key_bench_default_fn!(method_name: bitand_parallelized, display_name: bitand);
|
||||
define_server_key_bench_default_fn!(method_name: bitxor_parallelized, display_name: bitxor);
|
||||
define_server_key_bench_default_fn!(method_name: bitor_parallelized, display_name: bitor);
|
||||
@@ -671,7 +679,11 @@ define_server_key_bench_default_fn!(method_name: unchecked_bitand, display_name:
|
||||
define_server_key_bench_default_fn!(method_name: unchecked_bitor, display_name: bitor);
|
||||
define_server_key_bench_default_fn!(method_name: unchecked_bitxor, display_name: bitxor);
|
||||
|
||||
define_server_key_bench_default_fn!(method_name: unchecked_add_parallelized, display_name: add);
|
||||
define_server_key_bench_default_fn!(method_name: unchecked_mul_parallelized, display_name: mul);
|
||||
define_server_key_bench_default_fn!(method_name: unchecked_div_parallelized, display_name: div);
|
||||
define_server_key_bench_default_fn!(method_name: unchecked_rem_parallelized, display_name: modulo);
|
||||
define_server_key_bench_default_fn!(method_name: unchecked_div_rem_parallelized, display_name: div_mod);
|
||||
define_server_key_bench_default_fn!(
|
||||
method_name: unchecked_bitand_parallelized,
|
||||
display_name: bitand
|
||||
@@ -684,6 +696,38 @@ define_server_key_bench_default_fn!(
|
||||
method_name: unchecked_bitxor_parallelized,
|
||||
display_name: bitxor
|
||||
);
|
||||
define_server_key_bench_default_fn!(
|
||||
method_name: unchecked_rotate_right_parallelized,
|
||||
display_name: rotate_right
|
||||
);
|
||||
define_server_key_bench_default_fn!(
|
||||
method_name: unchecked_rotate_left_parallelized,
|
||||
display_name: rotate_left
|
||||
);
|
||||
define_server_key_bench_default_fn!(
|
||||
method_name: unchecked_right_shift_parallelized,
|
||||
display_name: right_shift
|
||||
);
|
||||
define_server_key_bench_default_fn!(
|
||||
method_name: unchecked_left_shift_parallelized,
|
||||
display_name: left_shift
|
||||
);
|
||||
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: unchecked_scalar_bitand_parallelized,
|
||||
display_name: bitand,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: unchecked_scalar_bitor_parallelized,
|
||||
display_name: bitor,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: unchecked_scalar_bitxor_parallelized,
|
||||
display_name: bitxor,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_scalar_fn!(
|
||||
method_name: smart_scalar_add,
|
||||
@@ -716,6 +760,46 @@ define_server_key_bench_scalar_fn!(
|
||||
display_name: mul,
|
||||
rng_func: mul_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_fn!(
|
||||
method_name: smart_scalar_div_parallelized,
|
||||
display_name: div,
|
||||
rng_func: div_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_fn!(
|
||||
method_name: smart_scalar_rem_parallelized,
|
||||
display_name: modulo,
|
||||
rng_func: div_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_fn!(
|
||||
method_name: smart_scalar_div_rem_parallelized,
|
||||
display_name: div_mod,
|
||||
rng_func: div_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_fn!(
|
||||
method_name: smart_scalar_bitand_parallelized,
|
||||
display_name: bitand,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_fn!(
|
||||
method_name: smart_scalar_bitor_parallelized,
|
||||
display_name: bitor,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_fn!(
|
||||
method_name: smart_scalar_bitxor_parallelized,
|
||||
display_name: bitxor,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_fn!(
|
||||
method_name: smart_scalar_rotate_left_parallelized,
|
||||
display_name: rotate_left,
|
||||
rng_func: shift_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_fn!(
|
||||
method_name: smart_scalar_rotate_right_parallelized,
|
||||
display_name: rotate_right,
|
||||
rng_func: shift_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: scalar_add_parallelized,
|
||||
@@ -742,6 +826,11 @@ define_server_key_bench_scalar_default_fn!(
|
||||
display_name: modulo,
|
||||
rng_func: div_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: scalar_div_rem_parallelized,
|
||||
display_name: div_mod,
|
||||
rng_func: div_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: scalar_left_shift_parallelized,
|
||||
display_name: left_shift,
|
||||
@@ -762,6 +851,22 @@ define_server_key_bench_scalar_default_fn!(
|
||||
display_name: rotate_right,
|
||||
rng_func: shift_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: scalar_bitand_parallelized,
|
||||
display_name: bitand,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: scalar_bitor_parallelized,
|
||||
display_name: bitor,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: scalar_bitxor_parallelized,
|
||||
display_name: bitxor,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: scalar_eq_parallelized,
|
||||
display_name: equal,
|
||||
@@ -803,6 +908,47 @@ define_server_key_bench_scalar_default_fn!(
|
||||
rng_func: default_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_scalar_fn!(
|
||||
method_name: smart_scalar_eq_parallelized,
|
||||
display_name: equal,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_fn!(
|
||||
method_name: smart_scalar_ne_parallelized,
|
||||
display_name: not_equal,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_fn!(
|
||||
method_name: smart_scalar_le_parallelized,
|
||||
display_name: less_or_equal,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_fn!(
|
||||
method_name: smart_scalar_lt_parallelized,
|
||||
display_name: less_than,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_fn!(
|
||||
method_name: smart_scalar_ge_parallelized,
|
||||
display_name: greater_or_equal,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_fn!(
|
||||
method_name: smart_scalar_gt_parallelized,
|
||||
display_name: greater_than,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_fn!(
|
||||
method_name: smart_scalar_max_parallelized,
|
||||
display_name: max,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_fn!(
|
||||
method_name: smart_scalar_min_parallelized,
|
||||
display_name: min,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: unchecked_scalar_add,
|
||||
display_name: add,
|
||||
@@ -818,10 +964,50 @@ define_server_key_bench_scalar_default_fn!(
|
||||
display_name: mul,
|
||||
rng_func: mul_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: unchecked_scalar_div_parallelized,
|
||||
display_name: div,
|
||||
rng_func: div_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: unchecked_scalar_rem_parallelized,
|
||||
display_name: modulo,
|
||||
rng_func: div_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: unchecked_scalar_div_rem_parallelized,
|
||||
display_name: div_mod,
|
||||
rng_func: div_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: unchecked_scalar_rotate_right_parallelized,
|
||||
display_name: rotate_right,
|
||||
rng_func: shift_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: unchecked_scalar_rotate_left_parallelized,
|
||||
display_name: rotate_left,
|
||||
rng_func: shift_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: unchecked_scalar_right_shift_parallelized,
|
||||
display_name: right_shift,
|
||||
rng_func: shift_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: unchecked_scalar_left_shift_parallelized,
|
||||
display_name: left_shift,
|
||||
rng_func: shift_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_unary_fn!(method_name: smart_neg, display_name: negation);
|
||||
define_server_key_bench_unary_fn!(method_name: smart_neg_parallelized, display_name: negation);
|
||||
define_server_key_bench_unary_fn!(method_name: smart_abs_parallelized, display_name: abs);
|
||||
|
||||
define_server_key_bench_unary_default_fn!(method_name: neg_parallelized, display_name: negation);
|
||||
define_server_key_bench_unary_default_fn!(method_name: abs_parallelized, display_name: abs);
|
||||
|
||||
define_server_key_bench_unary_default_fn!(method_name: unchecked_abs_parallelized, display_name: abs);
|
||||
|
||||
define_server_key_bench_unary_fn!(method_name: full_propagate, display_name: carry_propagation);
|
||||
define_server_key_bench_unary_fn!(
|
||||
@@ -832,6 +1018,7 @@ define_server_key_bench_unary_fn!(
|
||||
define_server_key_bench_default_fn!(method_name: unchecked_max, display_name: max);
|
||||
define_server_key_bench_default_fn!(method_name: unchecked_min, display_name: min);
|
||||
define_server_key_bench_default_fn!(method_name: unchecked_eq, display_name: equal);
|
||||
define_server_key_bench_default_fn!(method_name: unchecked_ne, display_name: not_equal);
|
||||
define_server_key_bench_default_fn!(method_name: unchecked_lt, display_name: less_than);
|
||||
define_server_key_bench_default_fn!(method_name: unchecked_le, display_name: less_or_equal);
|
||||
define_server_key_bench_default_fn!(method_name: unchecked_gt, display_name: greater_than);
|
||||
@@ -840,6 +1027,7 @@ define_server_key_bench_default_fn!(method_name: unchecked_ge, display_name: gre
|
||||
define_server_key_bench_default_fn!(method_name: unchecked_max_parallelized, display_name: max);
|
||||
define_server_key_bench_default_fn!(method_name: unchecked_min_parallelized, display_name: min);
|
||||
define_server_key_bench_default_fn!(method_name: unchecked_eq_parallelized, display_name: equal);
|
||||
define_server_key_bench_default_fn!(method_name: unchecked_ne_parallelized, display_name: not_equal);
|
||||
define_server_key_bench_default_fn!(
|
||||
method_name: unchecked_lt_parallelized,
|
||||
display_name: less_than
|
||||
@@ -857,9 +1045,35 @@ define_server_key_bench_default_fn!(
|
||||
display_name: greater_or_equal
|
||||
);
|
||||
|
||||
define_server_key_bench_scalar_default_fn!(method_name: unchecked_scalar_max_parallelized, display_name: max,rng_func: default_scalar);
|
||||
define_server_key_bench_scalar_default_fn!(method_name: unchecked_scalar_min_parallelized, display_name: min,rng_func: default_scalar);
|
||||
define_server_key_bench_scalar_default_fn!(method_name: unchecked_scalar_eq_parallelized, display_name: equal,rng_func: default_scalar);
|
||||
define_server_key_bench_scalar_default_fn!(method_name: unchecked_scalar_ne_parallelized, display_name: not_equal,rng_func: default_scalar);
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: unchecked_scalar_lt_parallelized,
|
||||
display_name: less_than,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: unchecked_scalar_le_parallelized,
|
||||
display_name: less_or_equal,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: unchecked_scalar_gt_parallelized,
|
||||
display_name: greater_than,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
define_server_key_bench_scalar_default_fn!(
|
||||
method_name: unchecked_scalar_ge_parallelized,
|
||||
display_name: greater_or_equal,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_fn!(method_name: smart_max, display_name: max);
|
||||
define_server_key_bench_fn!(method_name: smart_min, display_name: min);
|
||||
define_server_key_bench_fn!(method_name: smart_eq, display_name: equal);
|
||||
define_server_key_bench_fn!(method_name: smart_ne, display_name: not_equal);
|
||||
define_server_key_bench_fn!(method_name: smart_lt, display_name: less_than);
|
||||
define_server_key_bench_fn!(method_name: smart_le, display_name: less_or_equal);
|
||||
define_server_key_bench_fn!(method_name: smart_gt, display_name: greater_than);
|
||||
@@ -868,6 +1082,7 @@ define_server_key_bench_fn!(method_name: smart_ge, display_name: greater_or_equa
|
||||
define_server_key_bench_fn!(method_name: smart_max_parallelized, display_name: max);
|
||||
define_server_key_bench_fn!(method_name: smart_min_parallelized, display_name: min);
|
||||
define_server_key_bench_fn!(method_name: smart_eq_parallelized, display_name: equal);
|
||||
define_server_key_bench_fn!(method_name: smart_ne_parallelized, display_name: not_equal);
|
||||
define_server_key_bench_fn!(method_name: smart_lt_parallelized, display_name: less_than);
|
||||
define_server_key_bench_fn!(
|
||||
method_name: smart_le_parallelized,
|
||||
@@ -923,6 +1138,7 @@ criterion_group!(
|
||||
smart_max,
|
||||
smart_min,
|
||||
smart_eq,
|
||||
smart_ne,
|
||||
smart_lt,
|
||||
smart_le,
|
||||
smart_gt,
|
||||
@@ -931,12 +1147,21 @@ criterion_group!(
|
||||
|
||||
criterion_group!(
|
||||
smart_parallelized_ops,
|
||||
smart_neg_parallelized,
|
||||
smart_abs_parallelized,
|
||||
smart_add_parallelized,
|
||||
smart_sub_parallelized,
|
||||
smart_mul_parallelized,
|
||||
// smart_div_parallelized,
|
||||
// smart_rem_parallelized,
|
||||
smart_div_rem_parallelized, // For ciphertext div == rem == div_rem
|
||||
smart_bitand_parallelized,
|
||||
smart_bitor_parallelized,
|
||||
smart_bitxor_parallelized,
|
||||
smart_rotate_right_parallelized,
|
||||
smart_rotate_left_parallelized,
|
||||
smart_right_shift_parallelized,
|
||||
smart_left_shift_parallelized,
|
||||
);
|
||||
|
||||
criterion_group!(
|
||||
@@ -944,6 +1169,7 @@ criterion_group!(
|
||||
smart_max_parallelized,
|
||||
smart_min_parallelized,
|
||||
smart_eq_parallelized,
|
||||
smart_ne_parallelized,
|
||||
smart_lt_parallelized,
|
||||
smart_le_parallelized,
|
||||
smart_gt_parallelized,
|
||||
@@ -952,12 +1178,14 @@ criterion_group!(
|
||||
|
||||
criterion_group!(
|
||||
default_parallelized_ops,
|
||||
neg_parallelized,
|
||||
abs_parallelized,
|
||||
add_parallelized,
|
||||
sub_parallelized,
|
||||
mul_parallelized,
|
||||
div_parallelized,
|
||||
rem_parallelized,
|
||||
neg_parallelized,
|
||||
// div_parallelized,
|
||||
// rem_parallelized,
|
||||
div_rem_parallelized,
|
||||
bitand_parallelized,
|
||||
bitnot_parallelized,
|
||||
bitor_parallelized,
|
||||
@@ -993,6 +1221,26 @@ criterion_group!(
|
||||
smart_scalar_add_parallelized,
|
||||
smart_scalar_sub_parallelized,
|
||||
smart_scalar_mul_parallelized,
|
||||
smart_scalar_div_parallelized,
|
||||
smart_scalar_rem_parallelized, // For scalar rem == div_rem
|
||||
// smart_scalar_div_rem_parallelized,
|
||||
smart_scalar_bitand_parallelized,
|
||||
smart_scalar_bitor_parallelized,
|
||||
smart_scalar_bitxor_parallelized,
|
||||
smart_scalar_rotate_right_parallelized,
|
||||
smart_scalar_rotate_left_parallelized,
|
||||
);
|
||||
|
||||
criterion_group!(
|
||||
smart_scalar_parallelized_ops_comp,
|
||||
smart_scalar_max_parallelized,
|
||||
smart_scalar_min_parallelized,
|
||||
smart_scalar_eq_parallelized,
|
||||
smart_scalar_ne_parallelized,
|
||||
smart_scalar_lt_parallelized,
|
||||
smart_scalar_le_parallelized,
|
||||
smart_scalar_gt_parallelized,
|
||||
smart_scalar_ge_parallelized,
|
||||
);
|
||||
|
||||
criterion_group!(
|
||||
@@ -1002,10 +1250,14 @@ criterion_group!(
|
||||
scalar_mul_parallelized,
|
||||
scalar_div_parallelized,
|
||||
scalar_rem_parallelized,
|
||||
// scalar_div_rem_parallelized,
|
||||
scalar_left_shift_parallelized,
|
||||
scalar_right_shift_parallelized,
|
||||
scalar_rotate_left_parallelized,
|
||||
scalar_rotate_right_parallelized,
|
||||
scalar_bitand_parallelized,
|
||||
scalar_bitor_parallelized,
|
||||
scalar_bitxor_parallelized,
|
||||
);
|
||||
|
||||
criterion_group!(
|
||||
@@ -1030,11 +1282,40 @@ criterion_group!(
|
||||
unchecked_bitxor,
|
||||
);
|
||||
|
||||
criterion_group!(
|
||||
unchecked_parallelized_ops,
|
||||
unchecked_abs_parallelized,
|
||||
unchecked_add_parallelized,
|
||||
unchecked_mul_parallelized,
|
||||
// unchecked_div_parallelized,
|
||||
// unchecked_rem_parallelized,
|
||||
unchecked_div_rem_parallelized,
|
||||
unchecked_bitand_parallelized,
|
||||
unchecked_bitor_parallelized,
|
||||
unchecked_bitxor_parallelized,
|
||||
unchecked_rotate_right_parallelized,
|
||||
unchecked_rotate_left_parallelized,
|
||||
unchecked_right_shift_parallelized,
|
||||
unchecked_left_shift_parallelized,
|
||||
);
|
||||
|
||||
criterion_group!(
|
||||
unchecked_parallelized_ops_comp,
|
||||
unchecked_eq_parallelized,
|
||||
unchecked_ne_parallelized,
|
||||
unchecked_gt_parallelized,
|
||||
unchecked_ge_parallelized,
|
||||
unchecked_lt_parallelized,
|
||||
unchecked_max_parallelized,
|
||||
unchecked_min_parallelized,
|
||||
);
|
||||
|
||||
criterion_group!(
|
||||
unchecked_ops_comp,
|
||||
unchecked_max,
|
||||
unchecked_min,
|
||||
unchecked_eq,
|
||||
unchecked_ne,
|
||||
unchecked_lt,
|
||||
unchecked_le,
|
||||
unchecked_gt,
|
||||
@@ -1046,20 +1327,28 @@ criterion_group!(
|
||||
unchecked_scalar_add,
|
||||
unchecked_scalar_sub,
|
||||
unchecked_scalar_mul_parallelized,
|
||||
unchecked_bitand_parallelized,
|
||||
unchecked_bitor_parallelized,
|
||||
unchecked_bitxor_parallelized,
|
||||
unchecked_scalar_div_parallelized,
|
||||
unchecked_scalar_rem_parallelized,
|
||||
// unchecked_scalar_div_rem_parallelized,
|
||||
unchecked_scalar_bitand_parallelized,
|
||||
unchecked_scalar_bitor_parallelized,
|
||||
unchecked_scalar_bitxor_parallelized,
|
||||
unchecked_scalar_rotate_right_parallelized,
|
||||
unchecked_scalar_rotate_left_parallelized,
|
||||
unchecked_scalar_right_shift_parallelized,
|
||||
unchecked_scalar_left_shift_parallelized,
|
||||
);
|
||||
|
||||
criterion_group!(
|
||||
unchecked_scalar_ops_comp,
|
||||
unchecked_max_parallelized,
|
||||
unchecked_min_parallelized,
|
||||
unchecked_eq_parallelized,
|
||||
unchecked_lt_parallelized,
|
||||
unchecked_le_parallelized,
|
||||
unchecked_gt_parallelized,
|
||||
unchecked_ge_parallelized,
|
||||
unchecked_scalar_max_parallelized,
|
||||
unchecked_scalar_min_parallelized,
|
||||
unchecked_scalar_eq_parallelized,
|
||||
unchecked_scalar_ne_parallelized,
|
||||
unchecked_scalar_lt_parallelized,
|
||||
unchecked_scalar_le_parallelized,
|
||||
unchecked_scalar_gt_parallelized,
|
||||
unchecked_scalar_ge_parallelized,
|
||||
);
|
||||
|
||||
criterion_group!(misc, full_propagate, full_propagate_parallelized);
|
||||
@@ -1078,7 +1367,9 @@ fn main() {
|
||||
"smart_parallelized" => smart_parallelized_ops(),
|
||||
"smart_parallelized_comp" => smart_parallelized_ops_comp(),
|
||||
"smart_scalar_parallelized" => smart_scalar_parallelized_ops(),
|
||||
"smart_scalar_parallelized_comp" => smart_scalar_parallelized_ops_comp(),
|
||||
"unchecked" => unchecked_ops(),
|
||||
"unchecked_parallelized" => unchecked_parallelized_ops(),
|
||||
"unchecked_comp" => unchecked_ops_comp(),
|
||||
"unchecked_scalar" => unchecked_scalar_ops(),
|
||||
"unchecked_scalar_comp" => unchecked_scalar_ops_comp(),
|
||||
|
||||
811
tfhe/benches/integer/signed_bench.rs
Normal file
811
tfhe/benches/integer/signed_bench.rs
Normal file
@@ -0,0 +1,811 @@
|
||||
#[path = "../utilities.rs"]
|
||||
mod utilities;
|
||||
|
||||
use crate::utilities::{write_to_json, OperatorType};
|
||||
use std::env;
|
||||
|
||||
use criterion::{criterion_group, Criterion};
|
||||
use itertools::iproduct;
|
||||
use rand::prelude::*;
|
||||
use rand::Rng;
|
||||
use std::vec::IntoIter;
|
||||
use tfhe::integer::keycache::KEY_CACHE;
|
||||
use tfhe::integer::{RadixCiphertext, ServerKey, SignedRadixCiphertext, I256};
|
||||
use tfhe::keycache::NamedParam;
|
||||
|
||||
use tfhe::shortint::parameters::{
|
||||
PARAM_MESSAGE_2_CARRY_2_KS_PBS, PARAM_MULTI_BIT_MESSAGE_2_CARRY_2_GROUP_2_KS_PBS,
|
||||
};
|
||||
|
||||
fn gen_random_i256(rng: &mut ThreadRng) -> I256 {
|
||||
let clearlow = rng.gen::<u128>();
|
||||
let clearhigh = rng.gen::<u128>();
|
||||
|
||||
tfhe::integer::I256::from((clearlow, clearhigh))
|
||||
}
|
||||
|
||||
/// An iterator that yields a succession of combinations
|
||||
/// of parameters and a num_block to achieve a certain bit_size ciphertext
|
||||
/// in radix decomposition
|
||||
struct ParamsAndNumBlocksIter {
|
||||
params_and_bit_sizes:
|
||||
itertools::Product<IntoIter<tfhe::shortint::PBSParameters>, IntoIter<usize>>,
|
||||
}
|
||||
|
||||
impl Default for ParamsAndNumBlocksIter {
|
||||
fn default() -> Self {
|
||||
let is_multi_bit = match env::var("__TFHE_RS_BENCH_TYPE") {
|
||||
Ok(val) => val.to_lowercase() == "multi_bit",
|
||||
Err(_) => false,
|
||||
};
|
||||
|
||||
let is_fast_bench = match env::var("__TFHE_RS_FAST_BENCH") {
|
||||
Ok(val) => val.to_lowercase() == "true",
|
||||
Err(_) => false,
|
||||
};
|
||||
|
||||
if is_multi_bit {
|
||||
let params = vec![PARAM_MULTI_BIT_MESSAGE_2_CARRY_2_GROUP_2_KS_PBS.into()];
|
||||
|
||||
let bit_sizes = if is_fast_bench {
|
||||
vec![32]
|
||||
} else {
|
||||
vec![8, 16, 32, 40, 64]
|
||||
};
|
||||
|
||||
let params_and_bit_sizes = iproduct!(params, bit_sizes);
|
||||
Self {
|
||||
params_and_bit_sizes,
|
||||
}
|
||||
} else {
|
||||
// FIXME One set of parameter is tested since we want to benchmark only quickest
|
||||
// operations.
|
||||
let params = vec![
|
||||
PARAM_MESSAGE_2_CARRY_2_KS_PBS.into(),
|
||||
// PARAM_MESSAGE_3_CARRY_3_KS_PBS.into(),
|
||||
// PARAM_MESSAGE_4_CARRY_4_KS_PBS.into(),
|
||||
];
|
||||
|
||||
let bit_sizes = if is_fast_bench {
|
||||
vec![32]
|
||||
} else {
|
||||
vec![8, 16, 32, 40, 64, 128, 256]
|
||||
};
|
||||
|
||||
let params_and_bit_sizes = iproduct!(params, bit_sizes);
|
||||
Self {
|
||||
params_and_bit_sizes,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for ParamsAndNumBlocksIter {
|
||||
type Item = (tfhe::shortint::PBSParameters, usize, usize);
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let (param, bit_size) = self.params_and_bit_sizes.next()?;
|
||||
let num_block =
|
||||
(bit_size as f64 / param.message_modulus().0.ilog2() as f64).ceil() as usize;
|
||||
|
||||
Some((param, num_block, bit_size))
|
||||
}
|
||||
}
|
||||
|
||||
/// Base function to bench a server key function that is a binary operation, input ciphertext will
|
||||
/// contain only zero carries
|
||||
fn bench_server_key_signed_binary_function_clean_inputs<F>(
|
||||
c: &mut Criterion,
|
||||
bench_name: &str,
|
||||
display_name: &str,
|
||||
binary_op: F,
|
||||
sample_size: usize,
|
||||
) where
|
||||
F: Fn(&ServerKey, &SignedRadixCiphertext, &SignedRadixCiphertext),
|
||||
{
|
||||
let mut bench_group = c.benchmark_group(bench_name);
|
||||
bench_group
|
||||
.sample_size(sample_size)
|
||||
.measurement_time(std::time::Duration::from_secs(60));
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
for (param, num_block, bit_size) in ParamsAndNumBlocksIter::default() {
|
||||
let param_name = param.name();
|
||||
|
||||
let bench_id = format!("{bench_name}::{param_name}::{bit_size}_bits");
|
||||
bench_group.bench_function(&bench_id, |b| {
|
||||
let (cks, sks) = KEY_CACHE.get_from_params(param);
|
||||
|
||||
let encrypt_two_values = || {
|
||||
let ct_0 = cks.encrypt_signed_radix(gen_random_i256(&mut rng), num_block);
|
||||
let ct_1 = cks.encrypt_signed_radix(gen_random_i256(&mut rng), num_block);
|
||||
|
||||
(ct_0, ct_1)
|
||||
};
|
||||
|
||||
b.iter_batched(
|
||||
encrypt_two_values,
|
||||
|(ct_0, ct_1)| {
|
||||
binary_op(&sks, &ct_0, &ct_1);
|
||||
},
|
||||
criterion::BatchSize::SmallInput,
|
||||
)
|
||||
});
|
||||
|
||||
write_to_json::<u64, _>(
|
||||
&bench_id,
|
||||
param,
|
||||
param.name(),
|
||||
display_name,
|
||||
&OperatorType::Atomic,
|
||||
bit_size as u32,
|
||||
vec![param.message_modulus().0.ilog2(); num_block],
|
||||
);
|
||||
}
|
||||
|
||||
bench_group.finish()
|
||||
}
|
||||
|
||||
/// Shifts and rotations require a special function as the rhs,
|
||||
/// i.e. the shift amount has to be a positive radix type.
|
||||
fn bench_server_key_signed_shift_function_clean_inputs<F>(
|
||||
c: &mut Criterion,
|
||||
bench_name: &str,
|
||||
display_name: &str,
|
||||
binary_op: F,
|
||||
) where
|
||||
F: Fn(&ServerKey, &SignedRadixCiphertext, &RadixCiphertext),
|
||||
{
|
||||
let mut bench_group = c.benchmark_group(bench_name);
|
||||
bench_group
|
||||
.sample_size(15)
|
||||
.measurement_time(std::time::Duration::from_secs(60));
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
for (param, num_block, bit_size) in ParamsAndNumBlocksIter::default() {
|
||||
let param_name = param.name();
|
||||
|
||||
let bench_id = format!("{bench_name}::{param_name}::{bit_size}_bits");
|
||||
bench_group.bench_function(&bench_id, |b| {
|
||||
let (cks, sks) = KEY_CACHE.get_from_params(param);
|
||||
|
||||
let encrypt_two_values = || {
|
||||
let clear_1 = rng.gen_range(0u128..bit_size as u128);
|
||||
|
||||
let ct_0 = cks.encrypt_signed_radix(gen_random_i256(&mut rng), num_block);
|
||||
let ct_1 = cks.encrypt_radix(clear_1, num_block);
|
||||
|
||||
(ct_0, ct_1)
|
||||
};
|
||||
|
||||
b.iter_batched(
|
||||
encrypt_two_values,
|
||||
|(ct_0, ct_1)| {
|
||||
binary_op(&sks, &ct_0, &ct_1);
|
||||
},
|
||||
criterion::BatchSize::SmallInput,
|
||||
)
|
||||
});
|
||||
|
||||
write_to_json::<u64, _>(
|
||||
&bench_id,
|
||||
param,
|
||||
param.name(),
|
||||
display_name,
|
||||
&OperatorType::Atomic,
|
||||
bit_size as u32,
|
||||
vec![param.message_modulus().0.ilog2(); num_block],
|
||||
);
|
||||
}
|
||||
|
||||
bench_group.finish()
|
||||
}
|
||||
|
||||
/// Base function to bench a server key function that is a unary operation, input ciphertext will
|
||||
/// contain only zero carries
|
||||
fn bench_server_key_unary_function_clean_inputs<F>(
|
||||
c: &mut Criterion,
|
||||
bench_name: &str,
|
||||
display_name: &str,
|
||||
unary_fn: F,
|
||||
) where
|
||||
F: Fn(&ServerKey, &SignedRadixCiphertext),
|
||||
{
|
||||
let mut bench_group = c.benchmark_group(bench_name);
|
||||
bench_group
|
||||
.sample_size(15)
|
||||
.measurement_time(std::time::Duration::from_secs(60));
|
||||
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
for (param, num_block, bit_size) in ParamsAndNumBlocksIter::default() {
|
||||
let param_name = param.name();
|
||||
|
||||
let bench_id = format!("{bench_name}::{param_name}::{bit_size}_bits");
|
||||
bench_group.bench_function(&bench_id, |b| {
|
||||
let (cks, sks) = KEY_CACHE.get_from_params(param);
|
||||
|
||||
let encrypt_one_value =
|
||||
|| cks.encrypt_signed_radix(gen_random_i256(&mut rng), num_block);
|
||||
|
||||
b.iter_batched(
|
||||
encrypt_one_value,
|
||||
|ct_0| {
|
||||
unary_fn(&sks, &ct_0);
|
||||
},
|
||||
criterion::BatchSize::SmallInput,
|
||||
)
|
||||
});
|
||||
|
||||
write_to_json::<u64, _>(
|
||||
&bench_id,
|
||||
param,
|
||||
param.name(),
|
||||
display_name,
|
||||
&OperatorType::Atomic,
|
||||
bit_size as u32,
|
||||
vec![param.message_modulus().0.ilog2(); num_block],
|
||||
);
|
||||
}
|
||||
|
||||
bench_group.finish()
|
||||
}
|
||||
|
||||
fn signed_if_then_else_parallelized(c: &mut Criterion) {
|
||||
let bench_name = "integer::signed::if_then_else_parallelized";
|
||||
let display_name = "if_then_else";
|
||||
|
||||
let mut bench_group = c.benchmark_group(bench_name);
|
||||
bench_group
|
||||
.sample_size(15)
|
||||
.measurement_time(std::time::Duration::from_secs(60));
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
for (param, num_block, bit_size) in ParamsAndNumBlocksIter::default() {
|
||||
let param_name = param.name();
|
||||
|
||||
let bench_id = format!("{bench_name}::{param_name}::{bit_size}_bits");
|
||||
bench_group.bench_function(&bench_id, |b| {
|
||||
let (cks, sks) = KEY_CACHE.get_from_params(param);
|
||||
|
||||
let encrypt_tree_values = || {
|
||||
let ct_0 = cks.encrypt_signed_radix(gen_random_i256(&mut rng), num_block);
|
||||
let ct_1 = cks.encrypt_signed_radix(gen_random_i256(&mut rng), num_block);
|
||||
|
||||
let cond = sks.create_trivial_radix(rng.gen_bool(0.5) as u64, num_block);
|
||||
|
||||
(cond, ct_0, ct_1)
|
||||
};
|
||||
|
||||
b.iter_batched(
|
||||
encrypt_tree_values,
|
||||
|(condition, true_ct, false_ct)| {
|
||||
sks.if_then_else_parallelized(&condition, &true_ct, &false_ct)
|
||||
},
|
||||
criterion::BatchSize::SmallInput,
|
||||
)
|
||||
});
|
||||
|
||||
write_to_json::<u64, _>(
|
||||
&bench_id,
|
||||
param,
|
||||
param.name(),
|
||||
display_name,
|
||||
&OperatorType::Atomic,
|
||||
bit_size as u32,
|
||||
vec![param.message_modulus().0.ilog2(); num_block],
|
||||
);
|
||||
}
|
||||
|
||||
bench_group.finish()
|
||||
}
|
||||
|
||||
macro_rules! define_server_key_bench_binary_signed_clean_inputs_fn (
|
||||
(method_name: $server_key_method:ident, display_name:$name:ident $(,)?) => {
|
||||
fn $server_key_method(c: &mut Criterion) {
|
||||
bench_server_key_signed_binary_function_clean_inputs(
|
||||
c,
|
||||
concat!("integer::signed::", stringify!($server_key_method)),
|
||||
stringify!($name),
|
||||
|server_key, lhs, rhs| {
|
||||
server_key.$server_key_method(lhs, rhs);
|
||||
},
|
||||
15 /* sample_size */
|
||||
)
|
||||
}
|
||||
};
|
||||
(
|
||||
method_name: $server_key_method:ident,
|
||||
display_name:$name:ident,
|
||||
sample_size: $sample_size:expr $(,)?
|
||||
) => {
|
||||
fn $server_key_method(c: &mut Criterion) {
|
||||
bench_server_key_signed_binary_function_clean_inputs(
|
||||
c,
|
||||
concat!("integer::signed::", stringify!($server_key_method)),
|
||||
stringify!($name),
|
||||
|server_key, lhs, rhs| {
|
||||
server_key.$server_key_method(lhs, rhs);
|
||||
},
|
||||
$sample_size
|
||||
)
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
macro_rules! define_server_key_bench_unary_signed_clean_input_fn (
|
||||
(method_name: $server_key_method:ident, display_name:$name:ident $(,)?) => {
|
||||
fn $server_key_method(c: &mut Criterion) {
|
||||
bench_server_key_unary_function_clean_inputs(
|
||||
c,
|
||||
concat!("integer::signed::", stringify!($server_key_method)),
|
||||
stringify!($name),
|
||||
|server_key, lhs| {
|
||||
server_key.$server_key_method(lhs);
|
||||
},
|
||||
)
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_signed_clean_inputs_fn!(
|
||||
method_name: unchecked_mul_parallelized,
|
||||
display_name: mul
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_signed_clean_inputs_fn!(
|
||||
method_name: unchecked_bitand_parallelized,
|
||||
display_name: bitand
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_signed_clean_inputs_fn!(
|
||||
method_name: unchecked_bitor_parallelized,
|
||||
display_name: bitand
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_signed_clean_inputs_fn!(
|
||||
method_name: unchecked_bitxor_parallelized,
|
||||
display_name: bitand
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_signed_clean_inputs_fn!(
|
||||
method_name: unchecked_eq_parallelized,
|
||||
display_name: eq
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_signed_clean_inputs_fn!(
|
||||
method_name: unchecked_ne_parallelized,
|
||||
display_name: ne
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_signed_clean_inputs_fn!(
|
||||
method_name: unchecked_le_parallelized,
|
||||
display_name: le
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_signed_clean_inputs_fn!(
|
||||
method_name: unchecked_lt_parallelized,
|
||||
display_name: lt
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_signed_clean_inputs_fn!(
|
||||
method_name: unchecked_ge_parallelized,
|
||||
display_name: ge
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_signed_clean_inputs_fn!(
|
||||
method_name: unchecked_gt_parallelized,
|
||||
display_name: gt
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_signed_clean_inputs_fn!(
|
||||
method_name: unchecked_max_parallelized,
|
||||
display_name: max
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_signed_clean_inputs_fn!(
|
||||
method_name: unchecked_min_parallelized,
|
||||
display_name: min
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_signed_clean_inputs_fn!(
|
||||
method_name: unchecked_div_rem_parallelized,
|
||||
display_name: div_rem,
|
||||
sample_size: 10,
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_signed_clean_inputs_fn!(
|
||||
method_name: unchecked_div_rem_floor_parallelized,
|
||||
display_name: div_rem_floor,
|
||||
sample_size: 10,
|
||||
);
|
||||
|
||||
fn unchecked_left_shift_parallelized(c: &mut Criterion) {
|
||||
bench_server_key_signed_shift_function_clean_inputs(
|
||||
c,
|
||||
concat!("integer::signed::", "unchecked_left_shift_parallelized"),
|
||||
"left_shift",
|
||||
|server_key, lhs, rhs| {
|
||||
server_key.unchecked_left_shift_parallelized(lhs, rhs);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn unchecked_right_shift_parallelized(c: &mut Criterion) {
|
||||
bench_server_key_signed_shift_function_clean_inputs(
|
||||
c,
|
||||
concat!("integer::signed::", "unchecked_right_shift_parallelized"),
|
||||
"right_shift",
|
||||
|server_key, lhs, rhs| {
|
||||
server_key.unchecked_right_shift_parallelized(lhs, rhs);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn unchecked_rotate_left_parallelized(c: &mut Criterion) {
|
||||
bench_server_key_signed_shift_function_clean_inputs(
|
||||
c,
|
||||
concat!("integer::signed::", "unchecked_rotate_left_parallelized"),
|
||||
"rotate_left",
|
||||
|server_key, lhs, rhs| {
|
||||
server_key.unchecked_rotate_left_parallelized(lhs, rhs);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn unchecked_rotate_right_parallelized(c: &mut Criterion) {
|
||||
bench_server_key_signed_shift_function_clean_inputs(
|
||||
c,
|
||||
concat!("integer::signed::", "unchecked_rotate_right_parallelized"),
|
||||
"rotate_right",
|
||||
|server_key, lhs, rhs| {
|
||||
server_key.unchecked_rotate_right_parallelized(lhs, rhs);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
define_server_key_bench_unary_signed_clean_input_fn!(
|
||||
method_name: unchecked_abs_parallelized,
|
||||
display_name: abs,
|
||||
);
|
||||
|
||||
criterion_group!(
|
||||
unchecked_ops,
|
||||
unchecked_mul_parallelized,
|
||||
unchecked_left_shift_parallelized,
|
||||
unchecked_right_shift_parallelized,
|
||||
unchecked_rotate_left_parallelized,
|
||||
unchecked_rotate_right_parallelized,
|
||||
unchecked_bitand_parallelized,
|
||||
unchecked_bitor_parallelized,
|
||||
unchecked_bitxor_parallelized,
|
||||
unchecked_abs_parallelized,
|
||||
unchecked_div_rem_parallelized,
|
||||
unchecked_div_rem_floor_parallelized,
|
||||
);
|
||||
|
||||
criterion_group!(
|
||||
unchecked_ops_comp,
|
||||
unchecked_eq_parallelized,
|
||||
unchecked_ne_parallelized,
|
||||
unchecked_ge_parallelized,
|
||||
unchecked_gt_parallelized,
|
||||
unchecked_le_parallelized,
|
||||
unchecked_lt_parallelized,
|
||||
unchecked_max_parallelized,
|
||||
unchecked_min_parallelized,
|
||||
);
|
||||
|
||||
//================================================================================
|
||||
// Scalar Benches
|
||||
//================================================================================
|
||||
|
||||
type ScalarType = I256;
|
||||
|
||||
fn bench_server_key_binary_scalar_function_clean_inputs<F, G>(
|
||||
c: &mut Criterion,
|
||||
bench_name: &str,
|
||||
display_name: &str,
|
||||
binary_op: F,
|
||||
rng_func: G,
|
||||
) where
|
||||
F: Fn(&ServerKey, &mut SignedRadixCiphertext, ScalarType),
|
||||
G: Fn(&mut ThreadRng, usize) -> ScalarType,
|
||||
{
|
||||
let mut bench_group = c.benchmark_group(bench_name);
|
||||
bench_group
|
||||
.sample_size(15)
|
||||
.measurement_time(std::time::Duration::from_secs(60));
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
for (param, num_block, bit_size) in ParamsAndNumBlocksIter::default() {
|
||||
if bit_size > ScalarType::BITS as usize {
|
||||
break;
|
||||
}
|
||||
let param_name = param.name();
|
||||
|
||||
let range = range_for_signed_bit_size(bit_size);
|
||||
|
||||
let bench_id = format!("{bench_name}::{param_name}::{bit_size}_bits_scalar_{bit_size}");
|
||||
bench_group.bench_function(&bench_id, |b| {
|
||||
let (cks, sks) = KEY_CACHE.get_from_params(param);
|
||||
|
||||
let encrypt_one_value = || {
|
||||
let ct_0 = cks.encrypt_signed_radix(gen_random_i256(&mut rng), num_block);
|
||||
|
||||
let clear_1 = rng_func(&mut rng, bit_size);
|
||||
assert!(
|
||||
range.contains(&clear_1),
|
||||
"{:?} is not within the range {:?}",
|
||||
clear_1,
|
||||
range
|
||||
);
|
||||
|
||||
(ct_0, clear_1)
|
||||
};
|
||||
|
||||
b.iter_batched(
|
||||
encrypt_one_value,
|
||||
|(mut ct_0, clear_1)| {
|
||||
binary_op(&sks, &mut ct_0, clear_1);
|
||||
},
|
||||
criterion::BatchSize::SmallInput,
|
||||
)
|
||||
});
|
||||
|
||||
write_to_json::<u64, _>(
|
||||
&bench_id,
|
||||
param,
|
||||
param.name(),
|
||||
display_name,
|
||||
&OperatorType::Atomic,
|
||||
bit_size as u32,
|
||||
vec![param.message_modulus().0.ilog2(); num_block],
|
||||
);
|
||||
}
|
||||
|
||||
bench_group.finish()
|
||||
}
|
||||
|
||||
fn range_for_signed_bit_size(bit_size: usize) -> std::ops::RangeInclusive<ScalarType> {
|
||||
assert!(bit_size <= ScalarType::BITS as usize);
|
||||
assert!(bit_size > 0);
|
||||
let modulus = ScalarType::ONE << (bit_size - 1);
|
||||
// if clear_bit_size == ScalarType::BITS then modulus==T::MIN
|
||||
// -T::MIN = -T::MIN so we sill have our correct lower value
|
||||
// (in two's complement which rust uses)
|
||||
let lowest = modulus.wrapping_neg();
|
||||
// if clear_bit_size == 128 then modulus==T::MIN
|
||||
// T::MIN - 1 = T::MAX (in two's complement which rust uses)
|
||||
let highest = modulus.wrapping_sub(ScalarType::ONE);
|
||||
|
||||
lowest..=highest
|
||||
}
|
||||
|
||||
/// Creates a bitmask where bit_size bits are 1s, rest are 0s
|
||||
/// Only works if ScalarType in signed
|
||||
fn positive_bit_mask_for_bit_size(bit_size: usize) -> ScalarType {
|
||||
assert!(bit_size <= ScalarType::BITS as usize);
|
||||
assert!(bit_size > 0);
|
||||
let minus_one = -ScalarType::ONE; // (In two's complement this is full of 1s)
|
||||
// The last bit of bit_size can only be set for when value is positive
|
||||
let bitmask = (minus_one) >> (ScalarType::BITS as usize - bit_size - 1);
|
||||
// flib msb as they would still be one due to '>>' being arithmetic shift
|
||||
bitmask ^ ((minus_one) << (bit_size - 1))
|
||||
}
|
||||
|
||||
fn negative_bit_mask_for_bit_size(bit_size: usize) -> ScalarType {
|
||||
assert!(bit_size <= ScalarType::BITS as usize);
|
||||
assert!(bit_size > 0);
|
||||
let minus_one = -ScalarType::ONE; // (In two's complement this is full of 1s)
|
||||
let bitmask = (minus_one) >> (ScalarType::BITS as usize - bit_size);
|
||||
// flib msb as they would still be one due to '>>' being arithmetic shift
|
||||
bitmask ^ ((minus_one) << bit_size)
|
||||
}
|
||||
|
||||
// We have to do this complex stuff because we cannot impl
|
||||
// rand::distributions::Distribution<I256> because benches are considered out of the crate
|
||||
// so neither I256 nor rand::distributions::Distribution belong to the benches.
|
||||
//
|
||||
// rand::distributions::Distribution can't be implemented in tfhe sources
|
||||
// in a way that it becomes available to the benches, because rand is a dev dependency
|
||||
fn gen_random_i256_in_range(rng: &mut ThreadRng, bit_size: usize) -> I256 {
|
||||
let value = gen_random_i256(rng);
|
||||
if value >= I256::ZERO {
|
||||
value & positive_bit_mask_for_bit_size(bit_size)
|
||||
} else {
|
||||
(value & negative_bit_mask_for_bit_size(bit_size)) | -I256::ONE
|
||||
}
|
||||
}
|
||||
|
||||
// Functions used to apply different way of selecting a scalar based on the context.
|
||||
fn default_scalar(rng: &mut ThreadRng, clear_bit_size: usize) -> ScalarType {
|
||||
gen_random_i256_in_range(rng, clear_bit_size)
|
||||
}
|
||||
|
||||
fn shift_scalar(_rng: &mut ThreadRng, _clear_bit_size: usize) -> ScalarType {
|
||||
// Shifting by one is the worst case scenario.
|
||||
ScalarType::ONE
|
||||
}
|
||||
|
||||
fn div_scalar(rng: &mut ThreadRng, clear_bit_size: usize) -> ScalarType {
|
||||
loop {
|
||||
let scalar = gen_random_i256_in_range(rng, clear_bit_size);
|
||||
if scalar != ScalarType::ZERO {
|
||||
return scalar;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_server_key_bench_binary_scalar_clean_inputs_fn (
|
||||
(method_name: $server_key_method:ident, display_name:$name:ident, rng_func:$($rng_fn:tt)*) => {
|
||||
fn $server_key_method(c: &mut Criterion) {
|
||||
bench_server_key_binary_scalar_function_clean_inputs(
|
||||
c,
|
||||
concat!("integer::", stringify!($server_key_method)),
|
||||
stringify!($name),
|
||||
|server_key, lhs, rhs| {
|
||||
server_key.$server_key_method(lhs, rhs);
|
||||
}, $($rng_fn)*)
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_scalar_clean_inputs_fn!(
|
||||
method_name: unchecked_scalar_left_shift_parallelized,
|
||||
display_name: scalar_left_shift,
|
||||
rng_func: shift_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_scalar_clean_inputs_fn!(
|
||||
method_name: unchecked_scalar_right_shift_parallelized,
|
||||
display_name: scalar_right_shift,
|
||||
rng_func: shift_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_scalar_clean_inputs_fn!(
|
||||
method_name: unchecked_scalar_rotate_right_parallelized,
|
||||
display_name: scalar_rotate_right,
|
||||
rng_func: shift_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_scalar_clean_inputs_fn!(
|
||||
method_name: unchecked_scalar_rotate_left_parallelized,
|
||||
display_name: scalar_rotate_left,
|
||||
rng_func: shift_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_scalar_clean_inputs_fn!(
|
||||
method_name: unchecked_scalar_mul_parallelized,
|
||||
display_name: scalar_mul,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_scalar_clean_inputs_fn!(
|
||||
method_name: unchecked_scalar_bitand_parallelized,
|
||||
display_name: scalar_bitand,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_scalar_clean_inputs_fn!(
|
||||
method_name: unchecked_scalar_bitor_parallelized,
|
||||
display_name: scalar_bitor,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_scalar_clean_inputs_fn!(
|
||||
method_name: unchecked_scalar_bitxor_parallelized,
|
||||
display_name: scalar_bitxor,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_scalar_clean_inputs_fn!(
|
||||
method_name: unchecked_scalar_eq_parallelized,
|
||||
display_name: scalar_eq,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_scalar_clean_inputs_fn!(
|
||||
method_name: unchecked_scalar_ne_parallelized,
|
||||
display_name: scalar_ne,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_scalar_clean_inputs_fn!(
|
||||
method_name: unchecked_scalar_le_parallelized,
|
||||
display_name: scalar_le,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_scalar_clean_inputs_fn!(
|
||||
method_name: unchecked_scalar_lt_parallelized,
|
||||
display_name: scalar_lt,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_scalar_clean_inputs_fn!(
|
||||
method_name: unchecked_scalar_ge_parallelized,
|
||||
display_name: scalar_ge,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_scalar_clean_inputs_fn!(
|
||||
method_name: unchecked_scalar_gt_parallelized,
|
||||
display_name: scalar_gt,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_scalar_clean_inputs_fn!(
|
||||
method_name: unchecked_scalar_max_parallelized,
|
||||
display_name: scalar_max,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_scalar_clean_inputs_fn!(
|
||||
method_name: unchecked_scalar_min_parallelized,
|
||||
display_name: scalar_min,
|
||||
rng_func: default_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_scalar_clean_inputs_fn!(
|
||||
method_name: unchecked_signed_scalar_div_rem_parallelized,
|
||||
display_name: scalar_div_rem,
|
||||
rng_func: div_scalar
|
||||
);
|
||||
|
||||
define_server_key_bench_binary_scalar_clean_inputs_fn!(
|
||||
method_name: unchecked_signed_scalar_div_parallelized,
|
||||
display_name: scalar_div,
|
||||
rng_func: div_scalar
|
||||
);
|
||||
|
||||
criterion_group!(
|
||||
unchecked_scalar_ops,
|
||||
unchecked_scalar_left_shift_parallelized,
|
||||
unchecked_scalar_right_shift_parallelized,
|
||||
unchecked_scalar_rotate_right_parallelized,
|
||||
unchecked_scalar_rotate_left_parallelized,
|
||||
unchecked_scalar_bitand_parallelized,
|
||||
unchecked_scalar_bitor_parallelized,
|
||||
unchecked_scalar_bitxor_parallelized,
|
||||
unchecked_scalar_mul_parallelized,
|
||||
unchecked_signed_scalar_div_rem_parallelized,
|
||||
unchecked_signed_scalar_div_parallelized,
|
||||
unchecked_div_rem_floor_parallelized,
|
||||
);
|
||||
|
||||
criterion_group!(
|
||||
unchecked_scalar_ops_comp,
|
||||
unchecked_scalar_eq_parallelized,
|
||||
unchecked_scalar_ne_parallelized,
|
||||
unchecked_scalar_le_parallelized,
|
||||
unchecked_scalar_lt_parallelized,
|
||||
unchecked_scalar_ge_parallelized,
|
||||
unchecked_scalar_gt_parallelized,
|
||||
unchecked_scalar_max_parallelized,
|
||||
unchecked_scalar_min_parallelized,
|
||||
);
|
||||
|
||||
criterion_group!(default_ops, signed_if_then_else_parallelized,);
|
||||
|
||||
fn main() {
|
||||
match env::var("__TFHE_RS_BENCH_OP_FLAVOR") {
|
||||
Ok(val) => {
|
||||
match val.to_lowercase().as_str() {
|
||||
"unchecked" => unchecked_ops(),
|
||||
"unchecked_comp" => unchecked_ops_comp(),
|
||||
"unchecked_scalar" => unchecked_scalar_ops(),
|
||||
"unchecked_scalar_comp" => unchecked_scalar_ops_comp(),
|
||||
_ => panic!("unknown benchmark operations flavor"),
|
||||
};
|
||||
}
|
||||
Err(_) => {
|
||||
unchecked_ops();
|
||||
unchecked_ops_comp();
|
||||
unchecked_scalar_ops();
|
||||
unchecked_scalar_ops_comp();
|
||||
}
|
||||
};
|
||||
|
||||
Criterion::default().configure_from_args().final_summary();
|
||||
}
|
||||
@@ -37,4 +37,3 @@ foreach (testsourcefile ${TEST_CASES})
|
||||
# Enabled asserts even in release mode
|
||||
add_definitions(-UNDEBUG)
|
||||
endforeach (testsourcefile ${TEST_CASES})
|
||||
|
||||
|
||||
@@ -110,6 +110,134 @@ int uint256_public_key(const ClientKey *client_key, const PublicKey *public_key)
|
||||
return ok;
|
||||
}
|
||||
|
||||
int int256_client_key(const ClientKey *client_key) {
|
||||
int ok;
|
||||
FheInt256 *lhs = NULL;
|
||||
FheInt256 *rhs = NULL;
|
||||
FheInt256 *result = NULL;
|
||||
FheInt64 *cast_result = NULL;
|
||||
// This is +1
|
||||
I256 lhs_clear = {1, 0, 0, 0};
|
||||
// This is -1
|
||||
I256 rhs_clear = {UINT64_MAX, UINT64_MAX, UINT64_MAX, UINT64_MAX};
|
||||
I256 result_clear = {0};
|
||||
|
||||
ok = fhe_int256_try_encrypt_with_client_key_i256(lhs_clear, client_key, &lhs);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_int256_try_encrypt_with_client_key_i256(rhs_clear, client_key, &rhs);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_int256_add(lhs, rhs, &result);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_int256_decrypt(result, client_key, &result_clear);
|
||||
assert(ok == 0);
|
||||
|
||||
// We did 1 + (-1), so we expect 0
|
||||
assert(result_clear.w0 == 0);
|
||||
assert(result_clear.w1 == 0);
|
||||
assert(result_clear.w2 == 0);
|
||||
assert(result_clear.w3 == 0);
|
||||
|
||||
fhe_int256_destroy(result);
|
||||
ok = fhe_int256_sub(lhs, rhs, &result);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_int256_decrypt(result, client_key, &result_clear);
|
||||
assert(ok == 0);
|
||||
|
||||
// We did 1 - (-1), so we expect 2
|
||||
assert(result_clear.w0 == 2);
|
||||
assert(result_clear.w1 == 0);
|
||||
assert(result_clear.w2 == 0);
|
||||
assert(result_clear.w3 == 0);
|
||||
|
||||
// try some casting
|
||||
ok = fhe_int256_cast_into_fhe_int64(result, &cast_result);
|
||||
assert(ok == 0);
|
||||
int64_t u64_clear;
|
||||
ok = fhe_int64_decrypt(cast_result, client_key, &u64_clear);
|
||||
assert(ok == 0);
|
||||
assert(u64_clear == 2);
|
||||
|
||||
fhe_int256_destroy(lhs);
|
||||
fhe_int256_destroy(rhs);
|
||||
fhe_int256_destroy(result);
|
||||
fhe_int64_destroy(cast_result);
|
||||
return ok;
|
||||
}
|
||||
|
||||
int int256_encrypt_trivial(const ClientKey *client_key) {
|
||||
int ok;
|
||||
FheInt256 *lhs = NULL;
|
||||
FheInt256 *rhs = NULL;
|
||||
FheInt256 *result = NULL;
|
||||
I256 lhs_clear = {1, 2, 3, 4};
|
||||
I256 rhs_clear = {5, 6, 7, 8};
|
||||
I256 result_clear = {0};
|
||||
|
||||
ok = fhe_int256_try_encrypt_trivial_i256(lhs_clear, &lhs);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_int256_try_encrypt_trivial_i256(rhs_clear, &rhs);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_int256_add(lhs, rhs, &result);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_int256_decrypt(result, client_key, &result_clear);
|
||||
assert(ok == 0);
|
||||
|
||||
assert(result_clear.w0 == 6);
|
||||
assert(result_clear.w1 == 8);
|
||||
assert(result_clear.w2 == 10);
|
||||
assert(result_clear.w3 == 12);
|
||||
|
||||
fhe_int256_destroy(lhs);
|
||||
fhe_int256_destroy(rhs);
|
||||
fhe_int256_destroy(result);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
int int256_public_key(const ClientKey *client_key, const PublicKey *public_key) {
|
||||
int ok;
|
||||
FheInt256 *lhs = NULL;
|
||||
FheInt256 *rhs = NULL;
|
||||
FheInt256 *result = NULL;
|
||||
// This is +1
|
||||
I256 lhs_clear = {1, 0, 0, 0};
|
||||
// This is -1
|
||||
I256 rhs_clear = {UINT64_MAX, UINT64_MAX, UINT64_MAX, UINT64_MAX};
|
||||
I256 result_clear = {0};
|
||||
|
||||
ok = fhe_int256_try_encrypt_with_public_key_i256(lhs_clear, public_key, &lhs);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_int256_try_encrypt_with_public_key_i256(rhs_clear, public_key, &rhs);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_int256_sub(lhs, rhs, &result);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_int256_decrypt(result, client_key, &result_clear);
|
||||
assert(ok == 0);
|
||||
|
||||
// We did 1 - (-1), so we expect 2
|
||||
assert(result_clear.w0 == 2);
|
||||
assert(result_clear.w1 == 0);
|
||||
assert(result_clear.w2 == 0);
|
||||
assert(result_clear.w3 == 0);
|
||||
|
||||
fhe_int256_destroy(lhs);
|
||||
fhe_int256_destroy(rhs);
|
||||
fhe_int256_destroy(result);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(void) {
|
||||
int ok = 0;
|
||||
ConfigBuilder *builder;
|
||||
@@ -132,6 +260,10 @@ int main(void) {
|
||||
uint256_encrypt_trivial(client_key);
|
||||
uint256_public_key(client_key, public_key);
|
||||
|
||||
int256_client_key(client_key);
|
||||
int256_encrypt_trivial(client_key);
|
||||
int256_public_key(client_key, public_key);
|
||||
|
||||
client_key_destroy(client_key);
|
||||
public_key_destroy(public_key);
|
||||
server_key_destroy(server_key);
|
||||
|
||||
@@ -78,7 +78,7 @@ int uint256_encrypt_trivial(const ClientKey *client_key) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
int uint256_public_key(const ClientKey *client_key,
|
||||
int uint256_compact_public_key(const ClientKey *client_key,
|
||||
const CompressedCompactPublicKey *compressed_public_key) {
|
||||
int ok;
|
||||
CompactPublicKey *public_key = NULL;
|
||||
@@ -112,7 +112,7 @@ int uint256_public_key(const ClientKey *client_key,
|
||||
lhs = expand_output[0];
|
||||
rhs = expand_output[1];
|
||||
// We can destroy the compact list
|
||||
// The expanded ciphertext are independant from it
|
||||
// The expanded ciphertext are independent from it
|
||||
compact_fhe_uint256_list_destroy(list);
|
||||
|
||||
ok = fhe_uint256_sub(lhs, rhs, &result);
|
||||
@@ -158,6 +158,80 @@ int uint256_public_key(const ClientKey *client_key,
|
||||
return ok;
|
||||
}
|
||||
|
||||
int int32_compact_public_key(const ClientKey *client_key,
|
||||
const CompressedCompactPublicKey *compressed_public_key) {
|
||||
int ok;
|
||||
CompactPublicKey *public_key = NULL;
|
||||
FheInt32 *lhs = NULL;
|
||||
FheInt32 *rhs = NULL;
|
||||
FheInt32 *result = NULL;
|
||||
CompactFheInt32List *list = NULL;
|
||||
|
||||
int32_t result_clear = 0;
|
||||
int32_t clears[2] = {-9482394, 98712234};
|
||||
|
||||
ok = compressed_compact_public_key_decompress(compressed_public_key, &public_key);
|
||||
assert(ok == 0);
|
||||
|
||||
// Compact list example
|
||||
{
|
||||
ok = compact_fhe_int32_list_try_encrypt_with_compact_public_key_i32(&clears[0], 2,
|
||||
public_key, &list);
|
||||
assert(ok == 0);
|
||||
|
||||
size_t len = 0;
|
||||
ok = compact_fhe_int32_list_len(list, &len);
|
||||
assert(ok == 0);
|
||||
assert(len == 2);
|
||||
|
||||
FheInt32 *expand_output[2] = {NULL};
|
||||
ok = compact_fhe_int32_list_expand(list, &expand_output[0], 2);
|
||||
assert(ok == 0);
|
||||
|
||||
// transfer ownership
|
||||
lhs = expand_output[0];
|
||||
rhs = expand_output[1];
|
||||
// We can destroy the compact list
|
||||
// The expanded ciphertext are independent from it
|
||||
compact_fhe_int32_list_destroy(list);
|
||||
|
||||
ok = fhe_int32_sub(lhs, rhs, &result);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_int32_decrypt(result, client_key, &result_clear);
|
||||
assert(ok == 0);
|
||||
|
||||
assert(result_clear == clears[0] - clears[1]);
|
||||
|
||||
fhe_int32_destroy(lhs);
|
||||
fhe_int32_destroy(rhs);
|
||||
fhe_int32_destroy(result);
|
||||
}
|
||||
|
||||
{
|
||||
ok = fhe_int32_try_encrypt_with_compact_public_key_i32(clears[0], public_key, &lhs);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_int32_try_encrypt_with_compact_public_key_i32(clears[1], public_key, &rhs);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_int32_add(lhs, rhs, &result);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = fhe_int32_decrypt(result, client_key, &result_clear);
|
||||
assert(ok == 0);
|
||||
|
||||
assert(result_clear == clears[0] + clears[1]);
|
||||
|
||||
fhe_int32_destroy(lhs);
|
||||
fhe_int32_destroy(rhs);
|
||||
fhe_int32_destroy(result);
|
||||
}
|
||||
|
||||
compact_public_key_destroy(public_key);
|
||||
return ok;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
int ok = 0;
|
||||
{
|
||||
@@ -180,7 +254,8 @@ int main(void) {
|
||||
|
||||
uint256_client_key(client_key);
|
||||
uint256_encrypt_trivial(client_key);
|
||||
uint256_public_key(client_key, compressed_public_key);
|
||||
uint256_compact_public_key(client_key, compressed_public_key);
|
||||
int32_compact_public_key(client_key, compressed_public_key);
|
||||
|
||||
client_key_destroy(client_key);
|
||||
compressed_compact_public_key_destroy(compressed_public_key);
|
||||
@@ -207,7 +282,8 @@ int main(void) {
|
||||
|
||||
uint256_client_key(client_key);
|
||||
uint256_encrypt_trivial(client_key);
|
||||
uint256_public_key(client_key, compressed_public_key);
|
||||
uint256_compact_public_key(client_key, compressed_public_key);
|
||||
int32_compact_public_key(client_key, compressed_public_key);
|
||||
|
||||
client_key_destroy(client_key);
|
||||
compressed_compact_public_key_destroy(compressed_public_key);
|
||||
|
||||
@@ -64,6 +64,67 @@ int uint8_public_key(const ClientKey *client_key, const PublicKey *public_key) {
|
||||
return ok;
|
||||
}
|
||||
|
||||
int uint8_safe_serialization(const ClientKey *client_key, const ServerKey *server_key) {
|
||||
int ok;
|
||||
CompactFheUint8 *lhs = NULL;
|
||||
CompactFheUint8 *deserialized_lhs = NULL;
|
||||
CompactFheUint8 *result = NULL;
|
||||
Buffer value_buffer = {.pointer = NULL, .length = 0};
|
||||
Buffer cks_buffer = {.pointer = NULL, .length = 0};
|
||||
BufferView deser_view = {.pointer = NULL, .length = 0};
|
||||
ClientKey *deserialized_client_key = NULL;
|
||||
|
||||
const uint64_t max_serialization_size = UINT64_C(1) << UINT64_C(20);
|
||||
|
||||
uint8_t lhs_clear = 123;
|
||||
|
||||
ok = client_key_serialize(client_key, &cks_buffer);
|
||||
assert(ok == 0);
|
||||
|
||||
deser_view.pointer = cks_buffer.pointer;
|
||||
deser_view.length = cks_buffer.length;
|
||||
ok = client_key_deserialize(deser_view, &deserialized_client_key);
|
||||
assert(ok == 0);
|
||||
|
||||
struct CompactPublicKey *public_key;
|
||||
|
||||
ok = compact_public_key_new(deserialized_client_key, &public_key);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = compact_fhe_uint8_try_encrypt_with_compact_public_key_u8(lhs_clear, public_key, &lhs);
|
||||
assert(ok == 0);
|
||||
|
||||
ok = compact_fhe_uint8_safe_serialize(lhs, &value_buffer, max_serialization_size);
|
||||
assert(ok == 0);
|
||||
|
||||
deser_view.pointer = value_buffer.pointer;
|
||||
deser_view.length = value_buffer.length;
|
||||
ok = compact_fhe_uint8_safe_deserialize_conformant(deser_view, max_serialization_size, server_key,
|
||||
&deserialized_lhs);
|
||||
assert(ok == 0);
|
||||
|
||||
FheUint8 *expanded = NULL;
|
||||
|
||||
ok = compact_fhe_uint8_expand(deserialized_lhs, &expanded);
|
||||
assert(ok == 0);
|
||||
|
||||
uint8_t clear;
|
||||
ok = fhe_uint8_decrypt(expanded, deserialized_client_key, &clear);
|
||||
assert(ok == 0);
|
||||
|
||||
assert(clear == lhs_clear);
|
||||
|
||||
if (value_buffer.pointer != NULL) {
|
||||
destroy_buffer(&value_buffer);
|
||||
}
|
||||
compact_fhe_uint8_destroy(lhs);
|
||||
compact_fhe_uint8_destroy(deserialized_lhs);
|
||||
compact_fhe_uint8_destroy(result);
|
||||
fhe_uint8_destroy(expanded);
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
int uint8_serialization(const ClientKey *client_key) {
|
||||
int ok;
|
||||
FheUint8 *lhs = NULL;
|
||||
@@ -159,6 +220,8 @@ int main(void) {
|
||||
assert(ok == 0);
|
||||
ok = uint8_serialization(client_key);
|
||||
assert(ok == 0);
|
||||
ok = uint8_safe_serialization(client_key, server_key);
|
||||
assert(ok == 0);
|
||||
ok = uint8_compressed(client_key);
|
||||
assert(ok == 0);
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
## Getting Started
|
||||
* [Installation](getting_started/installation.md)
|
||||
* [Quick Start](getting_started/quick_start.md)
|
||||
* [Operations](getting_started/operations.md)
|
||||
* [Types & Operations](getting_started/operations.md)
|
||||
* [Benchmarks](getting_started/benchmarks.md)
|
||||
* [Security and Cryptography](getting_started/security_and_cryptography.md)
|
||||
|
||||
@@ -56,4 +56,3 @@
|
||||
|
||||
## API references
|
||||
* [docs.rs](https://docs.rs/tfhe/)
|
||||
|
||||
|
||||
@@ -504,7 +504,7 @@ 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 somwhere `ab`
|
||||
`/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`
|
||||
|
||||
@@ -249,7 +249,7 @@ pub fn main() {
|
||||
println!("Checking result...");
|
||||
assert_eq!(6, pbs_multiplication_result);
|
||||
println!(
|
||||
"Mulitplication via PBS result is correct! Expected 6, got {pbs_multiplication_result}"
|
||||
"Multiplication via PBS result is correct! Expected 6, got {pbs_multiplication_result}"
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
@@ -17,7 +17,7 @@ This crate implements two ways to represent an integer:
|
||||
|
||||
The first possibility to represent a large integer is to use a Radix-based decomposition on the plaintexts. Let $$B \in \mathbb{N}$$ be a basis such that the size of $$B$$ is smaller than (or equal to) 4 bits. Then, an integer $$m \in \mathbb{N}$$ can be written as $$m = m_0 + m_1*B + m_2*B^2 + ...$$, where each $$m_i$$ is strictly smaller than $$B$$. Each $$m_i$$ is then independently encrypted. In the end, an Integer ciphertext is defined as a set of shortint ciphertexts.
|
||||
|
||||
The definition of an integer requires a basis and a number of blocks. This is done at key generation. Below, the keys are dedicated to unsigned integers encrypting messages over 8 bits, using a basis over 2 bits (i.e., $$B=2^2$$) and 4 blocks.
|
||||
The definition of an integer requires a basis and a number of blocks. These parameters are chosen at key generation. Below, the keys are dedicated to integers encrypting messages over 8 bits, using a basis over 2 bits (i.e., $$B=2^2$$) and 4 blocks.
|
||||
|
||||
```rust
|
||||
use tfhe::integer::gen_keys_radix;
|
||||
@@ -93,6 +93,10 @@ Each operation may come in different 'flavors':
|
||||
|
||||
Not all operations have these 4 flavors, as some of them are implemented in a way that the operation is always possible without ever exceeding the plaintext space capacity.
|
||||
|
||||
{% hint style="info" %}
|
||||
If you don't know which flavor to use, you should use the `default` one.
|
||||
{% endhint %}
|
||||
|
||||
## How to use each operation type
|
||||
|
||||
Let's try to do a circuit evaluation using the different flavors of already introduced operations. For a very small circuit, the `unchecked` flavor may be enough to do the computation correctly. Otherwise, `checked` and `smart` are the best options.
|
||||
@@ -162,12 +166,15 @@ fn main() {
|
||||
assert!(result.is_ok());
|
||||
|
||||
let result = server_key.checked_sub_assign(&mut ct_1, &ct_2);
|
||||
assert!(result.is_ok());
|
||||
|
||||
let result = server_key.checked_add_assign(&mut ct_1, &ct_3);
|
||||
assert!(result.is_err());
|
||||
|
||||
// We use the client key to decrypt the output of the circuit:
|
||||
// Only the scalar multiplication could be done
|
||||
let output: u64 = client_key.decrypt(&ct_1);
|
||||
assert_eq!(output, (msg1 * scalar) % modulus as u64);
|
||||
assert_eq!(output, ((msg1 * scalar) - msg2) % modulus as u64);
|
||||
}
|
||||
```
|
||||
|
||||
@@ -208,6 +215,14 @@ 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" %}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Tutorial
|
||||
|
||||
`tfhe::integer` is dedicated to unsigned integers smaller than 256 bits. The steps to homomorphically evaluate an integer circuit are described here.
|
||||
`tfhe::integer` is dedicated to integers smaller than 256 bits. The steps to homomorphically evaluate an integer circuit are described here.
|
||||
|
||||
## Key Types
|
||||
|
||||
@@ -25,7 +25,7 @@ To generate the keys, a user needs two parameters:
|
||||
* A set of `shortint` cryptographic parameters.
|
||||
* The number of ciphertexts used to encrypt an integer (we call them "shortint blocks").
|
||||
|
||||
We are now going to build a pair of keys that can encrypt an **8-bit** integer by using **4** shortint blocks that store **2** bits of message each.
|
||||
We are now going to build a pair of keys that can encrypt **8-bit** integers (signed or unsigned) by using **4** shortint blocks that store **2** bits of message each.
|
||||
|
||||
```rust
|
||||
use tfhe::integer::gen_keys_radix;
|
||||
|
||||
@@ -141,7 +141,7 @@ use tfhe::integer::gen_keys_radix;
|
||||
use tfhe::shortint::parameters::PARAM_MESSAGE_2_CARRY_2;
|
||||
|
||||
fn main() {
|
||||
// We create keys for radix represention to create 16 bits integers
|
||||
// We create keys for radix representation to create 16 bits integers
|
||||
// using 8 blocks of 2 bits
|
||||
let (cks, sks) = gen_keys_radix(PARAM_MESSAGE_2_CARRY_2, 8);
|
||||
|
||||
|
||||
@@ -37,8 +37,14 @@ Each operation may come in different 'flavors':
|
||||
|
||||
Not all operations have these 4 flavors, as some of them are implemented in a way that the operation is always possible without ever exceeding the plaintext space capacity.
|
||||
|
||||
{% hint style="info" %}
|
||||
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.
|
||||
@@ -367,14 +373,14 @@ fn main() {
|
||||
let modulus = client_key.parameters.message_modulus().0 as u64;
|
||||
|
||||
// We use the private client key to encrypt two messages:
|
||||
let ct_1 = client_key.encrypt(msg1);
|
||||
let mut ct_1 = client_key.encrypt(msg1);
|
||||
let mut ct_2 = client_key.encrypt(msg2);
|
||||
|
||||
// Compute the lookup table for the bivariate functions
|
||||
let acc = server_key.generate_lookup_table_bivariate(|x,y| (x.count_ones()
|
||||
+ y.count_ones()) as u64 % modulus );
|
||||
|
||||
let ct_res = server_key.smart_apply_lookup_table_bivariate(&ct_1, &mut ct_2, &acc);
|
||||
let ct_res = server_key.smart_apply_lookup_table_bivariate(&mut ct_1, &mut ct_2, &acc);
|
||||
|
||||
// We use the client key to decrypt the output of the circuit:
|
||||
let output = client_key.decrypt(&ct_res);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Tutorial
|
||||
|
||||
`tfhe::shortint` is dedicated to small unsigned integers smaller than 8 bits. The steps to homomorphically evaluate a circuit are described below.
|
||||
`tfhe::shortint` is dedicated to unsigned integers smaller than 8 bits. The steps to homomorphically evaluate a circuit are described below.
|
||||
|
||||
## Key generation
|
||||
|
||||
|
||||
@@ -1,101 +1,106 @@
|
||||
# Benchmarks
|
||||
|
||||
Due to their nature, homomorphic operations are naturally slower than their clear equivalent. Some timings are exposed for basic operations. For completeness, benchmarks for other libraries are also given.
|
||||
Due to their nature, homomorphic operations are naturally slower than their cleartext equivalents. Some timings are exposed for basic operations. For completeness, benchmarks for other libraries are also given.
|
||||
|
||||
{% hint style="info" %}
|
||||
All benchmarks were launched on an AWS m6i.metal with the following specifications: Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz and 512GB of RAM.
|
||||
{% endhint %}
|
||||
|
||||
## Integer
|
||||
|
||||
This measures the execution time for some operation sets of tfhe-rs::integer (the unsigned version). Note that the timings for `FheInt` (i.e., the signed integers) are similar.
|
||||
|
||||
| Operation \ Size | `FheUint8` | `FheUint16` | `FheUint32` | `FheUint64` | `FheUint128` | `FheUint256` |
|
||||
|--------------------------------------------------------|------------|-------------|-------------|-------------|--------------|--------------|
|
||||
| Negation (`-`) | 70.9 ms | 99.3 ms | 129 ms | 180 ms | 239 ms | 333 ms |
|
||||
| Add / Sub (`+`,`-`) | 70.5 ms | 100 ms | 132 ms | 186 ms | 249 ms | 334 ms |
|
||||
| Mul (`x`) | 144 ms | 216 ms | 333 ms | 832 ms | 2.50 s | 8.85 s |
|
||||
| Equal / Not Equal (`eq`, `ne`) | 36.1 ms | 36.5 ms | 57.4 ms | 64.2 ms | 67.3 ms | 78.1 ms |
|
||||
| Comparisons (`ge`, `gt`, `le`, `lt`) | 52.6 ms | 73.1 ms | 98.8 ms | 124 ms | 165 ms | 201 ms |
|
||||
| Max / Min (`max`,`min`) | 76.2 ms | 102 ms | 135 ms | 171 ms | 212 ms | 301 ms |
|
||||
| Bitwise operations (`&`, `\|`, `^`) | 19.4 ms | 20.3 ms | 21.0 ms | 27.2 ms | 31.6 ms | 40.2 ms |
|
||||
| Div / Rem (`/`, `%`) | 729 ms | 1.93 s | 4.81 s | 12.2 s | 30.7 s | 89.6 s |
|
||||
| Left / Right Shifts (`<<`, `>>`) | 99.4 ms | 129 ms | 180 ms | 243 ms | 372 ms | 762 ms |
|
||||
| Left / Right Rotations (`left_rotate`, `right_rotate`) | 103 ms | 128 ms | 182 ms | 241 ms | 374 ms | 763 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`.
|
||||
|
||||
|
||||
## Shortint
|
||||
|
||||
This measures the execution time for some operations using various parameter sets of tfhe-rs::shortint. Except for `unchecked_add`, all timings are related to the `default` operations. This flavor ensures predictable timings for an operation along the entire circuit by clearing the carry space after each operation.
|
||||
|
||||
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 | 348 ns | 413 ns | 2.95 µs | 12.1 µs |
|
||||
| add | 7.59 ms | 17.0 ms | 121 ms | 835 ms |
|
||||
| mul\_lsb | 8.13 ms | 16.8 ms | 121 ms | 827 ms |
|
||||
| keyswitch\_programmable\_bootstrap | 7.28 ms | 16.6 ms | 121 ms | 811 ms |
|
||||
|
||||
|
||||
## Boolean
|
||||
|
||||
This measures the execution time of a single binary Boolean gate.
|
||||
|
||||
### tfhe-rs::boolean.
|
||||
|
||||
| Parameter set | Concrete FFT | Concrete FFT + AVX-512 |
|
||||
| --------------------- | ------------ | ---------------------- |
|
||||
| DEFAULT\_PARAMETERS | 8.8ms | 6.8ms |
|
||||
| TFHE\_LIB\_PARAMETERS | 13.6ms | 10.9ms |
|
||||
| Parameter set | Concrete FFT + AVX-512 |
|
||||
|------------------------------------------------------|------------------------|
|
||||
| DEFAULT\_PARAMETERS\_KS\_PBS | 9.19 ms |
|
||||
| PARAMETERS\_ERROR\_PROB\_2\_POW\_MINUS\_165\_KS\_PBS | 14.1 ms |
|
||||
| TFHE\_LIB\_PARAMETERS | 10.0 ms |
|
||||
|
||||
|
||||
### tfhe-lib.
|
||||
|
||||
| Parameter set | fftw | spqlios-fma |
|
||||
| ------------------------------------------------ | ------ | ----------- |
|
||||
| default\_128bit\_gate\_bootstrapping\_parameters | 28.9ms | 15.7ms |
|
||||
Using the same m6i.metal machine as the one for tfhe-rs, the timings are:
|
||||
|
||||
### OpenFHE.
|
||||
| Parameter set | spqlios-fma |
|
||||
|--------------------------------------------------|-------------|
|
||||
| default\_128bit\_gate\_bootstrapping\_parameters | 15.4 ms |
|
||||
|
||||
| Parameter set | GINX | GINX (Intel HEXL) |
|
||||
| ------------- | ----- | ----------------- |
|
||||
| STD\_128 | 172ms | 78ms |
|
||||
| MEDIUM | 113ms | 50.2ms |
|
||||
### OpenFHE (v1.1.1).
|
||||
|
||||
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
|
||||
|
||||
scripts/configure.sh
|
||||
Release -> y
|
||||
hexl -> y
|
||||
|
||||
scripts/build-openfhe-development-hexl.sh
|
||||
```
|
||||
|
||||
Using the same m6i.metal machine as the one for tfhe-rs, the timings are:
|
||||
|
||||
| Parameter set | GINX | GINX w/ Intel HEXL |
|
||||
|----------------------------------|---------|--------------------|
|
||||
| FHEW\_BINGATE/STD128\_OR | 40.2 ms | 31.0 ms |
|
||||
| FHEW\_BINGATE/STD128\_LMKCDEY_OR | 38.6 ms | 28.4 ms |
|
||||
|
||||
|
||||
## Integer
|
||||
This measures the execution time for some operation sets of tfhe-rs::integer.
|
||||
## How to reproduce TFHE-rs benchmarks
|
||||
|
||||
| Operation \ Size | `FheUint8` | `FheUint16` | `FheUint32` | ` FheUint64` | `FheUint128` | `FheUint256` |
|
||||
|--------------------------------------------------------|------------|-------------|-------------|--------------|--------------|--------------|
|
||||
| Negation (`-`) | 80.4 ms | 106 ms | 132 ms | 193 ms | 257 ms | 348 ms |
|
||||
| Add / Sub (`+`,`-`) | 81.5 ms | 110 ms | 139 ms | 200 ms | 262 ms | 355 ms |
|
||||
| Mul (`x`) | 150 ms | 221 ms | 361 ms | 928 ms | 2.90 s | 10.97 s |
|
||||
| Equal / Not Equal (`eq`, `ne`) | 39.4 ms | 40.2 ms | 61.1 ms | 66.4 ms | 74.5 ms | 85.7 ms |
|
||||
| Comparisons (`ge`, `gt`, `le`, `lt`) | 57.5 ms | 79.6 ms | 105 ms | 136 ms | 174 ms | 219 ms |
|
||||
| Max / Min (`max`,`min`) | 100 ms | 130 ms | 163 ms | 204 ms | 245 ms | 338 ms |
|
||||
| Bitwise operations (`&`, `|`, `^`) | 20.7 ms | 21.1 ms | 22.6 ms | 30.2 ms | 34.1 ms | 42.1 ms |
|
||||
| Div / Rem (`/`, `%`) | 1.37 s | 3.50 s | 9.12 s | 23.9 s | 59.9 s | 149.2 s |
|
||||
| Left / Right Shifts (`<<`, `>>`) | 106 ms | 140 ms | 202 ms | 262 ms | 403 ms | 827 ms |
|
||||
| Left / Right Rotations (`left_rotate`, `right_rotate`) | 105 ms | 140 ms | 199 ms | 263 ms | 403 ms | 829 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, 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 could be reduced by using `unchecked`, `checked`, or `smart`.
|
||||
|
||||
|
||||
## Shortint
|
||||
This measures the execution time for some operations using various parameter sets of tfhe-rs::shortint.
|
||||
|
||||
This uses the Concrete FFT + AVX-512 configuration.
|
||||
|
||||
| Parameter set | unchecked\_add | unchecked\_mul\_lsb | keyswitch\_programmable\_bootstrap |
|
||||
|-----------------------------|----------------|---------------------|------------------------------------|
|
||||
| PARAM\_MESSAGE\_1\_CARRY\_1 | 338 ns | 8.3 ms | 8.1 ms |
|
||||
| PARAM\_MESSAGE\_2\_CARRY\_2 | 406 ns | 18.4 ms | 18.4 ms |
|
||||
| PARAM\_MESSAGE\_3\_CARRY\_3 | 3.06 µs | 134 ms | 129.4 ms |
|
||||
| PARAM\_MESSAGE\_4\_CARRY\_4 | 11.7 µs | 854 ms | 828.1 ms |
|
||||
|
||||
Next, the timings for the operation flavor `default` are given. This flavor ensures predictable timings of an operation along the entire circuit by clearing the carry space after each operation.
|
||||
|
||||
| Parameter set | add | mul\_lsb | keyswitch\_programmable\_bootstrap |
|
||||
| --------------------------- | -------------- | ------------------- | ---------------------------------- |
|
||||
| PARAM\_MESSAGE\_1\_CARRY\_1 | 7.90 ms | 8.00 ms | 8.10 ms |
|
||||
| PARAM\_MESSAGE\_2\_CARRY\_2 | 18.4 ms | 18.1 ms | 18.4 ms |
|
||||
| PARAM\_MESSAGE\_3\_CARRY\_3 | 131.5 ms | 129.5 ms | 129.4 ms |
|
||||
| PARAM\_MESSAGE\_4\_CARRY\_4 | 852.5 ms | 839.7 ms | 828.1 ms |
|
||||
|
||||
## How to reproduce benchmarks
|
||||
|
||||
TFHE-rs benchmarks can easily be reproduced from the [sources](https://github.com/zama-ai/tfhe-rs).
|
||||
TFHE-rs benchmarks can be easily reproduced from [source](https://github.com/zama-ai/tfhe-rs).
|
||||
|
||||
```shell
|
||||
#Boolean benchmarks:
|
||||
make bench_boolean
|
||||
make AVX512_SUPPORT=ON bench_boolean
|
||||
|
||||
#Integer benchmarks:
|
||||
make bench_integer
|
||||
|
||||
#Shortint benchmarks:
|
||||
make bench_shortint
|
||||
```
|
||||
|
||||
If the host machine supports AVX-512, then the argument `AVX512_SUPPORT=ON' should be added, e.g.:
|
||||
|
||||
```shell
|
||||
#Integer benchmarks:
|
||||
make AVX512_SUPPORT=ON bench_integer
|
||||
|
||||
#Shortint benchmarks:
|
||||
make AVX512_SUPPORT=ON bench_shortint
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
If the host machine does not support AVX512, then turning on `AVX512_SUPPORT` will not provide any speed-up.
|
||||
|
||||
@@ -4,12 +4,22 @@
|
||||
|
||||
## Importing into your project
|
||||
|
||||
To use `TFHE-rs` in your project, you first need to add it as a dependency in your `Cargo.toml`:
|
||||
To use `TFHE-rs` in your project, you first need to add it as a dependency in your `Cargo.toml`.
|
||||
|
||||
If you are using an `x86` machine:
|
||||
```toml
|
||||
tfhe = { version = "0.4.0", features = [ "boolean", "shortint", "integer", "x86_64-unix" ] }
|
||||
```
|
||||
|
||||
If you are using an `ARM` machine:
|
||||
```toml
|
||||
tfhe = { version = "0.4.0", features = [ "boolean", "shortint", "integer", "aarch64-unix" ] }
|
||||
```
|
||||
|
||||
{% hint style="info" %}
|
||||
You need to use a Rust version >= 1.72 to compile TFHE-rs.
|
||||
{% endhint %}
|
||||
|
||||
{% hint style="success" %}
|
||||
When running code that uses `TFHE-rs`, it is highly recommended to run in release mode with cargo's `--release` flag to have the best possible performance
|
||||
{% endhint %}
|
||||
@@ -25,7 +35,3 @@ TFHE-rs is supported on Linux (x86, aarch64), macOS (x86, aarch64) and Windows (
|
||||
| Linux | `x86_64-unix` | `aarch64-unix`\* |
|
||||
| macOS | `x86_64-unix` | `aarch64-unix`\* |
|
||||
| Windows | `x86_64` | Unsupported |
|
||||
|
||||
{% hint style="info" %}
|
||||
Users who have ARM devices can compile TFHE-rs using a stable toolchain with version >= 1.72 (see [Configuration](../how_to/rust_configuration.md) for more details).
|
||||
{% endhint %}
|
||||
|
||||
@@ -1,47 +1,386 @@
|
||||
# Operations
|
||||
# Homomorphic Types and Operations
|
||||
|
||||
The table below contains an overview of the available operations in `TFHE-rs`. More details, and further examples, are given in the following sections.
|
||||
## Types
|
||||
`TFHE-rs` includes two main types to represent encrypted data:
|
||||
- `FheUint`: this is the homomorphic equivalent of Rust unsigned integers `u8, u16, ...`
|
||||
- `FheInt`: this is the homomorphic equivalent of Rust (signed) integers `i8, i16, ...`
|
||||
|
||||
| name | symbol | FheUint/FheUint | FheUint/Uint | Uint/FheUint |
|
||||
|-----------------------|-------------|--------------------|--------------------------|--------------------------|
|
||||
| Neg | `-` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Add | `+` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Sub | `-` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Mul | `*` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Div | `/` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Rem | `%` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Not | `!` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| BitAnd | `&` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| BitOr | `\|` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| BitXor | `^` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Shr | `>>` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Shl | `<<` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Min | `min` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Max | `max` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Greater than | `gt` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Greater or equal than | `ge` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Lower than | `lt` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Lower or equal than | `le` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Equal | `eq` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Cast (into dest type) | `cast_into` | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_multiplication_x: |
|
||||
| Cast (from src type) | `cast_from` | :heavy_check_mark: | :heavy_multiplication_x: | :heavy_multiplication_x: |
|
||||
In the same manner as many programming languages, the number of bits used to represent the data must be chosen when declaring a variable. For instance:
|
||||
|
||||
## Boolean Operations
|
||||
```Rust
|
||||
// let clear_a: u64 = 7;
|
||||
let mut a = FheUint64::try_encrypt(clear_a, &keys)?;
|
||||
|
||||
Native homomorphic Booleans support common Boolean operations.
|
||||
// let clear_b: i8 = 3;
|
||||
let mut b = FheInt8::try_encrypt(clear_b, &keys)?;
|
||||
|
||||
// let clear_c: u128 = 2;
|
||||
let mut c = FheUint128::try_encrypt(clear_c, &keys)?;
|
||||
```
|
||||
|
||||
## Operation list
|
||||
The table below contains an overview of the available operations in `TFHE-rs`. The notation `Enc` (for Encypted) either refers to `FheInt` or `FheUint`, for any size between 1 and 256-bits.
|
||||
|
||||
More details, and further examples, are given in the following sections.
|
||||
|
||||
| name | symbol | `Enc`/`Enc` | `Enc`/ `Int` |
|
||||
|-----------------------|----------------|--------------------|--------------------------|
|
||||
| Neg | `-` | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Add | `+` | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Sub | `-` | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Mul | `*` | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Div | `/` | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Rem | `%` | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Not | `!` | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| BitAnd | `&` | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| BitOr | `\|` | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| BitXor | `^` | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Shr | `>>` | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Shl | `<<` | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Min | `min` | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Max | `max` | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Greater than | `gt` | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Greater or equal than | `ge` | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Lower than | `lt` | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Lower or equal than | `le` | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Equal | `eq` | :heavy_check_mark: | :heavy_check_mark: |
|
||||
| Cast (into dest type) | `cast_into` | :heavy_check_mark: | :heavy_multiplication_x: |
|
||||
| Cast (from src type) | `cast_from` | :heavy_check_mark: | :heavy_multiplication_x: |
|
||||
| Ternary operator | `if_then_else` | :heavy_check_mark: | :heavy_multiplication_x: |
|
||||
|
||||
|
||||
## Integer
|
||||
|
||||
In `TFHE-rs`, integers are used to encrypt all messages which are larger than 4 bits. All supported operations are listed below.
|
||||
|
||||
### Arithmetic operations.
|
||||
|
||||
Homomorphic integer types support arithmetic operations.
|
||||
|
||||
The list of supported operations is:
|
||||
|
||||
| name | symbol | type |
|
||||
| ------------------------------------------------------------- | ------ | ------ |
|
||||
| [BitAnd](https://doc.rust-lang.org/std/ops/trait.BitAnd.html) | `&` | Binary |
|
||||
| [BitOr](https://doc.rust-lang.org/std/ops/trait.BitOr.html) | `\|` | Binary |
|
||||
| [BitXor](https://doc.rust-lang.org/std/ops/trait.BitXor.html) | `^` | Binary |
|
||||
| [Not](https://doc.rust-lang.org/std/ops/trait.Not.html) | `!` | Unary |
|
||||
| name | symbol | type |
|
||||
|----------------------------------------------------------|--------|--------|
|
||||
| [Neg](https://doc.rust-lang.org/std/ops/trait.Neg.html) | `-` | Unary |
|
||||
| [Add](https://doc.rust-lang.org/std/ops/trait.Add.html) | `+` | Binary |
|
||||
| [Sub](https://doc.rust-lang.org/std/ops/trait.Sub.html) | `-` | Binary |
|
||||
| [Mul](https://doc.rust-lang.org/std/ops/trait.Mul.html) | `*` | Binary |
|
||||
| [Div](https://doc.rust-lang.org/std/ops/trait.Div.html)* | `/` | Binary |
|
||||
| [Rem](https://doc.rust-lang.org/std/ops/trait.Rem.html)* | `%` | Binary |
|
||||
|
||||
For division by 0, the convention is to return `modulus - 1`. For instance, for `FheUint8`, the modulus is $$2^8=256$$, so a division by 0 will return an encryption of 255.
|
||||
For the remainder operator, the convention is to return the first input without any modification. For instance, if `ct1 = FheUint8(63)` and `ct2 = FheUint8(0)` then `ct1 % ct2` will return `FheUint8(63)`.
|
||||
|
||||
A simple example of how to use these operations:
|
||||
|
||||
```rust
|
||||
use tfhe::prelude::*;
|
||||
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheInt8, FheUint8};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = ConfigBuilder::all_disabled().enable_default_integers().build();
|
||||
let (keys, server_keys) = generate_keys(config);
|
||||
set_server_key(server_keys);
|
||||
|
||||
let clear_a = 15_u64;
|
||||
let clear_b = 27_u64;
|
||||
let clear_c = 43_u64;
|
||||
let clear_d = -87_i64;
|
||||
|
||||
let mut a = FheUint8::try_encrypt(clear_a, &keys)?;
|
||||
let mut b = FheUint8::try_encrypt(clear_b, &keys)?;
|
||||
let mut c = FheUint8::try_encrypt(clear_c, &keys)?;
|
||||
let mut d = FheInt8::try_encrypt(clear_d, &keys)?;
|
||||
|
||||
|
||||
a = a * &b; // Clear equivalent computations: 15 * 27 mod 256 = 149
|
||||
b = &b + &c; // Clear equivalent computations: 27 + 43 mod 256 = 70
|
||||
b = b - 76u8; // Clear equivalent computations: 70 - 76 mod 256 = 250
|
||||
d = d - 13i8; // Clear equivalent computations: -87 - 13 = 100 in [-128, 128[
|
||||
|
||||
let dec_a: u8 = a.decrypt(&keys);
|
||||
let dec_b: u8 = b.decrypt(&keys);
|
||||
let dec_d: i8 = d.decrypt(&keys);
|
||||
|
||||
assert_eq!(dec_a, ((clear_a * clear_b) % 256_u64) as u8);
|
||||
assert_eq!(dec_b, (((clear_b + clear_c).wrapping_sub(76_u64)) % 256_u64) as u8);
|
||||
assert_eq!(dec_d, (clear_d - 13) as i8);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### Bitwise operations.
|
||||
|
||||
Homomorphic integer types support some bitwise operations.
|
||||
|
||||
The list of supported operations is:
|
||||
|
||||
| name | symbol | type |
|
||||
|--------------------------------------------------------------------------------------|----------------|--------|
|
||||
| [Not](https://doc.rust-lang.org/std/ops/trait.Not.html) | `!` | Unary |
|
||||
| [BitAnd](https://doc.rust-lang.org/std/ops/trait.BitAnd.html) | `&` | Binary |
|
||||
| [BitOr](https://doc.rust-lang.org/std/ops/trait.BitOr.html) | `\|` | Binary |
|
||||
| [BitXor](https://doc.rust-lang.org/std/ops/trait.BitXor.html) | `^` | Binary |
|
||||
| [Shr](https://doc.rust-lang.org/std/ops/trait.Shr.html) | `>>` | Binary |
|
||||
| [Shl](https://doc.rust-lang.org/std/ops/trait.Shl.html) | `<<` | Binary |
|
||||
| [Rotate Right](https://doc.rust-lang.org/std/primitive.u32.html#method.rotate_right) | `rotate_right` | Binary |
|
||||
| [Rotate Left](https://doc.rust-lang.org/std/primitive.u32.html#method.rotate_left) | `rotate_left` | Binary |
|
||||
|
||||
A simple example of how to use these operations:
|
||||
|
||||
```rust
|
||||
use tfhe::prelude::*;
|
||||
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = ConfigBuilder::all_disabled().enable_default_integers().build();
|
||||
let (keys, server_keys) = generate_keys(config);
|
||||
set_server_key(server_keys);
|
||||
|
||||
let clear_a = 164;
|
||||
let clear_b = 212;
|
||||
|
||||
let mut a = FheUint8::try_encrypt(clear_a, &keys)?;
|
||||
let mut b = FheUint8::try_encrypt(clear_b, &keys)?;
|
||||
|
||||
a = a ^ &b;
|
||||
b = b ^ &a;
|
||||
a = a ^ &b;
|
||||
|
||||
let dec_a: u8 = a.decrypt(&keys);
|
||||
let dec_b: u8 = b.decrypt(&keys);
|
||||
|
||||
// We homomorphically swapped values using bitwise operations
|
||||
assert_eq!(dec_a, clear_b);
|
||||
assert_eq!(dec_b, clear_a);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### Comparisons.
|
||||
|
||||
Homomorphic integers support comparison operations.
|
||||
|
||||
Due to some Rust limitations, it is not possible to overload the comparison symbols because of the inner definition of the operations. This is because Rust expects to have a Boolean as an output, whereas a ciphertext is returned when using homomorphic types.
|
||||
|
||||
You will need to use different methods instead of using symbols for the comparisons. These methods follow the same naming conventions as the two standard Rust traits:
|
||||
|
||||
* [PartialOrd](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html)
|
||||
* [PartialEq](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html)
|
||||
|
||||
The list of supported operations is:
|
||||
|
||||
| name | symbol | type |
|
||||
|-----------------------------------------------------------------------------|--------|--------|
|
||||
| [Equal ](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html) | `eq` | Binary |
|
||||
| [Not Equal ](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html) | `ne` | Binary |
|
||||
| [Greater Than ](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html) | `gt` | Binary |
|
||||
| [Greater or Equal](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html) | `ge` | Binary |
|
||||
| [Lower ](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html) | `lt` | Binary |
|
||||
| [Lower or Equal ](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html) | `le` | Binary |
|
||||
|
||||
A simple example of how to use these operations:
|
||||
|
||||
```rust
|
||||
use tfhe::prelude::*;
|
||||
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheInt8};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = ConfigBuilder::all_disabled().enable_default_integers().build();
|
||||
let (keys, server_keys) = generate_keys(config);
|
||||
set_server_key(server_keys);
|
||||
|
||||
let clear_a: i8 = -121;
|
||||
let clear_b: i8 = 87;
|
||||
|
||||
let mut a = FheInt8::try_encrypt(clear_a, &keys)?;
|
||||
let mut b = FheInt8::try_encrypt(clear_b, &keys)?;
|
||||
|
||||
let greater = a.gt(&b);
|
||||
let greater_or_equal = a.ge(&b);
|
||||
let lower = a.lt(&b);
|
||||
let lower_or_equal = a.le(&b);
|
||||
let equal = a.eq(&b);
|
||||
|
||||
let dec_gt: i8 = greater.decrypt(&keys);
|
||||
let dec_ge: i8 = greater_or_equal.decrypt(&keys);
|
||||
let dec_lt: i8 = lower.decrypt(&keys);
|
||||
let dec_le: i8 = lower_or_equal.decrypt(&keys);
|
||||
let dec_eq: i8 = equal.decrypt(&keys);
|
||||
|
||||
// We homomorphically swapped values using bitwise operations
|
||||
assert_eq!(dec_gt, (clear_a > clear_b ) as i8);
|
||||
assert_eq!(dec_ge, (clear_a >= clear_b) as i8);
|
||||
assert_eq!(dec_lt, (clear_a < clear_b ) as i8);
|
||||
assert_eq!(dec_le, (clear_a <= clear_b) as i8);
|
||||
assert_eq!(dec_eq, (clear_a == clear_b) as i8);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### Min/Max.
|
||||
|
||||
Homomorphic integers support the min/max operations.
|
||||
|
||||
| name | symbol | type |
|
||||
| ---- | ------ | ------ |
|
||||
| Min | `min` | Binary |
|
||||
| Max | `max` | Binary |
|
||||
|
||||
A simple example of how to use these operations:
|
||||
|
||||
```rust
|
||||
use tfhe::prelude::*;
|
||||
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = ConfigBuilder::all_disabled().enable_default_integers().build();
|
||||
let (keys, server_keys) = generate_keys(config);
|
||||
set_server_key(server_keys);
|
||||
|
||||
let clear_a:u8 = 164;
|
||||
let clear_b:u8 = 212;
|
||||
|
||||
let mut a = FheUint8::try_encrypt(clear_a, &keys)?;
|
||||
let mut b = FheUint8::try_encrypt(clear_b, &keys)?;
|
||||
|
||||
let min = a.min(&b);
|
||||
let max = a.max(&b);
|
||||
|
||||
let dec_min : u8 = min.decrypt(&keys);
|
||||
let dec_max : u8 = max.decrypt(&keys);
|
||||
|
||||
// We homomorphically swapped values using bitwise operations
|
||||
assert_eq!(dec_min, u8::min(clear_a, clear_b));
|
||||
assert_eq!(dec_max, u8::max(clear_a, clear_b));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### Ternary conditional operator.
|
||||
The ternary conditional operator allows computing conditional instructions of the form `if cond { choice_if } else { choice_else }`.
|
||||
|
||||
| name | symbol | type |
|
||||
|------------------|----------------|---------|
|
||||
| Ternary operator | `if_then_else` | Ternary |
|
||||
|
||||
The syntax is `encrypted_condition.if_then_else(encrypted_choice_if, encrypted_choice_else)`. The `encrypted_condition` should be an encryption of 0 or 1 in order to be valid.
|
||||
|
||||
```rust
|
||||
use tfhe::prelude::*;
|
||||
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheInt32};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Basic configuration to use homomorphic integers
|
||||
let config = ConfigBuilder::all_disabled()
|
||||
.enable_default_integers()
|
||||
.build();
|
||||
|
||||
// Key generation
|
||||
let (client_key, server_keys) = generate_keys(config);
|
||||
|
||||
let clear_a = 32i32;
|
||||
let clear_b = -45i32;
|
||||
|
||||
// Encrypting the input data using the (private) client_key
|
||||
// FheInt32: Encrypted equivalent to i32
|
||||
let encrypted_a = FheInt32::try_encrypt(clear_a, &client_key)?;
|
||||
let encrypted_b = FheInt32::try_encrypt(clear_b, &client_key)?;
|
||||
|
||||
// On the server side:
|
||||
set_server_key(server_keys);
|
||||
|
||||
// Clear equivalent computations: 32 > -45
|
||||
let encrypted_comp = &encrypted_a.gt(&encrypted_b);
|
||||
let clear_res: i32 = encrypted_comp.decrypt(&client_key);
|
||||
assert_eq!(clear_res, (clear_a > clear_b) as i32);
|
||||
|
||||
// `encrypted_comp` contains the result of the comparison, i.e.,
|
||||
// a boolean value. This acts as a condition on which the
|
||||
// `if_then_else` function can be applied on.
|
||||
// Clear equivalent computations:
|
||||
// if 32 > -45 {result = 32} else {result = -45}
|
||||
let encrypted_res = &encrypted_comp.if_then_else(&encrypted_a, &encrypted_b);
|
||||
|
||||
let clear_res: i32 = encrypted_res.decrypt(&client_key);
|
||||
assert_eq!(clear_res, clear_a);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
### Casting.
|
||||
Casting between integer types is possible via the `cast_from` associated function
|
||||
or the `cast_into` method.
|
||||
|
||||
```rust
|
||||
use tfhe::prelude::*;
|
||||
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheInt16, FheUint8, FheUint32, FheUint16};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = ConfigBuilder::all_disabled()
|
||||
.enable_default_integers()
|
||||
.build();
|
||||
let (client_key, server_key) = generate_keys(config);
|
||||
|
||||
// Casting requires server_key to set
|
||||
// (encryptions/decryptions do not need server_key to be set)
|
||||
set_server_key(server_key);
|
||||
|
||||
{
|
||||
let clear = 12_837u16;
|
||||
let a = FheUint16::encrypt(clear, &client_key);
|
||||
|
||||
// Downcasting
|
||||
let a: FheUint8 = a.cast_into();
|
||||
let da: u8 = a.decrypt(&client_key);
|
||||
assert_eq!(da, clear as u8);
|
||||
|
||||
// Upcasting
|
||||
let a: FheUint32 = a.cast_into();
|
||||
let da: u32 = a.decrypt(&client_key);
|
||||
assert_eq!(da, (clear as u8) as u32);
|
||||
}
|
||||
|
||||
{
|
||||
let clear = 12_837u16;
|
||||
let a = FheUint16::encrypt(clear, &client_key);
|
||||
|
||||
// Upcasting
|
||||
let a = FheUint32::cast_from(a);
|
||||
let da: u32 = a.decrypt(&client_key);
|
||||
assert_eq!(da, clear as u32);
|
||||
|
||||
// Downcasting
|
||||
let a = FheUint8::cast_from(a);
|
||||
let da: u8 = a.decrypt(&client_key);
|
||||
assert_eq!(da, (clear as u32) as u8);
|
||||
}
|
||||
|
||||
{
|
||||
let clear = 12_837i16;
|
||||
let a = FheInt16::encrypt(clear, &client_key);
|
||||
|
||||
// Casting from FheInt16 to FheUint16
|
||||
let a = FheUint16::cast_from(a);
|
||||
let da: u16 = a.decrypt(&client_key);
|
||||
assert_eq!(da, clear as u16);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
## ShortInt Operations
|
||||
|
||||
Native small homomorphic integer types (e.g., FheUint3 or FheUint4) easily compute various operations. In general, computing over encrypted data is as easy as computing over clear data, since the same operation symbol is used. The addition between two ciphertexts is done using the symbol `+` between two FheUint values. Many operations can be computed between a clear value (i.e. a scalar) and a ciphertext.
|
||||
Native small homomorphic integer types (e.g., FheUint3 or FheUint4) can easily compute various operations. In general, computing over encrypted data in `TFHE-rs` is as easy as computing over clear data, since the same operation symbol is used. The addition between two ciphertexts is done using the symbol `+` between two FheUint values. Many operations can be computed between a clear value (i.e. a scalar) and a ciphertext.
|
||||
|
||||
In Rust, operations on native types are modular. For example, computations on `u8` are carried out modulo $$2^{8}$$. A similar idea applies for FheUintX, where operations are done modulo $$2^{X}$$. For FheUint3, operations are done modulo $$8 = 2^{3}$$.
|
||||
|
||||
@@ -49,7 +388,7 @@ In Rust, operations on native types are modular. For example, computations on `u
|
||||
|
||||
Small homomorphic integer types support all common arithmetic operations, meaning `+`, `-`, `x`, `/`, `mod`.
|
||||
|
||||
The division operation implements a subtlety: since data is encrypted, it is possible to compute a division by 0. In this case, the division is tweaked so that dividing by 0 returns the max possible value for the message.
|
||||
The division operation presents a subtlety: since data is encrypted, it is possible to compute a division by 0. In this case, the division is tweaked so that dividing by 0 returns the maximum possible value for the message.
|
||||
|
||||
The list of supported operations is:
|
||||
|
||||
@@ -62,7 +401,7 @@ The list of supported operations is:
|
||||
| [Rem](https://doc.rust-lang.org/std/ops/trait.Rem.html) | `%` | Binary |
|
||||
| [Neg](https://doc.rust-lang.org/std/ops/trait.Neg.html) | `-` | Unary |
|
||||
|
||||
A simple example on how to use these operations:
|
||||
A simple example of how to use these operations:
|
||||
|
||||
```rust
|
||||
use tfhe::prelude::*;
|
||||
@@ -114,7 +453,7 @@ The list of supported operations is:
|
||||
| [Rotate Right](https://doc.rust-lang.org/std/primitive.u32.html#method.rotate_right) | `rotate_right` | Binary |
|
||||
| [Rotate Left](https://doc.rust-lang.org/std/primitive.u32.html#method.rotate_left) | `rotate_left` | Binary |
|
||||
|
||||
A simple example on how to use these operations:
|
||||
A simple example of how to use these operations:
|
||||
|
||||
```rust
|
||||
use tfhe::prelude::*;
|
||||
@@ -169,7 +508,7 @@ The list of supported operations is:
|
||||
| [Lower or Equal ](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html) | `le` | Binary |
|
||||
|
||||
|
||||
A simple example on how to use these operations:
|
||||
A simple example of how to use these operations:
|
||||
|
||||
```rust
|
||||
use tfhe::prelude::*;
|
||||
@@ -197,7 +536,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
||||
The shortint type also supports the computation of univariate functions, which make use of TFHE's _programmable bootstrapping_.
|
||||
|
||||
A simple example on how to use these operations:
|
||||
A simple example of how to use these operations:
|
||||
|
||||
```rust
|
||||
use tfhe::prelude::*;
|
||||
@@ -252,257 +591,17 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
```
|
||||
|
||||
## Integer
|
||||
|
||||
In TFHE-rs, integers are used to encrypt any messages larger than 4 bits. All supported operations are listed below.
|
||||
|
||||
### Arithmetic operations.
|
||||
## Boolean Operations
|
||||
|
||||
Homomorphic integer types support arithmetic operations.
|
||||
Native homomorphic Booleans support common Boolean operations.
|
||||
|
||||
The list of supported operations is:
|
||||
|
||||
| name | symbol | type |
|
||||
|----------------------------------------------------------|--------|--------|
|
||||
| [Neg](https://doc.rust-lang.org/std/ops/trait.Neg.html) | `-` | Unary |
|
||||
| [Add](https://doc.rust-lang.org/std/ops/trait.Add.html) | `+` | Binary |
|
||||
| [Sub](https://doc.rust-lang.org/std/ops/trait.Sub.html) | `-` | Binary |
|
||||
| [Mul](https://doc.rust-lang.org/std/ops/trait.Mul.html) | `*` | Binary |
|
||||
| [Div](https://doc.rust-lang.org/std/ops/trait.Div.html)* | `/` | Binary |
|
||||
| [Rem](https://doc.rust-lang.org/std/ops/trait.Rem.html)* | `%` | Binary |
|
||||
|
||||
For division by $$0$$, the convention is to return $$modulus - 1$$. For instance, for FheUint8, the modulus is $$2^8=256$$, so a division by $$0$$ will return an encryption of $$255$$.
|
||||
For the remainder operator, the convention is to return the first input without any modification. For instance, for $$ct1 = FheUint8(63)$$ and $$ct2 = FheUint8(0)$$, then $$ct1 % ct2$$ will return $$FheUint8(63)$$.
|
||||
|
||||
|
||||
|
||||
A simple example on how to use these operations:
|
||||
|
||||
```rust
|
||||
use tfhe::prelude::*;
|
||||
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = ConfigBuilder::all_disabled().enable_default_integers().build();
|
||||
let (keys, server_keys) = generate_keys(config);
|
||||
set_server_key(server_keys);
|
||||
|
||||
let clear_a = 15_u64;
|
||||
let clear_b = 27_u64;
|
||||
let clear_c = 43_u64;
|
||||
|
||||
let mut a = FheUint8::try_encrypt(clear_a, &keys)?;
|
||||
let mut b = FheUint8::try_encrypt(clear_b, &keys)?;
|
||||
let mut c = FheUint8::try_encrypt(clear_c, &keys)?;
|
||||
|
||||
|
||||
a = a * &b; // Clear equivalent computations: 15 * 27 mod 256 = 149
|
||||
b = &b + &c; // Clear equivalent computations: 27 + 43 mod 256 = 70
|
||||
b = b - 76u8; // Clear equivalent computations: 70 - 76 mod 256 = 250
|
||||
|
||||
let dec_a: u8 = a.decrypt(&keys);
|
||||
let dec_b: u8 = b.decrypt(&keys);
|
||||
|
||||
assert_eq!(dec_a, ((clear_a * clear_b) % 256_u64) as u8);
|
||||
assert_eq!(dec_b, (((clear_b + clear_c).wrapping_sub(76_u64)) % 256_u64) as u8);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### Bitwise operations.
|
||||
|
||||
Homomorphic integer types support some bitwise operations.
|
||||
|
||||
The list of supported operations is:
|
||||
|
||||
| name | symbol | type |
|
||||
|--------------------------------------------------------------------------------------|----------------|--------|
|
||||
| [Not](https://doc.rust-lang.org/std/ops/trait.Not.html) | `!` | Unary |
|
||||
| [BitAnd](https://doc.rust-lang.org/std/ops/trait.BitAnd.html) | `&` | Binary |
|
||||
| [BitOr](https://doc.rust-lang.org/std/ops/trait.BitOr.html) | `\|` | Binary |
|
||||
| [BitXor](https://doc.rust-lang.org/std/ops/trait.BitXor.html) | `^` | Binary |
|
||||
| [Shr](https://doc.rust-lang.org/std/ops/trait.Shr.html) | `>>` | Binary |
|
||||
| [Shl](https://doc.rust-lang.org/std/ops/trait.Shl.html) | `<<` | Binary |
|
||||
| [Rotate Right](https://doc.rust-lang.org/std/primitive.u32.html#method.rotate_right) | `rotate_right` | Binary |
|
||||
| [Rotate Left](https://doc.rust-lang.org/std/primitive.u32.html#method.rotate_left) | `rotate_left` | Binary |
|
||||
|
||||
A simple example on how to use these operations:
|
||||
|
||||
```rust
|
||||
use tfhe::prelude::*;
|
||||
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = ConfigBuilder::all_disabled().enable_default_integers().build();
|
||||
let (keys, server_keys) = generate_keys(config);
|
||||
set_server_key(server_keys);
|
||||
|
||||
let clear_a = 164;
|
||||
let clear_b = 212;
|
||||
|
||||
let mut a = FheUint8::try_encrypt(clear_a, &keys)?;
|
||||
let mut b = FheUint8::try_encrypt(clear_b, &keys)?;
|
||||
|
||||
a = a ^ &b;
|
||||
b = b ^ &a;
|
||||
a = a ^ &b;
|
||||
|
||||
let dec_a: u8 = a.decrypt(&keys);
|
||||
let dec_b: u8 = b.decrypt(&keys);
|
||||
|
||||
// We homomorphically swapped values using bitwise operations
|
||||
assert_eq!(dec_a, clear_b);
|
||||
assert_eq!(dec_b, clear_a);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### Comparisons.
|
||||
|
||||
Homomorphic integers support comparison operations. Since Rust does not allow the overloading of these operations, a simple function has been associated to each one.
|
||||
|
||||
The list of supported operations is:
|
||||
|
||||
| name | symbol | type |
|
||||
|-----------------------------------------------------------------------------|--------|--------|
|
||||
| [Equal ](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html) | `eq` | Binary |
|
||||
| [Not Equal ](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html) | `ne` | Binary |
|
||||
| [Greater Than ](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html) | `gt` | Binary |
|
||||
| [Greater or Equal](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html) | `ge` | Binary |
|
||||
| [Lower ](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html) | `lt` | Binary |
|
||||
| [Lower or Equal ](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html) | `le` | Binary |
|
||||
|
||||
A simple example on how to use these operations:
|
||||
|
||||
```rust
|
||||
use tfhe::prelude::*;
|
||||
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = ConfigBuilder::all_disabled().enable_default_integers().build();
|
||||
let (keys, server_keys) = generate_keys(config);
|
||||
set_server_key(server_keys);
|
||||
|
||||
let clear_a:u8 = 164;
|
||||
let clear_b:u8 = 212;
|
||||
|
||||
let mut a = FheUint8::try_encrypt(clear_a, &keys)?;
|
||||
let mut b = FheUint8::try_encrypt(clear_b, &keys)?;
|
||||
|
||||
let greater = a.gt(&b);
|
||||
let greater_or_equal = a.ge(&b);
|
||||
let lower = a.lt(&b);
|
||||
let lower_or_equal = a.le(&b);
|
||||
let equal = a.eq(&b);
|
||||
|
||||
let dec_gt : u8 = greater.decrypt(&keys);
|
||||
let dec_ge : u8 = greater_or_equal.decrypt(&keys);
|
||||
let dec_lt : u8 = lower.decrypt(&keys);
|
||||
let dec_le : u8 = lower_or_equal.decrypt(&keys);
|
||||
let dec_eq : u8 = equal.decrypt(&keys);
|
||||
|
||||
// We homomorphically swapped values using bitwise operations
|
||||
assert_eq!(dec_gt, (clear_a > clear_b ) as u8);
|
||||
assert_eq!(dec_ge, (clear_a >= clear_b) as u8);
|
||||
assert_eq!(dec_lt, (clear_a < clear_b ) as u8);
|
||||
assert_eq!(dec_le, (clear_a <= clear_b) as u8);
|
||||
assert_eq!(dec_eq, (clear_a == clear_b) as u8);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### Min/Max.
|
||||
|
||||
Homomorphic integers support the min/max operations.
|
||||
|
||||
| name | symbol | type |
|
||||
| ---- | ------ | ------ |
|
||||
| Min | `min` | Binary |
|
||||
| Max | `max` | Binary |
|
||||
|
||||
A simple example on how to use these operations:
|
||||
|
||||
```rust
|
||||
use tfhe::prelude::*;
|
||||
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = ConfigBuilder::all_disabled().enable_default_integers().build();
|
||||
let (keys, server_keys) = generate_keys(config);
|
||||
set_server_key(server_keys);
|
||||
|
||||
let clear_a:u8 = 164;
|
||||
let clear_b:u8 = 212;
|
||||
|
||||
let mut a = FheUint8::try_encrypt(clear_a, &keys)?;
|
||||
let mut b = FheUint8::try_encrypt(clear_b, &keys)?;
|
||||
|
||||
let min = a.min(&b);
|
||||
let max = a.max(&b);
|
||||
|
||||
let dec_min : u8 = min.decrypt(&keys);
|
||||
let dec_max : u8 = max.decrypt(&keys);
|
||||
|
||||
// We homomorphically swapped values using bitwise operations
|
||||
assert_eq!(dec_min, u8::min(clear_a, clear_b));
|
||||
assert_eq!(dec_max, u8::max(clear_a, clear_b));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### Casting.
|
||||
|
||||
Casting between integer types is possible via the `cast_from` associated function
|
||||
or the `cast_into` method.
|
||||
|
||||
```rust
|
||||
use tfhe::prelude::*;
|
||||
use tfhe::{generate_keys, set_server_key, ConfigBuilder, FheUint8, FheUint32, FheUint16};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let config = ConfigBuilder::all_disabled()
|
||||
.enable_default_integers()
|
||||
.build();
|
||||
let (client_key, server_key) = generate_keys(config);
|
||||
|
||||
// Casting requires server_key to set
|
||||
// (encryptions/decryptions do not need server_key to be set)
|
||||
set_server_key(server_key);
|
||||
|
||||
{
|
||||
let clear = 12_837u16;
|
||||
let a = FheUint16::encrypt(clear, &client_key);
|
||||
|
||||
// Downcasting
|
||||
let a: FheUint8 = a.cast_into();
|
||||
let da: u8 = a.decrypt(&client_key);
|
||||
assert_eq!(da, clear as u8);
|
||||
|
||||
// Upcasting
|
||||
let a: FheUint32 = a.cast_into();
|
||||
let da: u32 = a.decrypt(&client_key);
|
||||
assert_eq!(da, (clear as u8) as u32);
|
||||
}
|
||||
|
||||
{
|
||||
let clear = 12_837u16;
|
||||
let a = FheUint16::encrypt(clear, &client_key);
|
||||
|
||||
// Upcasting
|
||||
let a = FheUint32::cast_from(a);
|
||||
let da: u32 = a.decrypt(&client_key);
|
||||
assert_eq!(da, clear as u32);
|
||||
|
||||
// Downcasting
|
||||
let a = FheUint8::cast_from(a);
|
||||
let da: u8 = a.decrypt(&client_key);
|
||||
assert_eq!(da, (clear as u32) as u8);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
| name | symbol | type |
|
||||
| ------------------------------------------------------------- | ------ | ------ |
|
||||
| [BitAnd](https://doc.rust-lang.org/std/ops/trait.BitAnd.html) | `&` | Binary |
|
||||
| [BitOr](https://doc.rust-lang.org/std/ops/trait.BitOr.html) | `\|` | Binary |
|
||||
| [BitXor](https://doc.rust-lang.org/std/ops/trait.BitXor.html) | `^` | Binary |
|
||||
| [Not](https://doc.rust-lang.org/std/ops/trait.Not.html) | `!` | Unary |
|
||||
|
||||
@@ -139,6 +139,3 @@ let clear_result = clear_a + clear_b;
|
||||
|
||||
assert_eq!(decrypted_result, clear_result);
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ The figure below illustrates this problem in the case of an addition, where an e
|
||||
|
||||
### Programmable BootStrapping (PBS)
|
||||
|
||||
The bootstrapping of TFHE has the particularity of being programmable: this means that any function can be homomorphically computed over an encrypted input, while also reducing the noise. These functions are represented by look-up tables. The computation of a PBS is in general either preceded or followed by a keyswitch, which is an operation used to change the encryption key. The output ciphertext is then encrypted with the same key as the input one. To do this, two (public) evaluation keys are required: a boostrapping key and a keyswitching key. These operations are quite complex to describe, more information about these operations (or about TFHE in general) can be found here [TFHE Deep Dive](https://www.zama.ai/post/tfhe-deep-dive-part-1).
|
||||
The bootstrapping of TFHE has the particularity of being programmable: this means that any function can be homomorphically computed over an encrypted input, while also reducing the noise. These functions are represented by look-up tables. The computation of a PBS is in general either preceded or followed by a keyswitch, which is an operation used to change the encryption key. The output ciphertext is then encrypted with the same key as the input one. To do this, two (public) evaluation keys are required: a bootstrapping key and a keyswitching key. These operations are quite complex to describe, more information about these operations (or about TFHE in general) can be found here [TFHE Deep Dive](https://www.zama.ai/post/tfhe-deep-dive-part-1).
|
||||
|
||||
|
||||
### Carry.
|
||||
|
||||
@@ -84,7 +84,7 @@ fn main() {
|
||||
This example shows how to compress the classical public keys.
|
||||
|
||||
{% hint style="warning" %}
|
||||
It is not currently recommended to use the CompressedPublicKey to encrypt ciphertexts without first decompressing it. In case the resulting PublicKey is too large to fit in memory the encryption with the CompressedPublicKey will be very slow, this is a known problem and will be adressed in future releases.
|
||||
It is not currently recommended to use the CompressedPublicKey to encrypt ciphertexts without first decompressing it. In case the resulting PublicKey is too large to fit in memory the encryption with the CompressedPublicKey will be very slow, this is a known problem and will be addressed in future releases.
|
||||
{% endhint %}
|
||||
|
||||
```rust
|
||||
|
||||
@@ -63,6 +63,3 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -49,4 +49,3 @@ fn main() {
|
||||
assert_eq!(clear, 255u8);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Using the right toolchain for TFHE-rs.
|
||||
|
||||
TFHE-rs only requires a nighlty toolchain for building the C API and using advanced SIMD instructions, otherwise you can use a stable toolchain (with version >= 1.72 for ARM devices)
|
||||
TFHE-rs only requires a nightly toolchain for building the C API and using advanced SIMD instructions, otherwise you can use a stable toolchain (with version >= 1.72)
|
||||
Install the needed Rust toolchain:
|
||||
|
||||
```shell
|
||||
@@ -27,7 +27,7 @@ cargo +nightly test
|
||||
|
||||
```shell
|
||||
# This should not be necessary by default, but if you want to make sure your configuration is
|
||||
# correct you can still set the overriden toolchain to stable
|
||||
# correct you can still set the overridden toolchain to stable
|
||||
rustup override set stable
|
||||
# cargo will use the `stable` toolchain.
|
||||
cargo build
|
||||
@@ -52,11 +52,11 @@ rustup show
|
||||
|
||||
This crate exposes two kinds of data types. Each kind is enabled by activating its corresponding feature in the TOML line. Each kind may have multiple types:
|
||||
|
||||
| Kind | Features | Type(s) |
|
||||
| --------- | ---------- | --------------------------------- |
|
||||
| Booleans | `boolean` | Booleans |
|
||||
| ShortInts | `shortint` | Short unsigned integers |
|
||||
| Integers | `integer` | Arbitrary-sized unsigned integers |
|
||||
| Kind | Features | Type(s) |
|
||||
|-----------|------------|---------------------------|
|
||||
| Booleans | `boolean` | Booleans |
|
||||
| ShortInts | `shortint` | Short integers |
|
||||
| Integers | `integer` | Arbitrary-sized integers |
|
||||
|
||||
|
||||
## AVX-512
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Trival Ciphertext
|
||||
# Trivial Ciphertext
|
||||
|
||||
Sometimes, the server side needs to initialize a value.
|
||||
For example, when computing the sum of a list of ciphertext,
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
use clap::{Arg, ArgAction, Command};
|
||||
use tfhe::boolean;
|
||||
use tfhe::boolean::parameters::{
|
||||
BooleanParameters, DEFAULT_PARAMETERS, DEFAULT_PARAMETERS_KS_PBS,
|
||||
PARAMETERS_ERROR_PROB_2_POW_MINUS_165, PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS,
|
||||
TFHE_LIB_PARAMETERS,
|
||||
};
|
||||
use tfhe::boolean::parameters::{BooleanParameters, DEFAULT_PARAMETERS, DEFAULT_PARAMETERS_KS_PBS};
|
||||
use tfhe::keycache::NamedParam;
|
||||
use tfhe::shortint::keycache::{KEY_CACHE, KEY_CACHE_KSK, KEY_CACHE_WOPBS};
|
||||
use tfhe::shortint::parameters::key_switching::{
|
||||
@@ -97,13 +93,8 @@ fn client_server_keys() {
|
||||
)];
|
||||
generate_wopbs_keys(&WOPBS_PARAMS);
|
||||
|
||||
const BOOLEAN_PARAMS: [BooleanParameters; 5] = [
|
||||
DEFAULT_PARAMETERS,
|
||||
DEFAULT_PARAMETERS_KS_PBS,
|
||||
TFHE_LIB_PARAMETERS,
|
||||
PARAMETERS_ERROR_PROB_2_POW_MINUS_165,
|
||||
PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS,
|
||||
];
|
||||
const BOOLEAN_PARAMS: [BooleanParameters; 2] =
|
||||
[DEFAULT_PARAMETERS, DEFAULT_PARAMETERS_KS_PBS];
|
||||
generate_boolean_keys(&BOOLEAN_PARAMS);
|
||||
} else {
|
||||
generate_pbs_keys(&ALL_PARAMETER_VEC);
|
||||
|
||||
623
tfhe/js_on_wasm_tests/test-hlapi-signed.js
Normal file
623
tfhe/js_on_wasm_tests/test-hlapi-signed.js
Normal file
@@ -0,0 +1,623 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert').strict;
|
||||
const { performance } = require('perf_hooks');
|
||||
const {
|
||||
init_panic_hook,
|
||||
ShortintParametersName,
|
||||
ShortintParameters,
|
||||
TfheClientKey,
|
||||
TfhePublicKey,
|
||||
TfheCompressedPublicKey,
|
||||
TfheCompactPublicKey,
|
||||
TfheCompressedServerKey,
|
||||
TfheConfigBuilder,
|
||||
CompressedFheInt8,
|
||||
FheInt8,
|
||||
FheInt32,
|
||||
CompactFheInt32,
|
||||
CompactFheInt32List,
|
||||
CompressedFheInt128,
|
||||
FheInt128,
|
||||
CompressedFheInt256,
|
||||
CompactFheInt256,
|
||||
CompactFheInt256List,
|
||||
FheInt256
|
||||
} = require("../pkg/tfhe.js");
|
||||
|
||||
|
||||
const I256_MIN = BigInt("-57896044618658097711785492504343953926634992332820282019728792003956564819968");
|
||||
const I256_MAX = BigInt("28948022309329048855892746252171976963317496166410141009864396001978282409983");
|
||||
const I128_MIN = BigInt("-170141183460469231731687303715884105728");
|
||||
const I32_MIN = -2147483648;
|
||||
|
||||
// This is useful to debug test
|
||||
init_panic_hook();
|
||||
|
||||
|
||||
test('hlapi_client_key_encrypt_decrypt_int8_big', (t) => {
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_default_integers()
|
||||
.build();
|
||||
|
||||
let clientKey = TfheClientKey.generate(config);
|
||||
|
||||
let clear = -73;
|
||||
let encrypted = FheInt8.encrypt_with_client_key(clear, clientKey);
|
||||
let decrypted = encrypted.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, clear);
|
||||
|
||||
let serialized = encrypted.serialize();
|
||||
let deserialized = FheInt8.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, clear);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = FheInt8.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, clear);
|
||||
});
|
||||
|
||||
test('hlapi_compressed_public_client_int8_big', (t) => {
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_default_integers()
|
||||
.build();
|
||||
|
||||
let clientKey = TfheClientKey.generate(config);
|
||||
|
||||
let clear = -128;
|
||||
let compressed_encrypted = CompressedFheInt8.encrypt_with_client_key(clear, clientKey);
|
||||
let compressed_serialized = compressed_encrypted.serialize();
|
||||
let compressed_deserialized = CompressedFheInt8.deserialize(compressed_serialized);
|
||||
let decompressed = compressed_deserialized.decompress()
|
||||
|
||||
let decrypted = decompressed.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, clear);
|
||||
|
||||
let compressed_safe_serialized = compressed_encrypted.safe_serialize(BigInt(10000000));
|
||||
let compressed_safe_deserialized = CompressedFheInt8.safe_deserialize(compressed_safe_serialized, BigInt(10000000));
|
||||
let safe_decompressed = compressed_safe_deserialized.decompress()
|
||||
|
||||
let safe_decrypted = safe_decompressed.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_decrypted, clear);
|
||||
});
|
||||
|
||||
test('hlapi_public_key_encrypt_decrypt_int32_small', (t) => {
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_default_integers_small()
|
||||
.build();
|
||||
|
||||
|
||||
let clientKey = TfheClientKey.generate(config);
|
||||
let publicKey = TfhePublicKey.new(clientKey);
|
||||
|
||||
let encrypted = FheInt32.encrypt_with_public_key(I32_MIN, publicKey);
|
||||
let decrypted = encrypted.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, I32_MIN);
|
||||
|
||||
let serialized = encrypted.serialize();
|
||||
let deserialized = FheInt32.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, I32_MIN);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(1000000));
|
||||
let safe_deserialized = FheInt32.safe_deserialize(safe_serialized, BigInt(1000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, I32_MIN);
|
||||
});
|
||||
|
||||
test('hlapi_decompress_public_key_then_encrypt_decrypt_int32_small', (t) => {
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_default_integers_small()
|
||||
.build();
|
||||
|
||||
|
||||
let clientKey = TfheClientKey.generate(config);
|
||||
var startTime = performance.now()
|
||||
let compressedPublicKey = TfheCompressedPublicKey.new(clientKey);
|
||||
var endTime = performance.now()
|
||||
|
||||
let data = compressedPublicKey.serialize()
|
||||
|
||||
let publicKey = compressedPublicKey.decompress();
|
||||
|
||||
|
||||
var startTime = performance.now()
|
||||
let encrypted = FheInt32.encrypt_with_public_key(I32_MIN, publicKey);
|
||||
var endTime = performance.now()
|
||||
|
||||
let ser = encrypted.serialize();
|
||||
let decrypted = encrypted.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, I32_MIN);
|
||||
|
||||
let serialized = encrypted.serialize();
|
||||
let deserialized = FheInt32.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, I32_MIN);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = FheInt32.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, I32_MIN);
|
||||
});
|
||||
|
||||
test('hlapi_client_key_encrypt_decrypt_int128_big', (t) => {
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_default_integers()
|
||||
.build();
|
||||
|
||||
let clientKey = TfheClientKey.generate(config);
|
||||
|
||||
let encrypted = FheInt128.encrypt_with_client_key(I128_MIN, clientKey);
|
||||
let decrypted = encrypted.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, I128_MIN);
|
||||
|
||||
let serialized = encrypted.serialize();
|
||||
let deserialized = FheInt128.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, I128_MIN);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = FheInt128.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, I128_MIN);
|
||||
|
||||
// Compressed
|
||||
let compressed_encrypted = CompressedFheInt128.encrypt_with_client_key(I128_MIN, clientKey);
|
||||
let compressed_serialized = compressed_encrypted.serialize();
|
||||
let compressed_deserialized = CompressedFheInt128.deserialize(compressed_serialized);
|
||||
let decompressed = compressed_deserialized.decompress()
|
||||
|
||||
decrypted = decompressed.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, I128_MIN);
|
||||
|
||||
let compressed_safe_serialized = compressed_encrypted.safe_serialize(BigInt(10000000));
|
||||
let compressed_safe_deserialized = CompressedFheInt128.safe_deserialize(compressed_safe_serialized, BigInt(10000000));
|
||||
let safe_decompressed = compressed_safe_deserialized.decompress()
|
||||
|
||||
safe_decrypted = safe_decompressed.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_decrypted, I128_MIN);
|
||||
});
|
||||
|
||||
test('hlapi_client_key_encrypt_decrypt_int128_small', (t) => {
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_default_integers_small()
|
||||
.build();
|
||||
|
||||
|
||||
let clientKey = TfheClientKey.generate(config);
|
||||
|
||||
let encrypted = FheInt128.encrypt_with_client_key(I128_MIN, clientKey);
|
||||
let decrypted = encrypted.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, I128_MIN);
|
||||
|
||||
let serialized = encrypted.serialize();
|
||||
let deserialized = FheInt128.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, I128_MIN);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = FheInt128.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, I128_MIN);
|
||||
|
||||
// Compressed
|
||||
let compressed_encrypted = CompressedFheInt128.encrypt_with_client_key(I128_MIN, clientKey);
|
||||
let compressed_serialized = compressed_encrypted.serialize();
|
||||
let compressed_deserialized = CompressedFheInt128.deserialize(compressed_serialized);
|
||||
let decompressed = compressed_deserialized.decompress()
|
||||
|
||||
decrypted = decompressed.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, I128_MIN);
|
||||
|
||||
let compressed_safe_serialized = compressed_encrypted.safe_serialize(BigInt(10000000));
|
||||
let compressed_safe_deserialized = CompressedFheInt128.safe_deserialize(compressed_safe_serialized, BigInt(10000000));
|
||||
let safe_decompressed = compressed_safe_deserialized.decompress()
|
||||
|
||||
safe_decrypted = safe_decompressed.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_decrypted, I128_MIN);
|
||||
});
|
||||
|
||||
test('hlapi_client_key_encrypt_decrypt_int256_big', (t) => {
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_default_integers()
|
||||
.build();
|
||||
|
||||
|
||||
let clientKey = TfheClientKey.generate(config);
|
||||
|
||||
|
||||
let round_trip_encrypt = (value) => {
|
||||
let encrypted = FheInt256.encrypt_with_client_key(value, clientKey);
|
||||
let decrypted = encrypted.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, value);
|
||||
|
||||
let serialized = encrypted.serialize();
|
||||
let deserialized = FheInt256.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, value);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = FheInt256.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, value);
|
||||
|
||||
// Compressed
|
||||
let compressed_encrypted = CompressedFheInt256.encrypt_with_client_key(value, clientKey);
|
||||
let compressed_serialized = compressed_encrypted.serialize();
|
||||
let compressed_deserialized = CompressedFheInt256.deserialize(compressed_serialized);
|
||||
let decompressed = compressed_deserialized.decompress();
|
||||
|
||||
decrypted = decompressed.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, value);
|
||||
|
||||
let compressed_safe_serialized = compressed_encrypted.safe_serialize(BigInt(10000000));
|
||||
let compressed_safe_deserialized = CompressedFheInt256.safe_deserialize(compressed_safe_serialized, BigInt(10000000));
|
||||
let safe_decompressed = compressed_safe_deserialized.decompress()
|
||||
|
||||
safe_decrypted = safe_decompressed.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_decrypted, value);
|
||||
};
|
||||
|
||||
|
||||
round_trip_encrypt(I256_MIN);
|
||||
round_trip_encrypt(I256_MAX);
|
||||
round_trip_encrypt(BigInt(-1));
|
||||
round_trip_encrypt(BigInt(1));
|
||||
round_trip_encrypt(BigInt(-128));
|
||||
round_trip_encrypt(BigInt(128));
|
||||
});
|
||||
|
||||
test('hlapi_client_key_encrypt_decrypt_int256_small', (t) => {
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_default_integers_small()
|
||||
.build();
|
||||
|
||||
|
||||
let clientKey = TfheClientKey.generate(config);
|
||||
|
||||
let round_trip_encrypt = (value) => {
|
||||
let encrypted = FheInt256.encrypt_with_client_key(value, clientKey);
|
||||
let decrypted = encrypted.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, value);
|
||||
|
||||
let serialized = encrypted.serialize();
|
||||
let deserialized = FheInt256.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, value);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = FheInt256.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, value);
|
||||
|
||||
// Compressed
|
||||
let compressed_encrypted = CompressedFheInt256.encrypt_with_client_key(value, clientKey);
|
||||
let compressed_serialized = compressed_encrypted.serialize();
|
||||
let compressed_deserialized = CompressedFheInt256.deserialize(compressed_serialized);
|
||||
let decompressed = compressed_deserialized.decompress()
|
||||
|
||||
decrypted = decompressed.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, value);
|
||||
|
||||
let compressed_safe_serialized = compressed_encrypted.safe_serialize(BigInt(10000000));
|
||||
let compressed_safe_deserialized = CompressedFheInt256.safe_deserialize(compressed_safe_serialized, BigInt(10000000));
|
||||
let safe_decompressed = compressed_safe_deserialized.decompress()
|
||||
|
||||
safe_decrypted = safe_decompressed.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_decrypted, value);
|
||||
}
|
||||
|
||||
round_trip_encrypt(I256_MIN);
|
||||
round_trip_encrypt(I256_MAX);
|
||||
round_trip_encrypt(BigInt(-1));
|
||||
round_trip_encrypt(BigInt(1));
|
||||
round_trip_encrypt(BigInt(-128));
|
||||
round_trip_encrypt(BigInt(128));
|
||||
});
|
||||
|
||||
test('hlapi_decompress_public_key_then_encrypt_decrypt_int256_small', (t) => {
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_default_integers_small()
|
||||
.build();
|
||||
|
||||
|
||||
let clientKey = TfheClientKey.generate(config);
|
||||
let compressedPublicKey = TfheCompressedPublicKey.new(clientKey);
|
||||
let publicKey = compressedPublicKey.decompress();
|
||||
|
||||
|
||||
let encrypted = FheInt256.encrypt_with_public_key(I256_MIN, publicKey);
|
||||
let decrypted = encrypted.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, I256_MIN);
|
||||
|
||||
let serialized = encrypted.serialize();
|
||||
let deserialized = FheInt256.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, I256_MIN);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = FheInt256.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, I256_MIN);
|
||||
});
|
||||
|
||||
test('hlapi_public_key_encrypt_decrypt_int256_small', (t) => {
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_default_integers_small()
|
||||
.build();
|
||||
|
||||
|
||||
let clientKey = TfheClientKey.generate(config);
|
||||
let publicKey = TfhePublicKey.new(clientKey);
|
||||
|
||||
let encrypted = FheInt256.encrypt_with_public_key(I256_MIN, publicKey);
|
||||
let decrypted = encrypted.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, I256_MIN);
|
||||
|
||||
let serialized = encrypted.serialize();
|
||||
let deserialized = FheInt256.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, I256_MIN);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = FheInt256.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, I256_MIN);
|
||||
});
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// 32 bits compact
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function hlapi_compact_public_key_encrypt_decrypt_int32_single(config) {
|
||||
let clientKey = TfheClientKey.generate(config);
|
||||
let publicKey = TfheCompactPublicKey.new(clientKey);
|
||||
|
||||
let encrypted = FheInt32.encrypt_with_compact_public_key(I32_MIN, publicKey);
|
||||
let decrypted = encrypted.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, I32_MIN);
|
||||
|
||||
let serialized = encrypted.serialize();
|
||||
let deserialized = FheInt32.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, I32_MIN);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = FheInt32.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, I32_MIN);
|
||||
}
|
||||
|
||||
test('hlapi_compact_public_key_encrypt_decrypt_int32_big_single', (t) => {
|
||||
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS);
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_custom_integers(block_params)
|
||||
.build();
|
||||
|
||||
hlapi_compact_public_key_encrypt_decrypt_int32_single(config);
|
||||
});
|
||||
|
||||
test('hlapi_compact_public_key_encrypt_decrypt_int32_small_single', (t) => {
|
||||
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS);
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_custom_integers(block_params)
|
||||
.build();
|
||||
|
||||
hlapi_compact_public_key_encrypt_decrypt_int32_single(config);
|
||||
});
|
||||
|
||||
function hlapi_compact_public_key_encrypt_decrypt_int32_single_compact(config) {
|
||||
let clientKey = TfheClientKey.generate(config);
|
||||
let publicKey = TfheCompactPublicKey.new(clientKey);
|
||||
|
||||
let compact_encrypted = CompactFheInt32.encrypt_with_compact_public_key(I32_MIN, publicKey);
|
||||
let encrypted = compact_encrypted.expand();
|
||||
let decrypted = encrypted.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, I32_MIN);
|
||||
|
||||
let serialized = compact_encrypted.serialize();
|
||||
let deserialized = CompactFheInt32.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.expand().decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, I32_MIN);
|
||||
|
||||
let safe_serialized = compact_encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = CompactFheInt32.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.expand().decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, I32_MIN);
|
||||
}
|
||||
|
||||
test('hlapi_compact_public_key_encrypt_decrypt_int32_small_single_compact', (t) => {
|
||||
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS);
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_custom_integers(block_params)
|
||||
.build();
|
||||
|
||||
hlapi_compact_public_key_encrypt_decrypt_int32_single_compact(config);
|
||||
});
|
||||
|
||||
test('hlapi_compact_public_key_encrypt_decrypt_int32_big_single_compact', (t) => {
|
||||
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS);
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_custom_integers(block_params)
|
||||
.build();
|
||||
|
||||
hlapi_compact_public_key_encrypt_decrypt_int32_single_compact(config);
|
||||
});
|
||||
|
||||
function hlapi_compact_public_key_encrypt_decrypt_int32_list_compact(config) {
|
||||
let clientKey = TfheClientKey.generate(config);
|
||||
let publicKey = TfheCompactPublicKey.new(clientKey);
|
||||
|
||||
let values = [0, 1, 2394, I32_MIN];
|
||||
|
||||
let compact_list = CompactFheInt32List.encrypt_with_compact_public_key(values, publicKey);
|
||||
|
||||
{
|
||||
let encrypted_list = compact_list.expand();
|
||||
|
||||
assert.deepStrictEqual(encrypted_list.length, values.length);
|
||||
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
let decrypted = encrypted_list[i].decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
let serialized_list = compact_list.serialize();
|
||||
let deserialized_list = CompactFheInt32List.deserialize(serialized_list);
|
||||
let encrypted_list = deserialized_list.expand();
|
||||
assert.deepStrictEqual(encrypted_list.length, values.length);
|
||||
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
let decrypted = encrypted_list[i].decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
test('hlapi_compact_public_key_encrypt_decrypt_int32_small_list_compact', (t) => {
|
||||
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS);
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_custom_integers(block_params)
|
||||
.build();
|
||||
|
||||
hlapi_compact_public_key_encrypt_decrypt_int32_list_compact(config);
|
||||
});
|
||||
|
||||
test('hlapi_compact_public_key_encrypt_decrypt_int32_big_list_compact', (t) => {
|
||||
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS);
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_custom_integers(block_params)
|
||||
.build();
|
||||
|
||||
hlapi_compact_public_key_encrypt_decrypt_int32_list_compact(config);
|
||||
});
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// 256 bits compact
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function hlapi_compact_public_key_encrypt_decrypt_int256_single(config) {
|
||||
let clientKey = TfheClientKey.generate(config);
|
||||
let publicKey = TfheCompactPublicKey.new(clientKey);
|
||||
|
||||
let encrypted = FheInt256.encrypt_with_compact_public_key(I256_MIN, publicKey);
|
||||
let decrypted = encrypted.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, I256_MIN);
|
||||
|
||||
let serialized = encrypted.serialize();
|
||||
let deserialized = FheInt256.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, I256_MIN);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = FheInt256.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, I256_MIN);
|
||||
}
|
||||
|
||||
test('hlapi_compact_public_key_encrypt_decrypt_int256_big_single', (t) => {
|
||||
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS);
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_custom_integers(block_params)
|
||||
.build();
|
||||
|
||||
hlapi_compact_public_key_encrypt_decrypt_int256_single(config);
|
||||
});
|
||||
|
||||
test('hlapi_compact_public_key_encrypt_decrypt_int256_small_single', (t) => {
|
||||
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS);
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_custom_integers(block_params)
|
||||
.build();
|
||||
|
||||
hlapi_compact_public_key_encrypt_decrypt_int256_single(config);
|
||||
});
|
||||
|
||||
function hlapi_compact_public_key_encrypt_decrypt_int256_single_compact(config) {
|
||||
let clientKey = TfheClientKey.generate(config);
|
||||
let publicKey = TfheCompactPublicKey.new(clientKey);
|
||||
|
||||
let compact_encrypted = CompactFheInt256.encrypt_with_compact_public_key(I256_MIN, publicKey);
|
||||
let encrypted = compact_encrypted.expand();
|
||||
let decrypted = encrypted.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, I256_MIN);
|
||||
|
||||
let serialized = compact_encrypted.serialize();
|
||||
let deserialized = CompactFheInt256.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.expand().decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, I256_MIN);
|
||||
|
||||
let safe_serialized = compact_encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = CompactFheInt256.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.expand().decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, I256_MIN);
|
||||
}
|
||||
|
||||
test('hlapi_compact_public_key_encrypt_decrypt_int256_small_single_compact', (t) => {
|
||||
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS);
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_custom_integers(block_params)
|
||||
.build();
|
||||
|
||||
hlapi_compact_public_key_encrypt_decrypt_int256_single_compact(config);
|
||||
});
|
||||
|
||||
test('hlapi_compact_public_key_encrypt_decrypt_int256_big_single_compact', (t) => {
|
||||
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS);
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_custom_integers(block_params)
|
||||
.build();
|
||||
|
||||
hlapi_compact_public_key_encrypt_decrypt_int256_single_compact(config);
|
||||
});
|
||||
|
||||
function hlapi_compact_public_key_encrypt_decrypt_int256_list_compact(config) {
|
||||
let clientKey = TfheClientKey.generate(config);
|
||||
let publicKey = TfheCompactPublicKey.new(clientKey);
|
||||
|
||||
let values = [BigInt(0), BigInt(1), BigInt(2394), BigInt(-2309840239), BigInt(I32_MIN), I256_MIN, I128_MIN];
|
||||
|
||||
let compact_list = CompactFheInt256List.encrypt_with_compact_public_key(values, publicKey);
|
||||
|
||||
{
|
||||
let encrypted_list = compact_list.expand();
|
||||
|
||||
assert.deepStrictEqual(encrypted_list.length, values.length);
|
||||
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
let decrypted = encrypted_list[i].decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
let serialized_list = compact_list.serialize();
|
||||
let deserialized_list = CompactFheInt256List.deserialize(serialized_list);
|
||||
let encrypted_list = deserialized_list.expand();
|
||||
assert.deepStrictEqual(encrypted_list.length, values.length);
|
||||
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
let decrypted = encrypted_list[i].decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, values[i]);
|
||||
}
|
||||
}
|
||||
|
||||
test('hlapi_compact_public_key_encrypt_decrypt_int256_small_list_compact', (t) => {
|
||||
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_PBS_KS);
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_custom_integers(block_params)
|
||||
.build();
|
||||
|
||||
hlapi_compact_public_key_encrypt_decrypt_int256_list_compact(config);
|
||||
});
|
||||
|
||||
test('hlapi_compact_public_key_encrypt_decrypt_int256_big_list_compact', (t) => {
|
||||
const block_params = new ShortintParameters(ShortintParametersName.PARAM_MESSAGE_2_CARRY_2_COMPACT_PK_KS_PBS);
|
||||
let config = TfheConfigBuilder.all_disabled()
|
||||
.enable_custom_integers(block_params)
|
||||
.build();
|
||||
|
||||
hlapi_compact_public_key_encrypt_decrypt_int256_list_compact(config);
|
||||
});
|
||||
@@ -29,9 +29,9 @@ const U256_MAX = BigInt("1157920892373161954235709850086879078532699846656405640
|
||||
const U128_MAX = BigInt("340282366920938463463374607431768211455");
|
||||
const U32_MAX = 4294967295;
|
||||
|
||||
// This use full to debug test
|
||||
//
|
||||
// Note that the test hlapi_panic
|
||||
// This is useful to debug test
|
||||
//
|
||||
// Note that the test hlapi_panic
|
||||
// purposefully creates a panic, to some panic message
|
||||
// will be printed and tess will be ok
|
||||
init_panic_hook();
|
||||
@@ -108,6 +108,11 @@ test('hlapi_client_key_encrypt_decrypt_uint8_big', (t) => {
|
||||
let deserialized = FheUint8.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, clear);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = FheUint8.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, clear);
|
||||
});
|
||||
|
||||
test('hlapi_compressed_public_client_uint8_big', (t) => {
|
||||
@@ -125,6 +130,13 @@ test('hlapi_compressed_public_client_uint8_big', (t) => {
|
||||
|
||||
let decrypted = decompressed.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, clear);
|
||||
|
||||
let compressed_safe_serialized = compressed_encrypted.safe_serialize(BigInt(10000000));
|
||||
let compressed_safe_deserialized = CompressedFheUint8.safe_deserialize(compressed_safe_serialized, BigInt(10000000));
|
||||
let safe_decompressed = compressed_safe_deserialized.decompress()
|
||||
|
||||
let safe_decrypted = safe_decompressed.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_decrypted, clear);
|
||||
});
|
||||
|
||||
test('hlapi_public_key_encrypt_decrypt_uint32_small', (t) => {
|
||||
@@ -139,11 +151,16 @@ test('hlapi_public_key_encrypt_decrypt_uint32_small', (t) => {
|
||||
let encrypted = FheUint32.encrypt_with_public_key(U32_MAX, publicKey);
|
||||
let decrypted = encrypted.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, U32_MAX);
|
||||
|
||||
|
||||
let serialized = encrypted.serialize();
|
||||
let deserialized = FheUint32.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, U32_MAX);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(1000000));
|
||||
let safe_deserialized = FheUint32.safe_deserialize(safe_serialized, BigInt(1000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, U32_MAX);
|
||||
});
|
||||
|
||||
test('hlapi_decompress_public_key_then_encrypt_decrypt_uint32_small', (t) => {
|
||||
@@ -174,6 +191,11 @@ test('hlapi_decompress_public_key_then_encrypt_decrypt_uint32_small', (t) => {
|
||||
let deserialized = FheUint32.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, U32_MAX);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = FheUint32.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, U32_MAX);
|
||||
});
|
||||
|
||||
test('hlapi_client_key_encrypt_decrypt_uint128_big', (t) => {
|
||||
@@ -193,6 +215,11 @@ test('hlapi_client_key_encrypt_decrypt_uint128_big', (t) => {
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, U128_MAX);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = FheUint128.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, U128_MAX);
|
||||
|
||||
// Compressed
|
||||
let compressed_encrypted = CompressedFheUint128.encrypt_with_client_key(U128_MAX, clientKey);
|
||||
let compressed_serialized = compressed_encrypted.serialize();
|
||||
@@ -201,6 +228,13 @@ test('hlapi_client_key_encrypt_decrypt_uint128_big', (t) => {
|
||||
|
||||
decrypted = decompressed.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, U128_MAX);
|
||||
|
||||
let compressed_safe_serialized = compressed_encrypted.safe_serialize(BigInt(10000000));
|
||||
let compressed_safe_deserialized = CompressedFheUint128.safe_deserialize(compressed_safe_serialized, BigInt(10000000));
|
||||
let safe_decompressed = compressed_safe_deserialized.decompress()
|
||||
|
||||
safe_decrypted = safe_decompressed.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_decrypted, U128_MAX);
|
||||
});
|
||||
|
||||
test('hlapi_client_key_encrypt_decrypt_uint128_small', (t) => {
|
||||
@@ -220,6 +254,11 @@ test('hlapi_client_key_encrypt_decrypt_uint128_small', (t) => {
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, U128_MAX);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = FheUint128.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, U128_MAX);
|
||||
|
||||
// Compressed
|
||||
let compressed_encrypted = CompressedFheUint128.encrypt_with_client_key(U128_MAX, clientKey);
|
||||
let compressed_serialized = compressed_encrypted.serialize();
|
||||
@@ -228,6 +267,13 @@ test('hlapi_client_key_encrypt_decrypt_uint128_small', (t) => {
|
||||
|
||||
decrypted = decompressed.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, U128_MAX);
|
||||
|
||||
let compressed_safe_serialized = compressed_encrypted.safe_serialize(BigInt(10000000));
|
||||
let compressed_safe_deserialized = CompressedFheUint128.safe_deserialize(compressed_safe_serialized, BigInt(10000000));
|
||||
let safe_decompressed = compressed_safe_deserialized.decompress()
|
||||
|
||||
safe_decrypted = safe_decompressed.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_decrypted, U128_MAX);
|
||||
});
|
||||
|
||||
test('hlapi_client_key_encrypt_decrypt_uint256_big', (t) => {
|
||||
@@ -247,6 +293,11 @@ test('hlapi_client_key_encrypt_decrypt_uint256_big', (t) => {
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, U256_MAX);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = FheUint256.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, U256_MAX);
|
||||
|
||||
// Compressed
|
||||
let compressed_encrypted = CompressedFheUint256.encrypt_with_client_key(U256_MAX, clientKey);
|
||||
let compressed_serialized = compressed_encrypted.serialize();
|
||||
@@ -255,6 +306,13 @@ test('hlapi_client_key_encrypt_decrypt_uint256_big', (t) => {
|
||||
|
||||
decrypted = decompressed.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, U256_MAX);
|
||||
|
||||
let compressed_safe_serialized = compressed_encrypted.safe_serialize(BigInt(10000000));
|
||||
let compressed_safe_deserialized = CompressedFheUint256.safe_deserialize(compressed_safe_serialized, BigInt(10000000));
|
||||
let safe_decompressed = compressed_safe_deserialized.decompress()
|
||||
|
||||
safe_decrypted = safe_decompressed.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_decrypted, U256_MAX);
|
||||
});
|
||||
|
||||
test('hlapi_client_key_encrypt_decrypt_uint256_small', (t) => {
|
||||
@@ -274,6 +332,11 @@ test('hlapi_client_key_encrypt_decrypt_uint256_small', (t) => {
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, U256_MAX);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = FheUint256.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, U256_MAX);
|
||||
|
||||
// Compressed
|
||||
let compressed_encrypted = CompressedFheUint256.encrypt_with_client_key(U256_MAX, clientKey);
|
||||
let compressed_serialized = compressed_encrypted.serialize();
|
||||
@@ -282,6 +345,13 @@ test('hlapi_client_key_encrypt_decrypt_uint256_small', (t) => {
|
||||
|
||||
decrypted = decompressed.decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, U256_MAX);
|
||||
|
||||
let compressed_safe_serialized = compressed_encrypted.safe_serialize(BigInt(10000000));
|
||||
let compressed_safe_deserialized = CompressedFheUint256.safe_deserialize(compressed_safe_serialized, BigInt(10000000));
|
||||
let safe_decompressed = compressed_safe_deserialized.decompress()
|
||||
|
||||
safe_decrypted = safe_decompressed.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_decrypted, U256_MAX);
|
||||
});
|
||||
|
||||
test('hlapi_decompress_public_key_then_encrypt_decrypt_uint256_small', (t) => {
|
||||
@@ -303,6 +373,11 @@ test('hlapi_decompress_public_key_then_encrypt_decrypt_uint256_small', (t) => {
|
||||
let deserialized = FheUint256.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, U256_MAX);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = FheUint256.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, U256_MAX);
|
||||
});
|
||||
|
||||
test('hlapi_public_key_encrypt_decrypt_uint256_small', (t) => {
|
||||
@@ -322,14 +397,19 @@ test('hlapi_public_key_encrypt_decrypt_uint256_small', (t) => {
|
||||
let deserialized = FheUint256.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, U256_MAX);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = FheUint256.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, U256_MAX);
|
||||
});
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// 32 bits compact
|
||||
/// 32 bits compact
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
function hlapi_compact_public_key_encrypt_decrypt_uint32_single(config) {
|
||||
let clientKey = TfheClientKey.generate(config);
|
||||
let publicKey = TfheCompactPublicKey.new(clientKey);
|
||||
@@ -342,6 +422,11 @@ function hlapi_compact_public_key_encrypt_decrypt_uint32_single(config) {
|
||||
let deserialized = FheUint32.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, U32_MAX);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = FheUint32.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, U32_MAX);
|
||||
}
|
||||
|
||||
test('hlapi_compact_public_key_encrypt_decrypt_uint32_big_single', (t) => {
|
||||
@@ -375,6 +460,11 @@ function hlapi_compact_public_key_encrypt_decrypt_uint32_single_compact(config)
|
||||
let deserialized = CompactFheUint32.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.expand().decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, U32_MAX);
|
||||
|
||||
let safe_serialized = compact_encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = CompactFheUint32.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.expand().decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, U32_MAX);
|
||||
}
|
||||
|
||||
test('hlapi_compact_public_key_encrypt_decrypt_uint32_small_single_compact', (t) => {
|
||||
@@ -408,8 +498,7 @@ function hlapi_compact_public_key_encrypt_decrypt_uint32_list_compact(config) {
|
||||
|
||||
assert.deepStrictEqual(encrypted_list.length, values.length);
|
||||
|
||||
for (let i = 0; i < values.length; i++)
|
||||
{
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
let decrypted = encrypted_list[i].decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, values[i]);
|
||||
}
|
||||
@@ -420,8 +509,7 @@ function hlapi_compact_public_key_encrypt_decrypt_uint32_list_compact(config) {
|
||||
let encrypted_list = deserialized_list.expand();
|
||||
assert.deepStrictEqual(encrypted_list.length, values.length);
|
||||
|
||||
for (let i = 0; i < values.length; i++)
|
||||
{
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
let decrypted = encrypted_list[i].decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, values[i]);
|
||||
}
|
||||
@@ -447,7 +535,7 @@ test('hlapi_compact_public_key_encrypt_decrypt_uint32_big_list_compact', (t) =>
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
/// 256 bits compact
|
||||
/// 256 bits compact
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function hlapi_compact_public_key_encrypt_decrypt_uint256_single(config) {
|
||||
@@ -462,6 +550,11 @@ function hlapi_compact_public_key_encrypt_decrypt_uint256_single(config) {
|
||||
let deserialized = FheUint256.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, U256_MAX);
|
||||
|
||||
let safe_serialized = encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = FheUint256.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, U256_MAX);
|
||||
}
|
||||
|
||||
test('hlapi_compact_public_key_encrypt_decrypt_uint256_big_single', (t) => {
|
||||
@@ -495,6 +588,11 @@ function hlapi_compact_public_key_encrypt_decrypt_uint256_single_compact(config)
|
||||
let deserialized = CompactFheUint256.deserialize(serialized);
|
||||
let deserialized_decrypted = deserialized.expand().decrypt(clientKey);
|
||||
assert.deepStrictEqual(deserialized_decrypted, U256_MAX);
|
||||
|
||||
let safe_serialized = compact_encrypted.safe_serialize(BigInt(10000000));
|
||||
let safe_deserialized = CompactFheUint256.safe_deserialize(safe_serialized, BigInt(10000000));
|
||||
let safe_deserialized_decrypted = safe_deserialized.expand().decrypt(clientKey);
|
||||
assert.deepStrictEqual(safe_deserialized_decrypted, U256_MAX);
|
||||
}
|
||||
|
||||
test('hlapi_compact_public_key_encrypt_decrypt_uint256_small_single_compact', (t) => {
|
||||
@@ -528,8 +626,7 @@ function hlapi_compact_public_key_encrypt_decrypt_uint256_list_compact(config) {
|
||||
|
||||
assert.deepStrictEqual(encrypted_list.length, values.length);
|
||||
|
||||
for (let i = 0; i < values.length; i++)
|
||||
{
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
let decrypted = encrypted_list[i].decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, values[i]);
|
||||
}
|
||||
@@ -540,8 +637,7 @@ function hlapi_compact_public_key_encrypt_decrypt_uint256_list_compact(config) {
|
||||
let encrypted_list = deserialized_list.expand();
|
||||
assert.deepStrictEqual(encrypted_list.length, values.length);
|
||||
|
||||
for (let i = 0; i < values.length; i++)
|
||||
{
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
let decrypted = encrypted_list[i].decrypt(clientKey);
|
||||
assert.deepStrictEqual(decrypted, values[i]);
|
||||
}
|
||||
@@ -160,7 +160,7 @@ impl Bootstrapper {
|
||||
&mut self,
|
||||
cks: &ClientKey,
|
||||
) -> Result<ServerKey, Box<dyn std::error::Error>> {
|
||||
let standard_bootstraping_key: LweBootstrapKeyOwned<u32> =
|
||||
let standard_bootstrapping_key: LweBootstrapKeyOwned<u32> =
|
||||
par_allocate_and_generate_new_lwe_bootstrap_key(
|
||||
&cks.lwe_secret_key,
|
||||
&cks.glwe_secret_key,
|
||||
@@ -173,14 +173,14 @@ impl Bootstrapper {
|
||||
|
||||
// creation of the bootstrapping key in the Fourier domain
|
||||
let mut fourier_bsk = FourierLweBootstrapKey::new(
|
||||
standard_bootstraping_key.input_lwe_dimension(),
|
||||
standard_bootstraping_key.glwe_size(),
|
||||
standard_bootstraping_key.polynomial_size(),
|
||||
standard_bootstraping_key.decomposition_base_log(),
|
||||
standard_bootstraping_key.decomposition_level_count(),
|
||||
standard_bootstrapping_key.input_lwe_dimension(),
|
||||
standard_bootstrapping_key.glwe_size(),
|
||||
standard_bootstrapping_key.polynomial_size(),
|
||||
standard_bootstrapping_key.decomposition_base_log(),
|
||||
standard_bootstrapping_key.decomposition_level_count(),
|
||||
);
|
||||
|
||||
let fft = Fft::new(standard_bootstraping_key.polynomial_size());
|
||||
let fft = Fft::new(standard_bootstrapping_key.polynomial_size());
|
||||
let fft = fft.as_view();
|
||||
self.computation_buffers.resize(
|
||||
convert_standard_lwe_bootstrap_key_to_fourier_mem_optimized_requirement(fft)
|
||||
@@ -190,7 +190,7 @@ impl Bootstrapper {
|
||||
|
||||
// Conversion to fourier domain
|
||||
par_convert_standard_lwe_bootstrap_key_to_fourier(
|
||||
&standard_bootstraping_key,
|
||||
&standard_bootstrapping_key,
|
||||
&mut fourier_bsk,
|
||||
);
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ pub(crate) trait BinaryGatesAssignEngine<L, R, K> {
|
||||
fn xnor_assign(&mut self, ct_left: L, ct_right: R, server_key: &K);
|
||||
}
|
||||
|
||||
/// Trait to be able to acces thread_local
|
||||
/// Trait to be able to access thread_local
|
||||
/// engines in a generic way
|
||||
pub(crate) trait WithThreadLocalEngine {
|
||||
fn with_thread_local_mut<R, F>(func: F) -> R
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
use crate::boolean::keycache::KEY_CACHE;
|
||||
use crate::boolean::prelude::*;
|
||||
|
||||
#[test]
|
||||
fn test_cast_boolean() {
|
||||
let (client_key_1, _server_key_1): (ClientKey, ServerKey) = gen_keys();
|
||||
let (client_key_2, _server_key_2): (ClientKey, ServerKey) = gen_keys();
|
||||
let keys_1 = KEY_CACHE.get_from_param(DEFAULT_PARAMETERS);
|
||||
let client_key_1 = keys_1.client_key();
|
||||
let keys_2 = KEY_CACHE.get_from_param(DEFAULT_PARAMETERS);
|
||||
let client_key_2 = keys_2.client_key();
|
||||
|
||||
let ksk_params = BooleanKeySwitchingParameters::new(
|
||||
client_key_2.parameters.ks_base_log,
|
||||
client_key_2.parameters.ks_level,
|
||||
);
|
||||
let ksk = KeySwitchingKey::new(&client_key_1, &client_key_2, ksk_params);
|
||||
let ksk = KeySwitchingKey::new(client_key_1, client_key_2, ksk_params);
|
||||
|
||||
let mut ct_true = client_key_1.encrypt(true);
|
||||
ct_true = ksk.cast(&ct_true);
|
||||
@@ -24,14 +27,16 @@ fn test_cast_boolean() {
|
||||
|
||||
#[test]
|
||||
fn test_cast_into_boolean() {
|
||||
let (client_key_1, server_key_1): (ClientKey, ServerKey) = gen_keys();
|
||||
let (client_key_2, _server_key_2): (ClientKey, ServerKey) = gen_keys();
|
||||
let keys_1 = KEY_CACHE.get_from_param(DEFAULT_PARAMETERS);
|
||||
let (client_key_1, server_key_1) = (keys_1.client_key(), keys_1.server_key());
|
||||
let keys_2 = KEY_CACHE.get_from_param(DEFAULT_PARAMETERS);
|
||||
let client_key_2 = keys_2.client_key();
|
||||
|
||||
let ksk_params = BooleanKeySwitchingParameters::new(
|
||||
client_key_2.parameters.ks_base_log,
|
||||
client_key_2.parameters.ks_level,
|
||||
);
|
||||
let ksk = KeySwitchingKey::new(&client_key_1, &client_key_2, ksk_params);
|
||||
let ksk = KeySwitchingKey::new(client_key_1, client_key_2, ksk_params);
|
||||
|
||||
let ct_true = client_key_1.encrypt(true);
|
||||
let mut ct_cast = server_key_1.trivial_encrypt(false);
|
||||
|
||||
@@ -36,7 +36,7 @@ use serde::{Deserialize, Serialize};
|
||||
/// first followed by a keyswitch.
|
||||
/// * The `Small` choice means the small LWE key is used to encrypt the input ciphertext.
|
||||
/// Performance is not as good as in the `Big` case but (`public
|
||||
/// key`)[`super::public_key::PublicKey`] sizes are much more manageable and shoud always fit in
|
||||
/// key`)[`super::public_key::PublicKey`] sizes are much more manageable and should always fit in
|
||||
/// memory. When refreshing a ciphertext and/or evaluating a table lookup the keyswitch is
|
||||
/// computed first followed by a PBS.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
|
||||
@@ -88,27 +88,32 @@ impl CompressedPublicKey {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::boolean::keycache::KEY_CACHE;
|
||||
use crate::boolean::prelude::{
|
||||
BinaryBooleanGates, BooleanParameters, ClientKey, CompressedPublicKey, ServerKey,
|
||||
DEFAULT_PARAMETERS, PARAMETERS_ERROR_PROB_2_POW_MINUS_165,
|
||||
BinaryBooleanGates, BooleanParameters, CompressedPublicKey, DEFAULT_PARAMETERS,
|
||||
PARAMETERS_ERROR_PROB_2_POW_MINUS_165,
|
||||
};
|
||||
use crate::boolean::random_boolean;
|
||||
#[cfg(not(feature = "__coverage"))]
|
||||
const NB_TEST: usize = 32;
|
||||
#[cfg(feature = "__coverage")]
|
||||
const NB_TEST: usize = 1;
|
||||
|
||||
#[test]
|
||||
fn test_compressed_public_key_default_parameters() {
|
||||
test_compressed_public_key(DEFAULT_PARAMETERS);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "__coverage"))]
|
||||
#[test]
|
||||
fn test_compressed_public_key_tfhe_lib_parameters() {
|
||||
test_compressed_public_key(PARAMETERS_ERROR_PROB_2_POW_MINUS_165);
|
||||
}
|
||||
|
||||
fn test_compressed_public_key(parameters: BooleanParameters) {
|
||||
let cks = ClientKey::new(¶meters);
|
||||
let sks = ServerKey::new(&cks);
|
||||
let cpks = CompressedPublicKey::new(&cks);
|
||||
let keys = KEY_CACHE.get_from_param(parameters);
|
||||
let (cks, sks) = (keys.client_key(), keys.server_key());
|
||||
let cpks = CompressedPublicKey::new(cks);
|
||||
|
||||
for _ in 0..NB_TEST {
|
||||
let b1 = random_boolean();
|
||||
|
||||
@@ -80,29 +80,34 @@ impl From<CompressedPublicKey> for PublicKey {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::boolean::keycache::KEY_CACHE;
|
||||
use crate::boolean::prelude::{
|
||||
BinaryBooleanGates, BooleanParameters, ClientKey, CompressedPublicKey, ServerKey,
|
||||
DEFAULT_PARAMETERS, PARAMETERS_ERROR_PROB_2_POW_MINUS_165,
|
||||
BinaryBooleanGates, BooleanParameters, CompressedPublicKey, DEFAULT_PARAMETERS,
|
||||
PARAMETERS_ERROR_PROB_2_POW_MINUS_165,
|
||||
};
|
||||
use crate::boolean::random_boolean;
|
||||
|
||||
use super::PublicKey;
|
||||
#[cfg(not(feature = "__coverage"))]
|
||||
const NB_TEST: usize = 32;
|
||||
#[cfg(feature = "__coverage")]
|
||||
const NB_TEST: usize = 1;
|
||||
|
||||
#[test]
|
||||
fn test_public_key_default_parameters() {
|
||||
test_public_key(DEFAULT_PARAMETERS);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "__coverage"))]
|
||||
#[test]
|
||||
fn test_public_key_tfhe_lib_parameters() {
|
||||
test_public_key(PARAMETERS_ERROR_PROB_2_POW_MINUS_165);
|
||||
}
|
||||
|
||||
fn test_public_key(parameters: BooleanParameters) {
|
||||
let cks = ClientKey::new(¶meters);
|
||||
let sks = ServerKey::new(&cks);
|
||||
let pks = PublicKey::new(&cks);
|
||||
let keys = KEY_CACHE.get_from_param(parameters);
|
||||
let (cks, sks) = (keys.client_key(), keys.server_key());
|
||||
let pks = PublicKey::new(cks);
|
||||
|
||||
for _ in 0..NB_TEST {
|
||||
let b1 = random_boolean();
|
||||
@@ -129,15 +134,16 @@ mod tests {
|
||||
test_public_key(DEFAULT_PARAMETERS);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "__coverage"))]
|
||||
#[test]
|
||||
fn test_decompressing_public_key_tfhe_lib_parameters() {
|
||||
test_decompressing_public_key(PARAMETERS_ERROR_PROB_2_POW_MINUS_165);
|
||||
}
|
||||
|
||||
fn test_decompressing_public_key(parameters: BooleanParameters) {
|
||||
let cks = ClientKey::new(¶meters);
|
||||
let sks = ServerKey::new(&cks);
|
||||
let cpks = CompressedPublicKey::new(&cks);
|
||||
let keys = KEY_CACHE.get_from_param(parameters);
|
||||
let (cks, sks) = (keys.client_key(), keys.server_key());
|
||||
let cpks = CompressedPublicKey::new(cks);
|
||||
let pks = PublicKey::from(cpks);
|
||||
|
||||
for _ in 0..NB_TEST {
|
||||
|
||||
@@ -69,6 +69,7 @@ mod default_parameters_tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "__coverage"))]
|
||||
mod low_prob_parameters_tests {
|
||||
use super::*;
|
||||
use crate::boolean::parameters::PARAMETERS_ERROR_PROB_2_POW_MINUS_165;
|
||||
@@ -161,6 +162,7 @@ mod default_parameters_ks_pbs_tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "__coverage"))]
|
||||
mod low_prob_parameters_ks_pbs_tests {
|
||||
use super::*;
|
||||
use crate::boolean::parameters::PARAMETERS_ERROR_PROB_2_POW_MINUS_165_KS_PBS;
|
||||
@@ -207,6 +209,7 @@ mod low_prob_parameters_ks_pbs_tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "__coverage"))]
|
||||
mod tfhe_lib_parameters_tests {
|
||||
use super::*;
|
||||
use crate::boolean::parameters::TFHE_LIB_PARAMETERS;
|
||||
|
||||
@@ -1,6 +1,157 @@
|
||||
use super::utils::*;
|
||||
use std::os::raw::c_int;
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn core_crypto_generate_binary_lwe_secret_key(
|
||||
output_lwe_sk_ptr: *mut u64,
|
||||
lwe_sk_dim: usize,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
use crate::core_crypto::prelude::*;
|
||||
|
||||
let mut seeder = new_seeder();
|
||||
let seeder = seeder.as_mut();
|
||||
// Create the LweSecretKey
|
||||
let output_lwe_sk_slice = std::slice::from_raw_parts_mut(output_lwe_sk_ptr, lwe_sk_dim);
|
||||
|
||||
let mut lwe_sk = LweSecretKey::from_container(output_lwe_sk_slice);
|
||||
|
||||
let mut secret_generator =
|
||||
SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
generate_binary_lwe_secret_key(&mut lwe_sk, &mut secret_generator);
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn core_crypto_lwe_encrypt(
|
||||
output_ct_ptr: *mut u64,
|
||||
pt: u64,
|
||||
lwe_sk_ptr: *const u64,
|
||||
lwe_sk_dim: usize,
|
||||
lwe_encryption_std_dev: f64,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
use crate::core_crypto::prelude::*;
|
||||
|
||||
let lwe_sk_slice = std::slice::from_raw_parts(lwe_sk_ptr, lwe_sk_dim);
|
||||
let lwe_sk = LweSecretKey::from_container(lwe_sk_slice);
|
||||
|
||||
let mut seeder = new_seeder();
|
||||
let seeder = seeder.as_mut();
|
||||
|
||||
let mut encryption_generator =
|
||||
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
|
||||
let plaintext = Plaintext(pt);
|
||||
let output_ct = std::slice::from_raw_parts_mut(output_ct_ptr, lwe_sk_dim + 1);
|
||||
let mut ct = LweCiphertext::from_container(output_ct, CiphertextModulus::new_native());
|
||||
|
||||
let lwe_encryption_std_dev = StandardDev(lwe_encryption_std_dev);
|
||||
|
||||
encrypt_lwe_ciphertext(
|
||||
&lwe_sk,
|
||||
&mut ct,
|
||||
plaintext,
|
||||
lwe_encryption_std_dev,
|
||||
&mut encryption_generator,
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn core_crypto_lwe_decrypt(
|
||||
output_pt: *mut u64,
|
||||
input_ct_ptr: *const u64,
|
||||
lwe_sk_ptr: *const u64,
|
||||
lwe_sk_dim: usize,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
use crate::core_crypto::prelude::*;
|
||||
|
||||
let lwe_sk_slice = std::slice::from_raw_parts(lwe_sk_ptr, lwe_sk_dim);
|
||||
let lwe_sk = LweSecretKey::from_container(lwe_sk_slice);
|
||||
|
||||
let input_ct = std::slice::from_raw_parts(input_ct_ptr, lwe_sk_dim + 1);
|
||||
let ct = LweCiphertext::from_container(input_ct, CiphertextModulus::new_native());
|
||||
|
||||
let plaintext = decrypt_lwe_ciphertext(&lwe_sk, &ct);
|
||||
|
||||
*output_pt = plaintext.0;
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn core_crypto_par_generate_lwe_bootstrapping_key(
|
||||
output_bsk_ptr: *mut u64,
|
||||
bsk_base_log: usize,
|
||||
bsk_level_count: usize,
|
||||
input_lwe_sk_ptr: *const u64,
|
||||
input_lwe_sk_dim: usize,
|
||||
output_glwe_sk_ptr: *const u64,
|
||||
output_glwe_sk_dim: usize,
|
||||
output_glwe_sk_poly_size: usize,
|
||||
glwe_encryption_std_dev: f64,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
use crate::core_crypto::prelude::*;
|
||||
|
||||
let input_lwe_sk_slice = std::slice::from_raw_parts(input_lwe_sk_ptr, input_lwe_sk_dim);
|
||||
let input_lwe_sk = LweSecretKey::from_container(input_lwe_sk_slice);
|
||||
|
||||
let output_glwe_sk_dim = GlweDimension(output_glwe_sk_dim);
|
||||
let output_glwe_sk_poly_size = PolynomialSize(output_glwe_sk_poly_size);
|
||||
let output_glwe_sk_size =
|
||||
glwe_ciphertext_mask_size(output_glwe_sk_dim, output_glwe_sk_poly_size);
|
||||
let output_glwe_sk_slice =
|
||||
std::slice::from_raw_parts(output_glwe_sk_ptr, output_glwe_sk_size);
|
||||
let output_glwe_sk =
|
||||
GlweSecretKey::from_container(output_glwe_sk_slice, output_glwe_sk_poly_size);
|
||||
|
||||
let mut seeder = new_seeder();
|
||||
let seeder = seeder.as_mut();
|
||||
|
||||
let mut encryption_random_generator =
|
||||
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
|
||||
let lwe_base_log = DecompositionBaseLog(bsk_base_log);
|
||||
let lwe_level_count = DecompositionLevelCount(bsk_level_count);
|
||||
|
||||
let lwe_slice_len = {
|
||||
let bsk = LweBootstrapKeyOwned::new(
|
||||
0u64,
|
||||
output_glwe_sk.glwe_dimension().to_glwe_size(),
|
||||
output_glwe_sk.polynomial_size(),
|
||||
lwe_base_log,
|
||||
lwe_level_count,
|
||||
input_lwe_sk.lwe_dimension(),
|
||||
CiphertextModulus::new_native(),
|
||||
);
|
||||
bsk.into_container().len()
|
||||
};
|
||||
|
||||
let bsk_slice = std::slice::from_raw_parts_mut(output_bsk_ptr, lwe_slice_len);
|
||||
|
||||
let mut bsk = LweBootstrapKey::from_container(
|
||||
bsk_slice,
|
||||
output_glwe_sk.glwe_dimension().to_glwe_size(),
|
||||
output_glwe_sk.polynomial_size(),
|
||||
lwe_base_log,
|
||||
lwe_level_count,
|
||||
CiphertextModulus::new_native(),
|
||||
);
|
||||
|
||||
let glwe_encryption_std_dev = StandardDev(glwe_encryption_std_dev);
|
||||
|
||||
par_generate_lwe_bootstrap_key(
|
||||
&input_lwe_sk,
|
||||
&output_glwe_sk,
|
||||
&mut bsk,
|
||||
glwe_encryption_std_dev,
|
||||
&mut encryption_random_generator,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn core_crypto_lwe_multi_bit_bootstrapping_key_element_size(
|
||||
input_lwe_sk_dim: usize,
|
||||
@@ -46,12 +197,8 @@ pub unsafe extern "C" fn core_crypto_par_generate_lwe_multi_bit_bootstrapping_ke
|
||||
lwe_multi_bit_level_count: usize,
|
||||
lwe_multi_bit_grouping_factor: usize,
|
||||
glwe_encryption_std_dev: f64,
|
||||
seed_low_bytes: u64,
|
||||
seed_high_bytes: u64,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
use crate::core_crypto::commons::generators::DeterministicSeeder;
|
||||
use crate::core_crypto::commons::math::random::Seed;
|
||||
use crate::core_crypto::prelude::*;
|
||||
|
||||
let input_lwe_sk_slice = std::slice::from_raw_parts(input_lwe_sk_ptr, input_lwe_sk_dim);
|
||||
@@ -66,17 +213,10 @@ pub unsafe extern "C" fn core_crypto_par_generate_lwe_multi_bit_bootstrapping_ke
|
||||
let output_glwe_sk =
|
||||
GlweSecretKey::from_container(output_glwe_sk_slice, output_glwe_sk_poly_size);
|
||||
|
||||
let seed_low_bytes: u128 = seed_low_bytes.into();
|
||||
let seed_high_bytes: u128 = seed_high_bytes.into();
|
||||
let seed = (seed_high_bytes << 64) | seed_low_bytes;
|
||||
|
||||
let mut deterministic_seeder =
|
||||
DeterministicSeeder::<ActivatedRandomGenerator>::new(Seed(seed));
|
||||
let mut seeder = new_seeder();
|
||||
let seeder = seeder.as_mut();
|
||||
let mut encryption_random_generator =
|
||||
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(
|
||||
deterministic_seeder.seed(),
|
||||
&mut deterministic_seeder,
|
||||
);
|
||||
EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
|
||||
let lwe_multi_bit_base_log = DecompositionBaseLog(lwe_multi_bit_base_log);
|
||||
let lwe_multi_bit_level_count = DecompositionLevelCount(lwe_multi_bit_level_count);
|
||||
|
||||
22
tfhe/src/c_api/high_level_api/i128.rs
Normal file
22
tfhe/src/c_api/high_level_api/i128.rs
Normal file
@@ -0,0 +1,22 @@
|
||||
/// w0 and w1 are words in little endian order
|
||||
/// using two's complement representation
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct I128 {
|
||||
pub w0: u64,
|
||||
pub w1: u64,
|
||||
}
|
||||
|
||||
impl From<i128> for I128 {
|
||||
fn from(value: i128) -> Self {
|
||||
let w0 = (value & (u64::MAX as i128)) as u64;
|
||||
let w1 = (value >> 64) as u64;
|
||||
Self { w0, w1 }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<I128> for i128 {
|
||||
fn from(value: I128) -> Self {
|
||||
((value.w1 as i128) << 64u128) | value.w0 as i128
|
||||
}
|
||||
}
|
||||
27
tfhe/src/c_api/high_level_api/i256.rs
Normal file
27
tfhe/src/c_api/high_level_api/i256.rs
Normal file
@@ -0,0 +1,27 @@
|
||||
/// w0, w1, w2, w3 are words in little endian order
|
||||
/// using two's complement representation
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct I256 {
|
||||
pub w0: u64,
|
||||
pub w1: u64,
|
||||
pub w2: u64,
|
||||
pub w3: u64,
|
||||
}
|
||||
|
||||
impl From<crate::integer::I256> for I256 {
|
||||
fn from(value: crate::integer::I256) -> Self {
|
||||
Self {
|
||||
w0: value.0[0],
|
||||
w1: value.0[1],
|
||||
w2: value.0[2],
|
||||
w3: value.0[3],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<I256> for crate::integer::I256 {
|
||||
fn from(value: I256) -> Self {
|
||||
Self([value.w0, value.w1, value.w2, value.w3])
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
use crate::c_api::high_level_api::keys::{ClientKey, CompactPublicKey, PublicKey};
|
||||
use crate::c_api::high_level_api::keys::CompactPublicKey;
|
||||
use crate::high_level_api::prelude::*;
|
||||
use std::ops::{
|
||||
Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Div, DivAssign,
|
||||
Mul, MulAssign, Neg, Not, Rem, RemAssign, Shl, ShlAssign, Shr, ShrAssign, Sub, SubAssign,
|
||||
};
|
||||
|
||||
use crate::c_api::high_level_api::i128::I128;
|
||||
use crate::c_api::high_level_api::i256::I256;
|
||||
use crate::c_api::high_level_api::u128::U128;
|
||||
use crate::c_api::high_level_api::u256::U256;
|
||||
use crate::c_api::utils::*;
|
||||
@@ -12,10 +14,17 @@ use std::os::raw::c_int;
|
||||
|
||||
/// Implement C functions for all the operations supported by a integer type,
|
||||
/// which should also be accessible from C API
|
||||
///
|
||||
/// We require the shift amount to be an unsigned type,
|
||||
/// so to be able to use that macro for signed integers (which have a signed clear type)
|
||||
/// we also accept an additional clear_shift_type
|
||||
macro_rules! impl_operations_for_integer_type {
|
||||
(
|
||||
name: $name:ident,
|
||||
clear_scalar_type: $clear_scalar_type:ty
|
||||
fhe_unsigned_type: $fhe_unsigned_type:ty,
|
||||
clear_scalar_type: $clear_scalar_type:ty,
|
||||
clear_shift_type: $clear_shift_type:ty
|
||||
$(,)?
|
||||
) => {
|
||||
impl_binary_fn_on_type!($name =>
|
||||
add,
|
||||
@@ -24,8 +33,6 @@ macro_rules! impl_operations_for_integer_type {
|
||||
bitand,
|
||||
bitor,
|
||||
bitxor,
|
||||
shl,
|
||||
shr,
|
||||
eq,
|
||||
ne,
|
||||
ge,
|
||||
@@ -37,6 +44,15 @@ macro_rules! impl_operations_for_integer_type {
|
||||
div,
|
||||
rem,
|
||||
);
|
||||
|
||||
// handle shift separately as they require
|
||||
// rhs to be an unsigned type
|
||||
impl_binary_fn_on_type!(
|
||||
lhs_type: $name,
|
||||
rhs_type: $fhe_unsigned_type,
|
||||
binary_fn_names: shl, shr, rotate_right, rotate_left,
|
||||
);
|
||||
|
||||
impl_binary_assign_fn_on_type!($name =>
|
||||
add_assign,
|
||||
sub_assign,
|
||||
@@ -44,11 +60,18 @@ macro_rules! impl_operations_for_integer_type {
|
||||
bitand_assign,
|
||||
bitor_assign,
|
||||
bitxor_assign,
|
||||
shl_assign,
|
||||
shr_assign,
|
||||
div_assign,
|
||||
rem_assign,
|
||||
);
|
||||
|
||||
// handle shift separately as they require
|
||||
// rhs to be an unsigned type
|
||||
impl_binary_assign_fn_on_type!(
|
||||
lhs_type: $name,
|
||||
rhs_type: $fhe_unsigned_type,
|
||||
binary_fn_names: shl_assign, shr_assign, rotate_right_assign, rotate_left_assign,
|
||||
);
|
||||
|
||||
impl_scalar_binary_fn_on_type!($name, $clear_scalar_type =>
|
||||
add,
|
||||
sub,
|
||||
@@ -56,8 +79,6 @@ macro_rules! impl_operations_for_integer_type {
|
||||
bitand,
|
||||
bitor,
|
||||
bitxor,
|
||||
shl,
|
||||
shr,
|
||||
eq,
|
||||
ne,
|
||||
ge,
|
||||
@@ -66,8 +87,6 @@ macro_rules! impl_operations_for_integer_type {
|
||||
lt,
|
||||
min,
|
||||
max,
|
||||
rotate_right,
|
||||
rotate_left,
|
||||
div,
|
||||
rem,
|
||||
);
|
||||
@@ -78,12 +97,23 @@ macro_rules! impl_operations_for_integer_type {
|
||||
bitand_assign,
|
||||
bitor_assign,
|
||||
bitxor_assign,
|
||||
div_assign,
|
||||
rem_assign,
|
||||
);
|
||||
|
||||
// handle shift separately as they require
|
||||
// rhs to be an unsigned type
|
||||
impl_scalar_binary_fn_on_type!($name, $clear_shift_type =>
|
||||
shl,
|
||||
shr,
|
||||
rotate_right,
|
||||
rotate_left,
|
||||
);
|
||||
impl_scalar_binary_assign_fn_on_type!($name, $clear_shift_type =>
|
||||
shl_assign,
|
||||
shr_assign,
|
||||
rotate_right_assign,
|
||||
rotate_left_assign,
|
||||
div_assign,
|
||||
rem_assign,
|
||||
);
|
||||
|
||||
impl_unary_fn_on_type!($name => neg, not);
|
||||
@@ -104,8 +134,8 @@ macro_rules! impl_operations_for_integer_type {
|
||||
) -> c_int {
|
||||
$crate::c_api::utils::catch_panic(|| {
|
||||
let lhs = $crate::c_api::utils::get_ref_checked(lhs).unwrap();
|
||||
let rhs = <$clear_scalar_type as $crate::c_api::high_level_api::utils::ToRustScalarType
|
||||
>::to_rust_scalar_type(rhs);
|
||||
let rhs = <$clear_scalar_type as $crate::c_api::high_level_api::utils::CApiIntegerType
|
||||
>::to_rust(rhs);
|
||||
|
||||
let (q, r) = (&lhs.0).div_rem(rhs);
|
||||
|
||||
@@ -153,29 +183,53 @@ macro_rules! impl_operations_for_integer_type {
|
||||
*result = Box::into_raw(Box::new($name(r)));
|
||||
})
|
||||
}
|
||||
|
||||
// map cmux to if_then_else
|
||||
pub unsafe extern "C" fn [<$name:snake _cmux>](
|
||||
condition_ct: *const $name,
|
||||
then_ct: *const $name,
|
||||
else_ct: *const $name,
|
||||
result: *mut *mut $name,
|
||||
) -> c_int {
|
||||
[<$name:snake _if_then_else>](condition_ct, then_ct, else_ct, result)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Creates a type that will act as an opaque wrapper
|
||||
/// aroung a tfhe integer.
|
||||
/// around a tfhe integer.
|
||||
///
|
||||
/// It also implements binary operations for this wrapper type
|
||||
macro_rules! create_integer_wrapper_type {
|
||||
|
||||
(
|
||||
name: $name:ident,
|
||||
clear_scalar_type: $clear_scalar_type:ty
|
||||
fhe_unsigned_type: $fhe_unsigned_type:ty,
|
||||
clear_scalar_type: $clear_scalar_type:ty,
|
||||
clear_shift_type: $clear_shift_type:ty
|
||||
$(,)?
|
||||
) => {
|
||||
pub struct $name($crate::high_level_api::$name);
|
||||
|
||||
impl_destroy_on_type!($name);
|
||||
|
||||
impl_operations_for_integer_type!(name: $name, clear_scalar_type: $clear_scalar_type);
|
||||
impl_operations_for_integer_type!(
|
||||
name: $name,
|
||||
fhe_unsigned_type: $fhe_unsigned_type,
|
||||
clear_scalar_type: $clear_scalar_type,
|
||||
clear_shift_type: $clear_shift_type,
|
||||
);
|
||||
|
||||
impl_serialize_deserialize_on_type!($name);
|
||||
|
||||
impl_clone_on_type!($name);
|
||||
|
||||
impl_safe_serialize_on_type!($name);
|
||||
|
||||
impl_safe_deserialize_conformant_integer!($name, crate::high_level_api::safe_deserialize_conformant_integer);
|
||||
|
||||
|
||||
// The compressed version of the ciphertext type
|
||||
::paste::paste! {
|
||||
pub struct [<Compressed $name>]($crate::high_level_api::[<Compressed $name>]);
|
||||
@@ -186,6 +240,11 @@ macro_rules! create_integer_wrapper_type {
|
||||
|
||||
impl_serialize_deserialize_on_type!([<Compressed $name>]);
|
||||
|
||||
impl_safe_serialize_on_type!([<Compressed $name>]);
|
||||
|
||||
impl_safe_deserialize_conformant_integer!([<Compressed $name>], crate::high_level_api::safe_deserialize_conformant_compressed_integer);
|
||||
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn [<compressed_ $name:snake _decompress>](
|
||||
sself: *const [<Compressed $name>],
|
||||
@@ -200,6 +259,35 @@ macro_rules! create_integer_wrapper_type {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The compact version of the ciphertext type
|
||||
::paste::paste! {
|
||||
pub struct [<Compact $name>]($crate::high_level_api::[<Compact $name>]);
|
||||
|
||||
impl_destroy_on_type!([<Compact $name>]);
|
||||
|
||||
impl_clone_on_type!([<Compact $name>]);
|
||||
|
||||
impl_serialize_deserialize_on_type!([<Compact $name>]);
|
||||
|
||||
impl_safe_serialize_on_type!([<Compact $name>]);
|
||||
|
||||
impl_safe_deserialize_conformant_integer!([<Compact $name>], crate::high_level_api::safe_deserialize_conformant_compact_integer);
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn [<compact_ $name:snake _expand>](
|
||||
sself: *const [<Compact $name>],
|
||||
output: *mut *mut $name,
|
||||
) -> ::std::os::raw::c_int {
|
||||
$crate::c_api::utils::catch_panic(|| {
|
||||
check_ptr_is_non_null_and_aligned(output).unwrap();
|
||||
let list = $crate::c_api::utils::get_ref_checked(sself).unwrap();
|
||||
let expanded = list.0.expand();
|
||||
|
||||
*output = Box::into_raw(Box::new($name(expanded)));
|
||||
})
|
||||
}
|
||||
}
|
||||
// The compact list version of the ciphertext type
|
||||
::paste::paste! {
|
||||
pub struct [<Compact $name List>]($crate::high_level_api::[<Compact $name List>]);
|
||||
@@ -243,6 +331,20 @@ macro_rules! create_integer_wrapper_type {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This entry point is meant for unsigned types
|
||||
(
|
||||
name: $name:ident,
|
||||
clear_scalar_type: $clear_scalar_type:ty
|
||||
) => {
|
||||
create_integer_wrapper_type!(
|
||||
name: $name,
|
||||
fhe_unsigned_type: $name,
|
||||
clear_scalar_type: $clear_scalar_type,
|
||||
clear_shift_type: $clear_scalar_type,
|
||||
);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
create_integer_wrapper_type!(name: FheUint8, clear_scalar_type: u8);
|
||||
@@ -260,6 +362,7 @@ impl_try_encrypt_trivial_on_type!(FheUint8{crate::high_level_api::FheUint8}, u8)
|
||||
impl_try_encrypt_with_client_key_on_type!(FheUint8{crate::high_level_api::FheUint8}, u8);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheUint8{crate::high_level_api::FheUint8}, u8);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(FheUint8{crate::high_level_api::FheUint8}, u8);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(CompactFheUint8{crate::high_level_api::CompactFheUint8}, u8);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint8{crate::high_level_api::CompressedFheUint8}, u8);
|
||||
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint8List{crate::high_level_api::CompactFheUint8List}, u8);
|
||||
|
||||
@@ -268,6 +371,7 @@ impl_try_encrypt_trivial_on_type!(FheUint10{crate::high_level_api::FheUint10}, u
|
||||
impl_try_encrypt_with_client_key_on_type!(FheUint10{crate::high_level_api::FheUint10}, u16);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheUint10{crate::high_level_api::FheUint10}, u16);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(FheUint10{crate::high_level_api::FheUint10}, u16);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(CompactFheUint10{crate::high_level_api::CompactFheUint10}, u16);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint10{crate::high_level_api::CompressedFheUint10}, u16);
|
||||
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint10List{crate::high_level_api::CompactFheUint10List}, u16);
|
||||
|
||||
@@ -276,6 +380,7 @@ impl_try_encrypt_trivial_on_type!(FheUint12{crate::high_level_api::FheUint12}, u
|
||||
impl_try_encrypt_with_client_key_on_type!(FheUint12{crate::high_level_api::FheUint12}, u16);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheUint12{crate::high_level_api::FheUint12}, u16);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(FheUint12{crate::high_level_api::FheUint12}, u16);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(CompactFheUint12{crate::high_level_api::CompactFheUint12}, u16);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint12{crate::high_level_api::CompressedFheUint12}, u16);
|
||||
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint12List{crate::high_level_api::CompactFheUint12List}, u16);
|
||||
|
||||
@@ -284,6 +389,7 @@ impl_try_encrypt_trivial_on_type!(FheUint14{crate::high_level_api::FheUint14}, u
|
||||
impl_try_encrypt_with_client_key_on_type!(FheUint14{crate::high_level_api::FheUint14}, u16);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheUint14{crate::high_level_api::FheUint14}, u16);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(FheUint14{crate::high_level_api::FheUint14}, u16);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(CompactFheUint14{crate::high_level_api::CompactFheUint14}, u16);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint14{crate::high_level_api::CompressedFheUint14}, u16);
|
||||
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint14List{crate::high_level_api::CompactFheUint14List}, u16);
|
||||
|
||||
@@ -292,6 +398,7 @@ impl_try_encrypt_trivial_on_type!(FheUint16{crate::high_level_api::FheUint16}, u
|
||||
impl_try_encrypt_with_client_key_on_type!(FheUint16{crate::high_level_api::FheUint16}, u16);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheUint16{crate::high_level_api::FheUint16}, u16);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(FheUint16{crate::high_level_api::FheUint16}, u16);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(CompactFheUint16{crate::high_level_api::CompactFheUint16}, u16);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint16{crate::high_level_api::CompressedFheUint16}, u16);
|
||||
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint16List{crate::high_level_api::CompactFheUint16List}, u16);
|
||||
|
||||
@@ -299,6 +406,7 @@ impl_decrypt_on_type!(FheUint32, u32);
|
||||
impl_try_encrypt_trivial_on_type!(FheUint32{crate::high_level_api::FheUint32}, u32);
|
||||
impl_try_encrypt_with_client_key_on_type!(FheUint32{crate::high_level_api::FheUint32}, u32);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheUint32{crate::high_level_api::FheUint32}, u32);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(CompactFheUint32{crate::high_level_api::CompactFheUint32}, u32);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(FheUint32{crate::high_level_api::FheUint32}, u32);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint32{crate::high_level_api::CompressedFheUint32}, u32);
|
||||
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint32List{crate::high_level_api::CompactFheUint32List}, u32);
|
||||
@@ -308,92 +416,108 @@ impl_try_encrypt_trivial_on_type!(FheUint64{crate::high_level_api::FheUint64}, u
|
||||
impl_try_encrypt_with_client_key_on_type!(FheUint64{crate::high_level_api::FheUint64}, u64);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheUint64{crate::high_level_api::FheUint64}, u64);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(FheUint64{crate::high_level_api::FheUint64}, u64);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(CompactFheUint64{crate::high_level_api::CompactFheUint64}, u64);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint64{crate::high_level_api::CompressedFheUint64}, u64);
|
||||
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheUint64List{crate::high_level_api::CompactFheUint64List}, u64);
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fhe_uint128_try_encrypt_trivial_u128(
|
||||
value: U128,
|
||||
result: *mut *mut FheUint128,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
let value = u128::from(value);
|
||||
impl_decrypt_on_type!(FheUint128, U128);
|
||||
impl_try_encrypt_trivial_on_type!(FheUint128{crate::high_level_api::FheUint128}, U128);
|
||||
impl_try_encrypt_with_client_key_on_type!(FheUint128{crate::high_level_api::FheUint128}, U128);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheUint128{crate::high_level_api::FheUint128}, U128);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(FheUint128{crate::high_level_api::FheUint128}, U128);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(CompactFheUint128{crate::high_level_api::CompactFheUint128}, U128);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint128{crate::high_level_api::CompressedFheUint128}, U128);
|
||||
|
||||
let inner = <crate::high_level_api::FheUint128>::try_encrypt_trivial(value).unwrap();
|
||||
impl_decrypt_on_type!(FheUint256, U256);
|
||||
impl_try_encrypt_trivial_on_type!(FheUint256{crate::high_level_api::FheUint256}, U256);
|
||||
impl_try_encrypt_with_client_key_on_type!(FheUint256{crate::high_level_api::FheUint256}, U256);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheUint256{crate::high_level_api::FheUint256}, U256);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(FheUint256{crate::high_level_api::FheUint256}, U256);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(CompactFheUint256{crate::high_level_api::CompactFheUint256}, U256);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheUint256{crate::high_level_api::CompressedFheUint256}, U256);
|
||||
|
||||
*result = Box::into_raw(Box::new(FheUint128(inner)));
|
||||
})
|
||||
}
|
||||
create_integer_wrapper_type!(
|
||||
name: FheInt8,
|
||||
fhe_unsigned_type: FheUint8,
|
||||
clear_scalar_type: i8,
|
||||
clear_shift_type: u8,
|
||||
);
|
||||
create_integer_wrapper_type!(
|
||||
name: FheInt16,
|
||||
fhe_unsigned_type: FheUint16,
|
||||
clear_scalar_type: i16,
|
||||
clear_shift_type: u16,
|
||||
);
|
||||
create_integer_wrapper_type!(
|
||||
name: FheInt32,
|
||||
fhe_unsigned_type: FheUint32,
|
||||
clear_scalar_type: i32,
|
||||
clear_shift_type: u32,
|
||||
);
|
||||
create_integer_wrapper_type!(
|
||||
name: FheInt64,
|
||||
fhe_unsigned_type: FheUint64,
|
||||
clear_scalar_type: i64,
|
||||
clear_shift_type: u64,
|
||||
);
|
||||
create_integer_wrapper_type!(
|
||||
name: FheInt128,
|
||||
fhe_unsigned_type: FheUint128,
|
||||
clear_scalar_type: I128,
|
||||
clear_shift_type: U128,
|
||||
);
|
||||
create_integer_wrapper_type!(
|
||||
name: FheInt256,
|
||||
fhe_unsigned_type: FheUint256,
|
||||
clear_scalar_type: I256,
|
||||
clear_shift_type: U256,
|
||||
);
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fhe_uint128_try_encrypt_with_client_key_u128(
|
||||
value: U128,
|
||||
client_key: *const ClientKey,
|
||||
result: *mut *mut FheUint128,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
let client_key = get_ref_checked(client_key).unwrap();
|
||||
impl_decrypt_on_type!(FheInt8, i8);
|
||||
impl_try_encrypt_trivial_on_type!(FheInt8{crate::high_level_api::FheInt8}, i8);
|
||||
impl_try_encrypt_with_client_key_on_type!(FheInt8{crate::high_level_api::FheInt8}, i8);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheInt8{crate::high_level_api::FheInt8}, i8);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(FheInt8{crate::high_level_api::FheInt8}, i8);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheInt8{crate::high_level_api::CompressedFheInt8}, i8);
|
||||
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheInt8List{crate::high_level_api::CompactFheInt8List}, i8);
|
||||
|
||||
let value = u128::from(value);
|
||||
impl_decrypt_on_type!(FheInt16, i16);
|
||||
impl_try_encrypt_trivial_on_type!(FheInt16{crate::high_level_api::FheInt16}, i16);
|
||||
impl_try_encrypt_with_client_key_on_type!(FheInt16{crate::high_level_api::FheInt16}, i16);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheInt16{crate::high_level_api::FheInt16}, i16);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(FheInt16{crate::high_level_api::FheInt16}, i16);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheInt16{crate::high_level_api::CompressedFheInt16}, i16);
|
||||
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheInt16List{crate::high_level_api::CompactFheInt16List}, i16);
|
||||
|
||||
let inner = <crate::high_level_api::FheUint128>::try_encrypt(value, &client_key.0).unwrap();
|
||||
impl_decrypt_on_type!(FheInt32, i32);
|
||||
impl_try_encrypt_trivial_on_type!(FheInt32{crate::high_level_api::FheInt32}, i32);
|
||||
impl_try_encrypt_with_client_key_on_type!(FheInt32{crate::high_level_api::FheInt32}, i32);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheInt32{crate::high_level_api::FheInt32}, i32);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(FheInt32{crate::high_level_api::FheInt32}, i32);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheInt32{crate::high_level_api::CompressedFheInt32}, i32);
|
||||
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheInt32List{crate::high_level_api::CompactFheInt32List}, i32);
|
||||
|
||||
*result = Box::into_raw(Box::new(FheUint128(inner)));
|
||||
})
|
||||
}
|
||||
impl_decrypt_on_type!(FheInt64, i64);
|
||||
impl_try_encrypt_trivial_on_type!(FheInt64{crate::high_level_api::FheInt64}, i64);
|
||||
impl_try_encrypt_with_client_key_on_type!(FheInt64{crate::high_level_api::FheInt64}, i64);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheInt64{crate::high_level_api::FheInt64}, i64);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(FheInt64{crate::high_level_api::FheInt64}, i64);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheInt64{crate::high_level_api::CompressedFheInt64}, i64);
|
||||
impl_try_encrypt_list_with_compact_public_key_on_type!(CompactFheInt64List{crate::high_level_api::CompactFheInt64List}, i64);
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn compressed_fhe_uint128_try_encrypt_with_client_key_u128(
|
||||
value: U128,
|
||||
client_key: *const ClientKey,
|
||||
result: *mut *mut CompressedFheUint128,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
let client_key = get_ref_checked(client_key).unwrap();
|
||||
impl_decrypt_on_type!(FheInt128, I128);
|
||||
impl_try_encrypt_trivial_on_type!(FheInt128{crate::high_level_api::FheInt128}, I128);
|
||||
impl_try_encrypt_with_client_key_on_type!(FheInt128{crate::high_level_api::FheInt128}, I128);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheInt128{crate::high_level_api::FheInt128}, I128);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(FheInt128{crate::high_level_api::FheInt128}, I128);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheInt128{crate::high_level_api::CompressedFheInt128}, I128);
|
||||
|
||||
let value = u128::from(value);
|
||||
|
||||
let inner =
|
||||
<crate::high_level_api::CompressedFheUint128>::try_encrypt(value, &client_key.0)
|
||||
.unwrap();
|
||||
|
||||
*result = Box::into_raw(Box::new(CompressedFheUint128(inner)));
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fhe_uint128_try_encrypt_with_public_key_u128(
|
||||
value: U128,
|
||||
public_key: *const PublicKey,
|
||||
result: *mut *mut FheUint128,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
let public_key = get_ref_checked(public_key).unwrap();
|
||||
|
||||
let value = u128::from(value);
|
||||
|
||||
let inner = <crate::high_level_api::FheUint128>::try_encrypt(value, &public_key.0).unwrap();
|
||||
|
||||
*result = Box::into_raw(Box::new(FheUint128(inner)));
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fhe_uint128_try_encrypt_with_compact_public_key_u128(
|
||||
value: U128,
|
||||
public_key: *const CompactPublicKey,
|
||||
result: *mut *mut FheUint128,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
let public_key = get_ref_checked(public_key).unwrap();
|
||||
|
||||
let value = u128::from(value);
|
||||
|
||||
let inner = <crate::high_level_api::FheUint128>::try_encrypt(value, &public_key.0).unwrap();
|
||||
|
||||
*result = Box::into_raw(Box::new(FheUint128(inner)));
|
||||
})
|
||||
}
|
||||
impl_decrypt_on_type!(FheInt256, I256);
|
||||
impl_try_encrypt_trivial_on_type!(FheInt256{crate::high_level_api::FheInt256}, I256);
|
||||
impl_try_encrypt_with_client_key_on_type!(FheInt256{crate::high_level_api::FheInt256}, I256);
|
||||
impl_try_encrypt_with_public_key_on_type!(FheInt256{crate::high_level_api::FheInt256}, I256);
|
||||
impl_try_encrypt_with_compact_public_key_on_type!(FheInt256{crate::high_level_api::FheInt256}, I256);
|
||||
impl_try_encrypt_with_client_key_on_type!(CompressedFheInt256{crate::high_level_api::CompressedFheInt256}, I256);
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn compact_fhe_uint256_list_try_encrypt_with_compact_public_key_u128(
|
||||
@@ -415,101 +539,6 @@ pub unsafe extern "C" fn compact_fhe_uint256_list_try_encrypt_with_compact_publi
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fhe_uint128_decrypt(
|
||||
encrypted_value: *const FheUint128,
|
||||
client_key: *const ClientKey,
|
||||
result: *mut U128,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
let client_key = get_ref_checked(client_key).unwrap();
|
||||
let encrypted_value = get_ref_checked(encrypted_value).unwrap();
|
||||
|
||||
let inner: u128 = encrypted_value.0.decrypt(&client_key.0);
|
||||
|
||||
*result = U128::from(inner);
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fhe_uint256_try_encrypt_trivial_u256(
|
||||
value: U256,
|
||||
result: *mut *mut FheUint256,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
let value = crate::integer::U256::from(value);
|
||||
let inner = <crate::high_level_api::FheUint256>::try_encrypt_trivial(value).unwrap();
|
||||
|
||||
*result = Box::into_raw(Box::new(FheUint256(inner)));
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fhe_uint256_try_encrypt_with_client_key_u256(
|
||||
value: U256,
|
||||
client_key: *const ClientKey,
|
||||
result: *mut *mut FheUint256,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
let client_key = get_ref_checked(client_key).unwrap();
|
||||
|
||||
let value = crate::integer::U256::from(value);
|
||||
let inner = <crate::high_level_api::FheUint256>::try_encrypt(value, &client_key.0).unwrap();
|
||||
|
||||
*result = Box::into_raw(Box::new(FheUint256(inner)));
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn compressed_fhe_uint256_try_encrypt_with_client_key_u256(
|
||||
value: U256,
|
||||
client_key: *const ClientKey,
|
||||
result: *mut *mut CompressedFheUint256,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
let client_key = get_ref_checked(client_key).unwrap();
|
||||
|
||||
let value = crate::integer::U256::from(value);
|
||||
let inner =
|
||||
<crate::high_level_api::CompressedFheUint256>::try_encrypt(value, &client_key.0)
|
||||
.unwrap();
|
||||
|
||||
*result = Box::into_raw(Box::new(CompressedFheUint256(inner)));
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fhe_uint256_try_encrypt_with_public_key_u256(
|
||||
value: U256,
|
||||
public_key: *const PublicKey,
|
||||
result: *mut *mut FheUint256,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
let public_key = get_ref_checked(public_key).unwrap();
|
||||
|
||||
let value = crate::integer::U256::from(value);
|
||||
let inner = <crate::high_level_api::FheUint256>::try_encrypt(value, &public_key.0).unwrap();
|
||||
|
||||
*result = Box::into_raw(Box::new(FheUint256(inner)));
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fhe_uint256_try_encrypt_with_compact_public_key_u256(
|
||||
value: U256,
|
||||
public_key: *const CompactPublicKey,
|
||||
result: *mut *mut FheUint256,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
let public_key = get_ref_checked(public_key).unwrap();
|
||||
|
||||
let value = crate::integer::U256::from(value);
|
||||
let inner = <crate::high_level_api::FheUint256>::try_encrypt(value, &public_key.0).unwrap();
|
||||
|
||||
*result = Box::into_raw(Box::new(FheUint256(inner)));
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn compact_fhe_uint256_list_try_encrypt_with_compact_public_key_u256(
|
||||
input: *const U256,
|
||||
@@ -535,17 +564,46 @@ pub unsafe extern "C" fn compact_fhe_uint256_list_try_encrypt_with_compact_publi
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn fhe_uint256_decrypt(
|
||||
encrypted_value: *const FheUint256,
|
||||
client_key: *const ClientKey,
|
||||
result: *mut U256,
|
||||
pub unsafe extern "C" fn compact_fhe_int128_list_try_encrypt_with_compact_public_key_i128(
|
||||
input: *const I128,
|
||||
input_len: usize,
|
||||
public_key: *const CompactPublicKey,
|
||||
result: *mut *mut CompactFheInt128List,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
let client_key = get_ref_checked(client_key).unwrap();
|
||||
let encrypted_value = get_ref_checked(encrypted_value).unwrap();
|
||||
let public_key = get_ref_checked(public_key).unwrap();
|
||||
|
||||
let inner: crate::integer::U256 = encrypted_value.0.decrypt(&client_key.0);
|
||||
*result = U256::from(inner);
|
||||
let slc = ::std::slice::from_raw_parts(input, input_len);
|
||||
let values = slc.iter().copied().map(i128::from).collect::<Vec<_>>();
|
||||
let inner =
|
||||
<crate::high_level_api::CompactFheInt128List>::try_encrypt(&values, &public_key.0)
|
||||
.unwrap();
|
||||
|
||||
*result = Box::into_raw(Box::new(CompactFheInt128List(inner)));
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn compact_fhe_int256_list_try_encrypt_with_compact_public_key_i256(
|
||||
input: *const I256,
|
||||
input_len: usize,
|
||||
public_key: *const CompactPublicKey,
|
||||
result: *mut *mut CompactFheInt256List,
|
||||
) -> c_int {
|
||||
catch_panic(|| {
|
||||
let public_key = get_ref_checked(public_key).unwrap();
|
||||
|
||||
let slc = ::std::slice::from_raw_parts(input, input_len);
|
||||
let values = slc
|
||||
.iter()
|
||||
.copied()
|
||||
.map(crate::integer::I256::from)
|
||||
.collect::<Vec<_>>();
|
||||
let inner =
|
||||
<crate::high_level_api::CompactFheInt256List>::try_encrypt(&values, &public_key.0)
|
||||
.unwrap();
|
||||
|
||||
*result = Box::into_raw(Box::new(CompactFheInt256List(inner)));
|
||||
})
|
||||
}
|
||||
|
||||
@@ -570,12 +628,34 @@ macro_rules! define_casting_operation(
|
||||
}
|
||||
);
|
||||
|
||||
define_casting_operation!(FheUint8 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256);
|
||||
define_casting_operation!(FheUint10 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256);
|
||||
define_casting_operation!(FheUint12 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256);
|
||||
define_casting_operation!(FheUint14 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256);
|
||||
define_casting_operation!(FheUint16 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256);
|
||||
define_casting_operation!(FheUint32 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256);
|
||||
define_casting_operation!(FheUint64 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256);
|
||||
define_casting_operation!(FheUint128 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256);
|
||||
define_casting_operation!(FheUint256 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256);
|
||||
define_casting_operation!(FheUint8 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256,
|
||||
FheInt8, FheInt16, FheInt32, FheInt64, FheInt128, FheInt256);
|
||||
define_casting_operation!(FheUint10 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256,
|
||||
FheInt8, FheInt16, FheInt32, FheInt64, FheInt128, FheInt256);
|
||||
define_casting_operation!(FheUint12 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256,
|
||||
FheInt8, FheInt16, FheInt32, FheInt64, FheInt128, FheInt256);
|
||||
define_casting_operation!(FheUint14 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256,
|
||||
FheInt8, FheInt16, FheInt32, FheInt64, FheInt128, FheInt256);
|
||||
define_casting_operation!(FheUint16 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256,
|
||||
FheInt8, FheInt16, FheInt32, FheInt64, FheInt128, FheInt256);
|
||||
define_casting_operation!(FheUint32 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256,
|
||||
FheInt8, FheInt16, FheInt32, FheInt64, FheInt128, FheInt256);
|
||||
define_casting_operation!(FheUint64 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256,
|
||||
FheInt8, FheInt16, FheInt32, FheInt64, FheInt128, FheInt256);
|
||||
define_casting_operation!(FheUint128 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256,
|
||||
FheInt8, FheInt16, FheInt32, FheInt64, FheInt128, FheInt256);
|
||||
define_casting_operation!(FheUint256 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256,
|
||||
FheInt8, FheInt16, FheInt32, FheInt64, FheInt128, FheInt256);
|
||||
|
||||
define_casting_operation!(FheInt8 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256,
|
||||
FheInt8, FheInt16, FheInt32, FheInt64, FheInt128, FheInt256);
|
||||
define_casting_operation!(FheInt16 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256,
|
||||
FheInt8, FheInt16, FheInt32, FheInt64, FheInt128, FheInt256);
|
||||
define_casting_operation!(FheInt32 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256,
|
||||
FheInt8, FheInt16, FheInt32, FheInt64, FheInt128, FheInt256);
|
||||
define_casting_operation!(FheInt64 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256,
|
||||
FheInt8, FheInt16, FheInt32, FheInt64, FheInt128, FheInt256);
|
||||
define_casting_operation!(FheInt128 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256,
|
||||
FheInt8, FheInt16, FheInt32, FheInt64, FheInt128, FheInt256);
|
||||
define_casting_operation!(FheInt256 => FheUint8, FheUint10, FheUint14, FheUint16, FheUint32, FheUint64, FheUint128, FheUint256,
|
||||
FheInt8, FheInt16, FheInt32, FheInt64, FheInt128, FheInt256);
|
||||
|
||||
@@ -4,6 +4,10 @@ mod utils;
|
||||
pub mod booleans;
|
||||
pub mod config;
|
||||
#[cfg(feature = "integer")]
|
||||
pub mod i128;
|
||||
#[cfg(feature = "integer")]
|
||||
pub mod i256;
|
||||
#[cfg(feature = "integer")]
|
||||
pub mod integers;
|
||||
pub mod keys;
|
||||
#[cfg(feature = "integer")]
|
||||
|
||||
@@ -1,3 +1,48 @@
|
||||
/// The C Standard only define integers from u8 to u64.
|
||||
/// So to support u128 and u256 we had to create our own C friendly
|
||||
/// data types.
|
||||
///
|
||||
/// This trait exists to be able to easily write wrapper function
|
||||
/// by allowing to generically go from a C API scalar type to a rust one
|
||||
pub(in crate::c_api::high_level_api) trait CApiIntegerType:
|
||||
From<Self::RustEquivalent>
|
||||
{
|
||||
type RustEquivalent: From<Self>;
|
||||
|
||||
fn to_rust(self) -> Self::RustEquivalent {
|
||||
Self::RustEquivalent::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_c_api_integer_type(
|
||||
// For when the C Integer type is _not_ the same as the Rust Integer type
|
||||
($c_type:ty => $rust_type:ty) => {
|
||||
impl CApiIntegerType for $c_type {
|
||||
type RustEquivalent = $rust_type;
|
||||
}
|
||||
};
|
||||
// For when the C Integer type is the same as Rust Integer type
|
||||
($type:ty) => {
|
||||
impl CApiIntegerType for $type {
|
||||
type RustEquivalent = $type;
|
||||
}
|
||||
};
|
||||
);
|
||||
|
||||
impl_c_api_integer_type!(bool);
|
||||
impl_c_api_integer_type!(u8);
|
||||
impl_c_api_integer_type!(u16);
|
||||
impl_c_api_integer_type!(u32);
|
||||
impl_c_api_integer_type!(u64);
|
||||
impl_c_api_integer_type!(i8);
|
||||
impl_c_api_integer_type!(i16);
|
||||
impl_c_api_integer_type!(i32);
|
||||
impl_c_api_integer_type!(i64);
|
||||
impl_c_api_integer_type!(crate::c_api::high_level_api::u128::U128 => u128);
|
||||
impl_c_api_integer_type!(crate::c_api::high_level_api::i128::I128 => i128);
|
||||
impl_c_api_integer_type!(crate::c_api::high_level_api::u256::U256 => crate::integer::U256);
|
||||
impl_c_api_integer_type!(crate::c_api::high_level_api::i256::I256 => crate::integer::I256);
|
||||
|
||||
macro_rules! impl_destroy_on_type {
|
||||
($wrapper_type:ty) => {
|
||||
::paste::paste! {
|
||||
@@ -27,6 +72,7 @@ macro_rules! impl_try_encrypt_with_client_key_on_type {
|
||||
result: *mut *mut $wrapper_type,
|
||||
) -> ::std::os::raw::c_int {
|
||||
$crate::c_api::utils::catch_panic(|| {
|
||||
let value = <$input_type as $crate::c_api::high_level_api::utils::CApiIntegerType>::to_rust(value);
|
||||
let client_key = $crate::c_api::utils::get_ref_checked(client_key).unwrap();
|
||||
|
||||
let inner = <$wrapped_type>::try_encrypt(value, &client_key.0).unwrap();
|
||||
@@ -48,6 +94,8 @@ macro_rules! impl_try_encrypt_with_public_key_on_type {
|
||||
result: *mut *mut $wrapper_type,
|
||||
) -> ::std::os::raw::c_int {
|
||||
$crate::c_api::utils::catch_panic(|| {
|
||||
let value = <$input_type as $crate::c_api::high_level_api::utils::CApiIntegerType>::to_rust(value);
|
||||
|
||||
let public_key = $crate::c_api::utils::get_ref_checked(public_key).unwrap();
|
||||
|
||||
let inner = <$wrapped_type>::try_encrypt(value, &public_key.0).unwrap();
|
||||
@@ -69,6 +117,8 @@ macro_rules! impl_try_encrypt_with_compact_public_key_on_type {
|
||||
result: *mut *mut $wrapper_type,
|
||||
) -> ::std::os::raw::c_int {
|
||||
$crate::c_api::utils::catch_panic(|| {
|
||||
let value = <$input_type as $crate::c_api::high_level_api::utils::CApiIntegerType>::to_rust(value);
|
||||
|
||||
let public_key = $crate::c_api::utils::get_ref_checked(public_key).unwrap();
|
||||
|
||||
let inner = <$wrapped_type>::try_encrypt(value, &public_key.0).unwrap();
|
||||
@@ -111,6 +161,7 @@ macro_rules! impl_try_encrypt_trivial_on_type {
|
||||
result: *mut *mut $wrapper_type,
|
||||
) -> ::std::os::raw::c_int {
|
||||
$crate::c_api::utils::catch_panic(|| {
|
||||
let value = <$input_type as $crate::c_api::high_level_api::utils::CApiIntegerType>::to_rust(value);
|
||||
|
||||
let inner = <$wrapped_type>::try_encrypt_trivial(value).unwrap();
|
||||
|
||||
@@ -133,7 +184,11 @@ macro_rules! impl_decrypt_on_type {
|
||||
let client_key = $crate::c_api::utils::get_ref_checked(client_key).unwrap();
|
||||
let encrypted_value = $crate::c_api::utils::get_ref_checked(encrypted_value).unwrap();
|
||||
|
||||
*result = encrypted_value.0.decrypt(&client_key.0);
|
||||
type RustScalarType_ = <$output_type as $crate::c_api::high_level_api::utils::CApiIntegerType>::RustEquivalent;
|
||||
|
||||
let rust_clear: RustScalarType_ = encrypted_value.0.decrypt(&client_key.0);
|
||||
|
||||
*result = <$output_type>::from(rust_clear);
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -206,15 +261,93 @@ macro_rules! impl_serialize_deserialize_on_type {
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_safe_serialize_on_type {
|
||||
($wrapper_type:ty) => {
|
||||
::paste::paste! {
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn [<$wrapper_type:snake _safe_serialize>](
|
||||
sself: *const $wrapper_type,
|
||||
result: *mut crate::c_api::buffer::Buffer,
|
||||
serialized_size_limit: u64,
|
||||
) -> ::std::os::raw::c_int {
|
||||
crate::c_api::utils::catch_panic(|| {
|
||||
crate::c_api::utils::check_ptr_is_non_null_and_aligned(result).unwrap();
|
||||
|
||||
let mut buffer = vec![];
|
||||
|
||||
let sself = crate::c_api::utils::get_ref_checked(sself).unwrap();
|
||||
|
||||
crate::high_level_api::safe_serialize(&sself.0, &mut buffer, serialized_size_limit)
|
||||
.unwrap();
|
||||
|
||||
*result = buffer.into();
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_safe_deserialize_conformant_integer {
|
||||
($wrapper_type:ty, $function_name:path) => {
|
||||
::paste::paste! {
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn [<$wrapper_type:snake _safe_deserialize_conformant>](
|
||||
buffer_view: crate::c_api::buffer::BufferView,
|
||||
serialized_size_limit: u64,
|
||||
server_key: *const crate::c_api::high_level_api::keys::ServerKey,
|
||||
result: *mut *mut $wrapper_type,
|
||||
) -> ::std::os::raw::c_int {
|
||||
crate::c_api::utils::catch_panic(|| {
|
||||
crate::c_api::utils::check_ptr_is_non_null_and_aligned(result).unwrap();
|
||||
|
||||
let sk = crate::c_api::utils::get_ref_checked(server_key).unwrap();
|
||||
|
||||
let buffer_view: &[u8] = buffer_view.into();
|
||||
|
||||
// First fill the result with a null ptr so that if we fail and the return code is not
|
||||
// checked, then any access to the result pointer will segfault (mimics malloc on failure)
|
||||
*result = std::ptr::null_mut();
|
||||
|
||||
let object: $wrapper_type = $wrapper_type(
|
||||
$function_name(
|
||||
buffer_view,
|
||||
serialized_size_limit,
|
||||
&sk.0,
|
||||
)
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let heap_allocated_object = Box::new(object);
|
||||
|
||||
*result = Box::into_raw(heap_allocated_object);
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_binary_fn_on_type {
|
||||
($wrapper_type:ty => $($binary_fn_name:ident),* $(,)?) => {
|
||||
$(
|
||||
::paste::paste! {
|
||||
// More general binary fn case,
|
||||
// where the type of the left-hand side can be different
|
||||
// than the type of the right-hand side.
|
||||
//
|
||||
// The result type is the one of the left-hand side.
|
||||
//
|
||||
// In practice, this is used for shifts on signed type,
|
||||
// where lhs is a signed type and rhs is an unsigned type
|
||||
(
|
||||
lhs_type: $lhs_type:ty,
|
||||
rhs_type: $rhs_type:ty,
|
||||
binary_fn_names: $($binary_fn_name:ident),*
|
||||
$(,)?
|
||||
) => {
|
||||
$( // unroll binary_fn_names
|
||||
::paste::paste! {
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn [<$wrapper_type:snake _ $binary_fn_name>](
|
||||
lhs: *const $wrapper_type,
|
||||
rhs: *const $wrapper_type,
|
||||
result: *mut *mut $wrapper_type,
|
||||
pub unsafe extern "C" fn [<$lhs_type:snake _ $binary_fn_name>](
|
||||
lhs: *const $lhs_type,
|
||||
rhs: *const $rhs_type,
|
||||
result: *mut *mut $lhs_type,
|
||||
) -> ::std::os::raw::c_int {
|
||||
$crate::c_api::utils::catch_panic(|| {
|
||||
let lhs = $crate::c_api::utils::get_ref_checked(lhs).unwrap();
|
||||
@@ -222,11 +355,21 @@ macro_rules! impl_binary_fn_on_type {
|
||||
|
||||
let inner = (&lhs.0).$binary_fn_name(&rhs.0);
|
||||
|
||||
*result = Box::into_raw(Box::new($wrapper_type(inner)));
|
||||
*result = Box::into_raw(Box::new($lhs_type(inner)));
|
||||
})
|
||||
}
|
||||
}
|
||||
)*
|
||||
)*
|
||||
|
||||
};
|
||||
|
||||
// Usual binary fn case, where lhs, rhs and result are all of the same type
|
||||
($wrapper_type:ty => $($binary_fn_name:ident),* $(,)?) => {
|
||||
impl_binary_fn_on_type!(
|
||||
lhs_type: $wrapper_type,
|
||||
rhs_type: $wrapper_type,
|
||||
binary_fn_names: $($binary_fn_name),*
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -254,13 +397,24 @@ macro_rules! impl_unary_fn_on_type {
|
||||
|
||||
#[cfg(feature = "integer")]
|
||||
macro_rules! impl_binary_assign_fn_on_type {
|
||||
($wrapper_type:ty => $($binary_assign_fn_name:ident),* $(,)?) => {
|
||||
// More general binary fn case,
|
||||
// where the type of the left-hand side can be different
|
||||
// than the type of the right-hand side.
|
||||
//
|
||||
// In practice, this is used for shifts on signed type,
|
||||
// where lhs is a signed type and rhs is an unsigned type
|
||||
(
|
||||
lhs_type: $lhs_type:ty,
|
||||
rhs_type: $rhs_type:ty,
|
||||
binary_fn_names: $($binary_assign_fn_name:ident),*
|
||||
$(,)?
|
||||
) => {
|
||||
$(
|
||||
::paste::paste! {
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn [<$wrapper_type:snake _ $binary_assign_fn_name>](
|
||||
lhs: *mut $wrapper_type,
|
||||
rhs: *const $wrapper_type,
|
||||
pub unsafe extern "C" fn [<$lhs_type:snake _ $binary_assign_fn_name>](
|
||||
lhs: *mut $lhs_type,
|
||||
rhs: *const $rhs_type,
|
||||
) -> ::std::os::raw::c_int {
|
||||
$crate::c_api::utils::catch_panic(|| {
|
||||
let lhs = $crate::c_api::utils::get_mut_checked(lhs).unwrap();
|
||||
@@ -272,53 +426,13 @@ macro_rules! impl_binary_assign_fn_on_type {
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
/// The C Standard only define integers from u8 to u64.
|
||||
/// So to support u128 and u256 we had to create our own C friendly
|
||||
/// data types.
|
||||
///
|
||||
/// This trait exists to be able to easily write wrapper function
|
||||
/// by allowing to generically go from a C API scalar type to a rust one
|
||||
pub(in crate::c_api::high_level_api) trait ToRustScalarType {
|
||||
type RustScalarType;
|
||||
|
||||
fn to_rust_scalar_type(self) -> Self::RustScalarType;
|
||||
}
|
||||
|
||||
/// Implements the trait for when the C API type is the same
|
||||
/// as the Rust API type (eg u64)
|
||||
macro_rules! impl_to_rust_scalar_type(
|
||||
($type:ty) => {
|
||||
impl ToRustScalarType for $type {
|
||||
type RustScalarType = $type;
|
||||
|
||||
fn to_rust_scalar_type(self) -> Self::RustScalarType {
|
||||
self
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
impl_to_rust_scalar_type!(u8);
|
||||
impl_to_rust_scalar_type!(u16);
|
||||
impl_to_rust_scalar_type!(u32);
|
||||
impl_to_rust_scalar_type!(u64);
|
||||
|
||||
impl ToRustScalarType for crate::c_api::high_level_api::u128::U128 {
|
||||
type RustScalarType = u128;
|
||||
|
||||
fn to_rust_scalar_type(self) -> Self::RustScalarType {
|
||||
u128::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToRustScalarType for crate::c_api::high_level_api::u256::U256 {
|
||||
type RustScalarType = crate::integer::U256;
|
||||
|
||||
fn to_rust_scalar_type(self) -> Self::RustScalarType {
|
||||
crate::integer::U256::from(self)
|
||||
}
|
||||
($wrapper_type:ty => $($binary_assign_fn_name:ident),* $(,)?) => {
|
||||
impl_binary_assign_fn_on_type!(
|
||||
lhs_type: $wrapper_type,
|
||||
rhs_type: $wrapper_type,
|
||||
binary_fn_names: $($binary_assign_fn_name),*
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "integer")]
|
||||
@@ -334,7 +448,7 @@ macro_rules! impl_scalar_binary_fn_on_type {
|
||||
) -> c_int {
|
||||
$crate::c_api::utils::catch_panic(|| {
|
||||
let lhs = $crate::c_api::utils::get_ref_checked(lhs).unwrap();
|
||||
let rhs = <$scalar_type as $crate::c_api::high_level_api::utils::ToRustScalarType>::to_rust_scalar_type(rhs);
|
||||
let rhs = <$scalar_type as $crate::c_api::high_level_api::utils::CApiIntegerType>::to_rust(rhs);
|
||||
|
||||
let inner = (&lhs.0).$binary_fn_name(rhs);
|
||||
|
||||
@@ -358,8 +472,8 @@ macro_rules! impl_scalar_binary_assign_fn_on_type {
|
||||
) -> c_int {
|
||||
$crate::c_api::utils::catch_panic(|| {
|
||||
let lhs = $crate::c_api::utils::get_mut_checked(lhs).unwrap();
|
||||
let rhs = <$scalar_type as $crate::c_api::high_level_api::utils::ToRustScalarType
|
||||
>::to_rust_scalar_type(rhs);
|
||||
let rhs = <$scalar_type as $crate::c_api::high_level_api::utils::CApiIntegerType
|
||||
>::to_rust(rhs);
|
||||
|
||||
lhs.0.$binary_assign_fn_name(rhs);
|
||||
})
|
||||
|
||||
@@ -119,7 +119,7 @@ pub unsafe extern "C" fn shortint_server_key_generate_bivariate_pbs_lookup_table
|
||||
pub unsafe extern "C" fn shortint_server_key_bivariate_programmable_bootstrap(
|
||||
server_key: *const ShortintServerKey,
|
||||
lookup_table: *const ShortintBivariatePBSLookupTable,
|
||||
ct_left: *const ShortintCiphertext,
|
||||
ct_left: *mut ShortintCiphertext,
|
||||
ct_right: *mut ShortintCiphertext,
|
||||
result: *mut *mut ShortintCiphertext,
|
||||
) -> c_int {
|
||||
@@ -132,14 +132,14 @@ pub unsafe extern "C" fn shortint_server_key_bivariate_programmable_bootstrap(
|
||||
|
||||
let server_key = get_ref_checked(server_key).unwrap();
|
||||
let lookup_table = get_ref_checked(lookup_table).unwrap();
|
||||
let ct_left = get_ref_checked(ct_left).unwrap();
|
||||
let ct_left = get_mut_checked(ct_left).unwrap();
|
||||
let ct_right = get_mut_checked(ct_right).unwrap();
|
||||
|
||||
let res = crate::shortint::engine::ShortintEngine::with_thread_local_mut(|engine| {
|
||||
engine
|
||||
.smart_apply_lookup_table_bivariate(
|
||||
&server_key.0,
|
||||
&ct_left.0,
|
||||
&mut ct_left.0,
|
||||
&mut ct_right.0,
|
||||
&lookup_table.0,
|
||||
)
|
||||
|
||||
@@ -17,7 +17,7 @@ pub fn check_ptr_is_non_null_and_aligned<T>(ptr: *const T) -> Result<(), String>
|
||||
let expected_alignment = std::mem::align_of::<T>();
|
||||
if ptr as usize % expected_alignment != 0 {
|
||||
return Err(format!(
|
||||
"pointer is misaligned, expected {expected_alignment} bytes alignement, got pointer: \
|
||||
"pointer is misaligned, expected {expected_alignment} bytes alignment, got pointer: \
|
||||
{ptr:p}. You May have mixed some pointers in your function call. If that's not the \
|
||||
case check tfhe.h for alignment constants for plain data types allocation.",
|
||||
));
|
||||
|
||||
70
tfhe/src/conformance.rs
Normal file
70
tfhe/src/conformance.rs
Normal file
@@ -0,0 +1,70 @@
|
||||
/// A trait for objects which can be checked to be conformant with a parameter set
|
||||
pub trait ParameterSetConformant {
|
||||
type ParameterSet;
|
||||
|
||||
fn is_conformant(&self, parameter_set: &Self::ParameterSet) -> bool;
|
||||
}
|
||||
|
||||
/// A constraint on a list size
|
||||
/// The list must be composed of a number `n` of groups of size `group_size` which means list size
|
||||
/// must be a multiple of `group_size`.
|
||||
/// Moreover, `n` must be:
|
||||
/// - bigger or equal to `min_inclusive_group_count`
|
||||
/// - smaller of equal to `max_inclusive_group_count`
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct ListSizeConstraint {
|
||||
min_inclusive_group_count: usize,
|
||||
max_inclusive_group_count: usize,
|
||||
group_size: usize,
|
||||
}
|
||||
|
||||
impl ListSizeConstraint {
|
||||
pub fn exact_size(size: usize) -> ListSizeConstraint {
|
||||
ListSizeConstraint {
|
||||
min_inclusive_group_count: size,
|
||||
max_inclusive_group_count: size,
|
||||
group_size: 1,
|
||||
}
|
||||
}
|
||||
pub fn try_size_in_range(
|
||||
min_inclusive: usize,
|
||||
max_inclusive: usize,
|
||||
) -> Result<ListSizeConstraint, String> {
|
||||
if max_inclusive < min_inclusive {
|
||||
return Err("max_inclusive < min_inclusive".to_owned());
|
||||
}
|
||||
Ok(ListSizeConstraint {
|
||||
min_inclusive_group_count: min_inclusive,
|
||||
max_inclusive_group_count: max_inclusive,
|
||||
group_size: 1,
|
||||
})
|
||||
}
|
||||
pub fn try_size_of_group_in_range(
|
||||
group_size: usize,
|
||||
min_inclusive_group_count: usize,
|
||||
max_inclusive_group_count: usize,
|
||||
) -> Result<ListSizeConstraint, String> {
|
||||
if max_inclusive_group_count < min_inclusive_group_count {
|
||||
return Err("max_inclusive < min_inclusive".to_owned());
|
||||
}
|
||||
Ok(ListSizeConstraint {
|
||||
min_inclusive_group_count,
|
||||
max_inclusive_group_count,
|
||||
group_size,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn multiply_group_size(&self, group_size_multiplier: usize) -> Self {
|
||||
ListSizeConstraint {
|
||||
min_inclusive_group_count: self.min_inclusive_group_count,
|
||||
max_inclusive_group_count: self.max_inclusive_group_count,
|
||||
group_size: self.group_size * group_size_multiplier,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_valid(&self, size: usize) -> bool {
|
||||
size % self.group_size == 0
|
||||
&& size >= self.min_inclusive_group_count * self.group_size
|
||||
&& size <= self.max_inclusive_group_count * self.group_size
|
||||
}
|
||||
}
|
||||
@@ -831,7 +831,7 @@ where
|
||||
|
||||
/// Convenience function to share the core logic of the seeded GLWE encryption between all
|
||||
/// functions needing it.
|
||||
pub fn encrypt_seeded_glwe_ciphertext_with_exsiting_generator<
|
||||
pub fn encrypt_seeded_glwe_ciphertext_with_existing_generator<
|
||||
Scalar,
|
||||
KeyCont,
|
||||
OutputCont,
|
||||
@@ -988,7 +988,7 @@ pub fn encrypt_seeded_glwe_ciphertext<Scalar, KeyCont, InputCont, OutputCont, No
|
||||
noise_seeder,
|
||||
);
|
||||
|
||||
encrypt_seeded_glwe_ciphertext_with_exsiting_generator(
|
||||
encrypt_seeded_glwe_ciphertext_with_existing_generator(
|
||||
glwe_secret_key,
|
||||
output_glwe_ciphertext,
|
||||
input_plaintext_list,
|
||||
|
||||
@@ -644,7 +644,7 @@ where
|
||||
slice_wrapping_opposite_assign(ct.as_mut());
|
||||
}
|
||||
|
||||
/// Mulitply the left-hand side [`GLWE ciphertext`](`GlweCiphertext`) by the right-hand side
|
||||
/// Multiply the left-hand side [`GLWE ciphertext`](`GlweCiphertext`) by the right-hand side
|
||||
/// cleartext updating it in-place.
|
||||
///
|
||||
/// # Example
|
||||
@@ -727,7 +727,7 @@ pub fn glwe_ciphertext_cleartext_mul_assign<Scalar, InCont>(
|
||||
slice_wrapping_scalar_mul_assign(lhs.as_mut(), rhs.0);
|
||||
}
|
||||
|
||||
/// Mulitply the left-hand side [`GLWE ciphertext`](`GlweCiphertext`) by the right-hand side
|
||||
/// Multiply the left-hand side [`GLWE ciphertext`](`GlweCiphertext`) by the right-hand side
|
||||
/// cleartext writing the result in the output [`GLWE ciphertext`](`GlweCiphertext`).
|
||||
///
|
||||
/// # Example
|
||||
|
||||
@@ -2,11 +2,13 @@
|
||||
//! _sample extract_ in the literature. Allowing to extract a single
|
||||
//! [`LWE Ciphertext`](`LweCiphertext`) from a given [`GLWE ciphertext`](`GlweCiphertext`).
|
||||
|
||||
use crate::core_crypto::algorithms::misc::divide_ceil;
|
||||
use crate::core_crypto::algorithms::slice_algorithms::*;
|
||||
use crate::core_crypto::commons::numeric::UnsignedInteger;
|
||||
use crate::core_crypto::commons::parameters::{MonomialDegree, *};
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use rayon::prelude::*;
|
||||
|
||||
/// Extract the nth coefficient from the body of a [`GLWE Ciphertext`](`GlweCiphertext`) as an
|
||||
/// [`LWE ciphertext`](`LweCiphertext`).
|
||||
@@ -95,13 +97,18 @@ pub fn extract_lwe_sample_from_glwe_ciphertext<Scalar, InputCont, OutputCont>(
|
||||
InputCont: Container<Element = Scalar>,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
assert!(
|
||||
input_glwe.glwe_size().to_glwe_dimension().0 * input_glwe.polynomial_size().0
|
||||
== output_lwe.lwe_size().to_lwe_dimension().0,
|
||||
let in_lwe_dim = input_glwe
|
||||
.glwe_size()
|
||||
.to_glwe_dimension()
|
||||
.to_equivalent_lwe_dimension(input_glwe.polynomial_size());
|
||||
|
||||
let out_lwe_dim = output_lwe.lwe_size().to_lwe_dimension();
|
||||
|
||||
assert_eq!(
|
||||
in_lwe_dim, out_lwe_dim,
|
||||
"Mismatch between equivalent LweDimension of input ciphertext and output ciphertext. \
|
||||
Got {:?} for input and {:?} for output.",
|
||||
LweDimension(input_glwe.glwe_size().to_glwe_dimension().0 * input_glwe.polynomial_size().0),
|
||||
output_lwe.lwe_size().to_lwe_dimension(),
|
||||
in_lwe_dim, out_lwe_dim,
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
@@ -139,3 +146,294 @@ pub fn extract_lwe_sample_from_glwe_ciphertext<Scalar, InputCont, OutputCont>(
|
||||
lwe_mask_poly.rotate_left(opposite_count);
|
||||
}
|
||||
}
|
||||
|
||||
/// Parallel variant of [`extract_lwe_sample_from_glwe_ciphertext`] performing a sample extract on
|
||||
/// all coefficients from a [`GlweCiphertext`] in an output [`LweCiphertextList`].
|
||||
///
|
||||
/// This will use all threads available in the current rayon thread pool.
|
||||
///
|
||||
/// # Formal definition
|
||||
///
|
||||
/// This operation is usually referred to as a _sample extract_ in the literature.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for GlweCiphertext creation
|
||||
/// let glwe_size = GlweSize(2);
|
||||
/// let polynomial_size = PolynomialSize(1024);
|
||||
/// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
/// // Create the PRNG
|
||||
/// let mut seeder = new_seeder();
|
||||
/// let seeder = seeder.as_mut();
|
||||
/// let mut encryption_generator =
|
||||
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
/// let mut secret_generator =
|
||||
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
///
|
||||
/// // Create the GlweSecretKey
|
||||
/// let glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
|
||||
/// glwe_size.to_glwe_dimension(),
|
||||
/// polynomial_size,
|
||||
/// &mut secret_generator,
|
||||
/// );
|
||||
///
|
||||
/// // Create the plaintext
|
||||
/// let msg = 3u64;
|
||||
/// let encoded_msg = msg << 60;
|
||||
/// let mut plaintext_list = PlaintextList::new(encoded_msg, PlaintextCount(polynomial_size.0));
|
||||
///
|
||||
/// // Create a new GlweCiphertext
|
||||
/// let mut glwe = GlweCiphertext::new(0u64, glwe_size, polynomial_size, ciphertext_modulus);
|
||||
///
|
||||
/// encrypt_glwe_ciphertext(
|
||||
/// &glwe_secret_key,
|
||||
/// &mut glwe,
|
||||
/// &plaintext_list,
|
||||
/// glwe_modular_std_dev,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
///
|
||||
/// // Now we get the equivalent LweSecretKey from the GlweSecretKey
|
||||
/// let equivalent_lwe_sk = glwe_secret_key.clone().into_lwe_secret_key();
|
||||
///
|
||||
/// let mut extracted_samples = LweCiphertextList::new(
|
||||
/// 0u64,
|
||||
/// equivalent_lwe_sk.lwe_dimension().to_lwe_size(),
|
||||
/// LweCiphertextCount(glwe.polynomial_size().0),
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// // Use all threads available in the current rayon thread pool
|
||||
/// par_extract_lwe_sample_from_glwe_ciphertext(&glwe, &mut extracted_samples);
|
||||
///
|
||||
/// let mut output_plaintext_list = PlaintextList::new(
|
||||
/// 0u64,
|
||||
/// PlaintextCount(extracted_samples.lwe_ciphertext_count().0),
|
||||
/// );
|
||||
///
|
||||
/// decrypt_lwe_ciphertext_list(
|
||||
/// &equivalent_lwe_sk,
|
||||
/// &extracted_samples,
|
||||
/// &mut output_plaintext_list,
|
||||
/// );
|
||||
///
|
||||
/// // Round and remove encoding
|
||||
/// // First create a decomposer working on the high 4 bits corresponding to our encoding.
|
||||
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
|
||||
///
|
||||
/// output_plaintext_list
|
||||
/// .iter_mut()
|
||||
/// .for_each(|x| *x.0 = decomposer.closest_representable(*x.0) >> 60);
|
||||
///
|
||||
/// // We check we recover our msg stored in all slots of the GlweCiphertext
|
||||
/// assert!(output_plaintext_list.iter().all(|x| *x.0 == msg));
|
||||
/// ```
|
||||
pub fn par_extract_lwe_sample_from_glwe_ciphertext<Scalar, InputCont, OutputCont>(
|
||||
input_glwe: &GlweCiphertext<InputCont>,
|
||||
output_lwe_list: &mut LweCiphertextList<OutputCont>,
|
||||
) where
|
||||
Scalar: UnsignedInteger + Send + Sync,
|
||||
InputCont: Container<Element = Scalar> + Sync,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let thread_count = ThreadCount(rayon::current_num_threads());
|
||||
par_extract_lwe_sample_from_glwe_ciphertext_with_thread_count(
|
||||
input_glwe,
|
||||
output_lwe_list,
|
||||
thread_count,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parallel variant of [`extract_lwe_sample_from_glwe_ciphertext`] performing a sample extract on
|
||||
/// all coefficients from a [`GlweCiphertext`] in an output [`LweCiphertextList`].
|
||||
///
|
||||
/// This will try to use `thread_count` threads for the computation, if this number is bigger than
|
||||
/// the available number of threads in the current rayon thread pool then only the number of
|
||||
/// available threads will be used. Note that `thread_count` cannot be 0.
|
||||
///
|
||||
/// # Formal definition
|
||||
///
|
||||
/// This operation is usually referred to as a _sample extract_ in the literature.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
/// // Define parameters for GlweCiphertext creation
|
||||
/// let glwe_size = GlweSize(2);
|
||||
/// let polynomial_size = PolynomialSize(1024);
|
||||
/// let glwe_modular_std_dev = StandardDev(0.00000000000000029403601535432533);
|
||||
/// let ciphertext_modulus = CiphertextModulus::new_native();
|
||||
///
|
||||
/// // Create the PRNG
|
||||
/// let mut seeder = new_seeder();
|
||||
/// let seeder = seeder.as_mut();
|
||||
/// let mut encryption_generator =
|
||||
/// EncryptionRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed(), seeder);
|
||||
/// let mut secret_generator =
|
||||
/// SecretRandomGenerator::<ActivatedRandomGenerator>::new(seeder.seed());
|
||||
///
|
||||
/// // Create the GlweSecretKey
|
||||
/// let glwe_secret_key = allocate_and_generate_new_binary_glwe_secret_key(
|
||||
/// glwe_size.to_glwe_dimension(),
|
||||
/// polynomial_size,
|
||||
/// &mut secret_generator,
|
||||
/// );
|
||||
///
|
||||
/// // Create the plaintext
|
||||
/// let msg = 3u64;
|
||||
/// let encoded_msg = msg << 60;
|
||||
/// let mut plaintext_list = PlaintextList::new(encoded_msg, PlaintextCount(polynomial_size.0));
|
||||
///
|
||||
/// // Create a new GlweCiphertext
|
||||
/// let mut glwe = GlweCiphertext::new(0u64, glwe_size, polynomial_size, ciphertext_modulus);
|
||||
///
|
||||
/// encrypt_glwe_ciphertext(
|
||||
/// &glwe_secret_key,
|
||||
/// &mut glwe,
|
||||
/// &plaintext_list,
|
||||
/// glwe_modular_std_dev,
|
||||
/// &mut encryption_generator,
|
||||
/// );
|
||||
///
|
||||
/// // Now we get the equivalent LweSecretKey from the GlweSecretKey
|
||||
/// let equivalent_lwe_sk = glwe_secret_key.clone().into_lwe_secret_key();
|
||||
///
|
||||
/// let mut extracted_samples = LweCiphertextList::new(
|
||||
/// 0u64,
|
||||
/// equivalent_lwe_sk.lwe_dimension().to_lwe_size(),
|
||||
/// LweCiphertextCount(glwe.polynomial_size().0),
|
||||
/// ciphertext_modulus,
|
||||
/// );
|
||||
///
|
||||
/// // Try to use 4 threads for the keyswitch if enough are available
|
||||
/// // in the current rayon thread pool
|
||||
/// par_extract_lwe_sample_from_glwe_ciphertext_with_thread_count(
|
||||
/// &glwe,
|
||||
/// &mut extracted_samples,
|
||||
/// ThreadCount(4),
|
||||
/// );
|
||||
///
|
||||
/// let mut output_plaintext_list = PlaintextList::new(
|
||||
/// 0u64,
|
||||
/// PlaintextCount(extracted_samples.lwe_ciphertext_count().0),
|
||||
/// );
|
||||
///
|
||||
/// decrypt_lwe_ciphertext_list(
|
||||
/// &equivalent_lwe_sk,
|
||||
/// &extracted_samples,
|
||||
/// &mut output_plaintext_list,
|
||||
/// );
|
||||
///
|
||||
/// // Round and remove encoding
|
||||
/// // First create a decomposer working on the high 4 bits corresponding to our encoding.
|
||||
/// let decomposer = SignedDecomposer::new(DecompositionBaseLog(4), DecompositionLevelCount(1));
|
||||
///
|
||||
/// output_plaintext_list
|
||||
/// .iter_mut()
|
||||
/// .for_each(|x| *x.0 = decomposer.closest_representable(*x.0) >> 60);
|
||||
///
|
||||
/// // We check we recover our msg stored in all slots of the GlweCiphertext
|
||||
/// assert!(output_plaintext_list.iter().all(|x| *x.0 == msg));
|
||||
/// ```
|
||||
pub fn par_extract_lwe_sample_from_glwe_ciphertext_with_thread_count<
|
||||
Scalar,
|
||||
InputCont,
|
||||
OutputCont,
|
||||
>(
|
||||
input_glwe: &GlweCiphertext<InputCont>,
|
||||
output_lwe_list: &mut LweCiphertextList<OutputCont>,
|
||||
thread_count: ThreadCount,
|
||||
) where
|
||||
Scalar: UnsignedInteger + Send + Sync,
|
||||
InputCont: Container<Element = Scalar> + Sync,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
{
|
||||
let in_lwe_dim = input_glwe
|
||||
.glwe_size()
|
||||
.to_glwe_dimension()
|
||||
.to_equivalent_lwe_dimension(input_glwe.polynomial_size());
|
||||
|
||||
let out_lwe_dim = output_lwe_list.lwe_size().to_lwe_dimension();
|
||||
|
||||
assert_eq!(
|
||||
in_lwe_dim, out_lwe_dim,
|
||||
"Mismatch between equivalent LweDimension of input ciphertext and output ciphertext. \
|
||||
Got {:?} for input and {:?} for output.",
|
||||
in_lwe_dim, out_lwe_dim,
|
||||
);
|
||||
|
||||
assert!(
|
||||
input_glwe.polynomial_size().0 <= output_lwe_list.lwe_ciphertext_count().0,
|
||||
"The output LweCiphertextList does not have enough space ({:?}) \
|
||||
to extract all input GlweCiphertext coefficients ({})",
|
||||
output_lwe_list.lwe_ciphertext_count(),
|
||||
input_glwe.polynomial_size().0
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
input_glwe.ciphertext_modulus(),
|
||||
output_lwe_list.ciphertext_modulus(),
|
||||
"Mismatched moduli between input_glwe ({:?}) and output_lwe ({:?})",
|
||||
input_glwe.ciphertext_modulus(),
|
||||
output_lwe_list.ciphertext_modulus()
|
||||
);
|
||||
|
||||
let polynomial_size = input_glwe.polynomial_size();
|
||||
let (glwe_mask, glwe_body) = input_glwe.get_mask_and_body();
|
||||
|
||||
let thread_count = thread_count.0.min(rayon::current_num_threads());
|
||||
let chunk_size = divide_ceil(polynomial_size.0, thread_count);
|
||||
|
||||
glwe_body
|
||||
.as_ref()
|
||||
.par_chunks(chunk_size)
|
||||
.zip(output_lwe_list.par_chunks_mut(chunk_size))
|
||||
.enumerate()
|
||||
.for_each(
|
||||
|(chunk_idx, (glwe_body_chunk, mut output_lwe_list_chunk))| {
|
||||
for (coeff_idx, (glwe_coeff, mut output_lwe)) in glwe_body_chunk
|
||||
.iter()
|
||||
.zip(output_lwe_list_chunk.iter_mut())
|
||||
.enumerate()
|
||||
{
|
||||
let nth = chunk_idx * chunk_size + coeff_idx;
|
||||
|
||||
let (mut lwe_mask, lwe_body) = output_lwe.get_mut_mask_and_body();
|
||||
|
||||
// We copy the body
|
||||
*lwe_body.data = *glwe_coeff;
|
||||
|
||||
// We copy the mask (each polynomial is in the wrong order)
|
||||
lwe_mask.as_mut().copy_from_slice(glwe_mask.as_ref());
|
||||
|
||||
// We compute the number of elements which must be
|
||||
// turned into their opposite
|
||||
let opposite_count = input_glwe.polynomial_size().0 - nth - 1;
|
||||
|
||||
// We loop through the polynomials
|
||||
for lwe_mask_poly in lwe_mask
|
||||
.as_mut()
|
||||
.chunks_exact_mut(input_glwe.polynomial_size().0)
|
||||
{
|
||||
// We reverse the polynomial
|
||||
lwe_mask_poly.reverse();
|
||||
// We compute the opposite of the proper coefficients
|
||||
slice_wrapping_opposite_assign(&mut lwe_mask_poly[0..opposite_count]);
|
||||
// We rotate the polynomial properly
|
||||
lwe_mask_poly.rotate_left(opposite_count);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ pub fn generate_lwe_compact_public_key<Scalar, InputKeyCont, OutputKeyCont, Gen>
|
||||
assert!(
|
||||
lwe_secret_key.lwe_dimension() == output.lwe_dimension(),
|
||||
"Mismatched LweDimension between input LweSecretKey {:?} \
|
||||
and ouptut LweCompactPublicKey {:?}",
|
||||
and output LweCompactPublicKey {:?}",
|
||||
lwe_secret_key.lwe_dimension(),
|
||||
output.lwe_dimension()
|
||||
);
|
||||
@@ -97,7 +97,7 @@ pub fn generate_seeded_lwe_compact_public_key<Scalar, InputKeyCont, OutputKeyCon
|
||||
assert!(
|
||||
lwe_secret_key.lwe_dimension() == output.lwe_dimension(),
|
||||
"Mismatched LweDimension between input LweSecretKey {:?} \
|
||||
and ouptut LweCompactPublicKey {:?}",
|
||||
and output LweCompactPublicKey {:?}",
|
||||
lwe_secret_key.lwe_dimension(),
|
||||
output.lwe_dimension()
|
||||
);
|
||||
|
||||
@@ -1573,7 +1573,7 @@ pub fn encrypt_lwe_ciphertext_with_compact_public_key<
|
||||
{
|
||||
assert!(
|
||||
output.lwe_size().to_lwe_dimension() == lwe_compact_public_key.lwe_dimension(),
|
||||
"Mismatch between LweDimension of output cipertext and input public key. \
|
||||
"Mismatch between LweDimension of output ciphertext and input public key. \
|
||||
Got {:?} in output, and {:?} in public key.",
|
||||
output.lwe_size().to_lwe_dimension(),
|
||||
lwe_compact_public_key.lwe_dimension()
|
||||
@@ -1581,7 +1581,7 @@ pub fn encrypt_lwe_ciphertext_with_compact_public_key<
|
||||
|
||||
assert!(
|
||||
lwe_compact_public_key.ciphertext_modulus() == output.ciphertext_modulus(),
|
||||
"Mismatch between CiphertextModulus of output cipertext and input public key. \
|
||||
"Mismatch between CiphertextModulus of output ciphertext and input public key. \
|
||||
Got {:?} in output, and {:?} in public key.",
|
||||
output.ciphertext_modulus(),
|
||||
lwe_compact_public_key.ciphertext_modulus()
|
||||
@@ -1728,7 +1728,7 @@ pub fn encrypt_lwe_compact_ciphertext_list_with_compact_public_key<
|
||||
{
|
||||
assert!(
|
||||
output.lwe_size().to_lwe_dimension() == lwe_compact_public_key.lwe_dimension(),
|
||||
"Mismatch between LweDimension of output cipertext and input public key. \
|
||||
"Mismatch between LweDimension of output ciphertext and input public key. \
|
||||
Got {:?} in output, and {:?} in public key.",
|
||||
output.lwe_size().to_lwe_dimension(),
|
||||
lwe_compact_public_key.lwe_dimension()
|
||||
@@ -1736,7 +1736,7 @@ pub fn encrypt_lwe_compact_ciphertext_list_with_compact_public_key<
|
||||
|
||||
assert!(
|
||||
lwe_compact_public_key.ciphertext_modulus() == output.ciphertext_modulus(),
|
||||
"Mismatch between CiphertextModulus of output cipertext and input public key. \
|
||||
"Mismatch between CiphertextModulus of output ciphertext and input public key. \
|
||||
Got {:?} in output, and {:?} in public key.",
|
||||
output.ciphertext_modulus(),
|
||||
lwe_compact_public_key.ciphertext_modulus()
|
||||
@@ -1744,7 +1744,7 @@ pub fn encrypt_lwe_compact_ciphertext_list_with_compact_public_key<
|
||||
|
||||
assert!(
|
||||
output.lwe_ciphertext_count().0 == encoded.plaintext_count().0,
|
||||
"Mismatch between LweCiphertextCount of output cipertext and \
|
||||
"Mismatch between LweCiphertextCount of output ciphertext and \
|
||||
PlaintextCount of input list. Got {:?} in output, and {:?} in input plaintext list.",
|
||||
output.lwe_ciphertext_count(),
|
||||
encoded.plaintext_count()
|
||||
@@ -1936,7 +1936,7 @@ pub fn par_encrypt_lwe_compact_ciphertext_list_with_compact_public_key<
|
||||
{
|
||||
assert!(
|
||||
output.lwe_size().to_lwe_dimension() == lwe_compact_public_key.lwe_dimension(),
|
||||
"Mismatch between LweDimension of output cipertext and input public key. \
|
||||
"Mismatch between LweDimension of output ciphertext and input public key. \
|
||||
Got {:?} in output, and {:?} in public key.",
|
||||
output.lwe_size().to_lwe_dimension(),
|
||||
lwe_compact_public_key.lwe_dimension()
|
||||
@@ -1944,7 +1944,7 @@ pub fn par_encrypt_lwe_compact_ciphertext_list_with_compact_public_key<
|
||||
|
||||
assert!(
|
||||
lwe_compact_public_key.ciphertext_modulus() == output.ciphertext_modulus(),
|
||||
"Mismatch between CiphertextModulus of output cipertext and input public key. \
|
||||
"Mismatch between CiphertextModulus of output ciphertext and input public key. \
|
||||
Got {:?} in output, and {:?} in public key.",
|
||||
output.ciphertext_modulus(),
|
||||
lwe_compact_public_key.ciphertext_modulus()
|
||||
@@ -1952,7 +1952,7 @@ pub fn par_encrypt_lwe_compact_ciphertext_list_with_compact_public_key<
|
||||
|
||||
assert!(
|
||||
output.lwe_ciphertext_count().0 == encoded.plaintext_count().0,
|
||||
"Mismatch between LweCiphertextCount of output cipertext and \
|
||||
"Mismatch between LweCiphertextCount of output ciphertext and \
|
||||
PlaintextCount of input list. Got {:?} in output, and {:?} in input plaintext list.",
|
||||
output.lwe_ciphertext_count(),
|
||||
encoded.plaintext_count()
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use rayon::prelude::*;
|
||||
|
||||
/// Keyswitch an [`LWE ciphertext`](`LweCiphertext`) encrytped under an
|
||||
/// Keyswitch an [`LWE ciphertext`](`LweCiphertext`) encrypted under an
|
||||
/// [`LWE secret key`](`LweSecretKey`) to another [`LWE secret key`](`LweSecretKey`).
|
||||
///
|
||||
/// # Formal Definition
|
||||
|
||||
@@ -388,7 +388,7 @@ where
|
||||
slice_wrapping_opposite_assign(ct.as_mut());
|
||||
}
|
||||
|
||||
/// Mulitply the left-hand side [`LWE ciphertext`](`LweCiphertext`) by the right-hand side cleartext
|
||||
/// Multiply the left-hand side [`LWE ciphertext`](`LweCiphertext`) by the right-hand side cleartext
|
||||
/// updating it in-place.
|
||||
///
|
||||
/// # Example
|
||||
@@ -455,7 +455,7 @@ pub fn lwe_ciphertext_cleartext_mul_assign<Scalar, InCont>(
|
||||
slice_wrapping_scalar_mul_assign(lhs.as_mut(), rhs.0);
|
||||
}
|
||||
|
||||
/// Mulitply the left-hand side [`LWE ciphertext`](`LweCiphertext`) by the right-hand side cleartext
|
||||
/// Multiply the left-hand side [`LWE ciphertext`](`LweCiphertext`) by the right-hand side cleartext
|
||||
/// writing the result in the output [`LWE ciphertext`](`LweCiphertext`).
|
||||
///
|
||||
/// # Example
|
||||
|
||||
@@ -6,7 +6,7 @@ use crate::core_crypto::commons::math::decomposition::SignedDecomposer;
|
||||
use crate::core_crypto::commons::parameters::*;
|
||||
use crate::core_crypto::commons::traits::*;
|
||||
use crate::core_crypto::entities::*;
|
||||
use crate::core_crypto::fft_impl::common::pbs_modulus_switch;
|
||||
use crate::core_crypto::fft_impl::common::fast_pbs_modulus_switch;
|
||||
use crate::core_crypto::fft_impl::fft64::crypto::ggsw::{
|
||||
add_external_product_assign, add_external_product_assign_scratch, update_with_fmadd_factor,
|
||||
};
|
||||
@@ -61,7 +61,7 @@ pub fn prepare_multi_bit_ggsw_mem_optimized<
|
||||
monomial_degree.wrapping_add(selection_bit.wrapping_mul(mask_element));
|
||||
}
|
||||
|
||||
let switched_degree = pbs_modulus_switch(
|
||||
let switched_degree = fast_pbs_modulus_switch(
|
||||
monomial_degree,
|
||||
polynomial_size,
|
||||
ModulusSwitchOffset(0),
|
||||
@@ -289,7 +289,7 @@ pub fn prepare_multi_bit_ggsw_mem_optimized<
|
||||
/// println!("Checking result...");
|
||||
/// assert_eq!(6, pbs_multiplication_result);
|
||||
/// println!(
|
||||
/// "Mulitplication via PBS result is correct! Expected 6, got {pbs_multiplication_result}"
|
||||
/// "Multiplication via PBS result is correct! Expected 6, got {pbs_multiplication_result}"
|
||||
/// );
|
||||
/// ```
|
||||
pub fn multi_bit_blind_rotate_assign<Scalar, InputCont, OutputCont, KeyCont>(
|
||||
@@ -307,7 +307,7 @@ pub fn multi_bit_blind_rotate_assign<Scalar, InputCont, OutputCont, KeyCont>(
|
||||
assert_eq!(
|
||||
input.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.input_lwe_dimension(),
|
||||
"Mimatched input LweDimension. LweCiphertext input LweDimension {:?}. \
|
||||
"Mismatched input LweDimension. LweCiphertext input LweDimension {:?}. \
|
||||
FourierLweMultiBitBootstrapKey input LweDimension {:?}.",
|
||||
input.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.input_lwe_dimension(),
|
||||
@@ -316,7 +316,7 @@ pub fn multi_bit_blind_rotate_assign<Scalar, InputCont, OutputCont, KeyCont>(
|
||||
assert_eq!(
|
||||
accumulator.glwe_size(),
|
||||
multi_bit_bsk.glwe_size(),
|
||||
"Mimatched GlweSize. Accumulator GlweSize {:?}. \
|
||||
"Mismatched GlweSize. Accumulator GlweSize {:?}. \
|
||||
FourierLweMultiBitBootstrapKey GlweSize {:?}.",
|
||||
accumulator.glwe_size(),
|
||||
multi_bit_bsk.glwe_size(),
|
||||
@@ -325,7 +325,7 @@ pub fn multi_bit_blind_rotate_assign<Scalar, InputCont, OutputCont, KeyCont>(
|
||||
assert_eq!(
|
||||
accumulator.polynomial_size(),
|
||||
multi_bit_bsk.polynomial_size(),
|
||||
"Mimatched PolynomialSize. Accumulator PolynomialSize {:?}. \
|
||||
"Mismatched PolynomialSize. Accumulator PolynomialSize {:?}. \
|
||||
FourierLweMultiBitBootstrapKey PolynomialSize {:?}.",
|
||||
accumulator.polynomial_size(),
|
||||
multi_bit_bsk.polynomial_size(),
|
||||
@@ -370,7 +370,7 @@ pub fn multi_bit_blind_rotate_assign<Scalar, InputCont, OutputCont, KeyCont>(
|
||||
let work_queue = Mutex::new(work_queue);
|
||||
|
||||
let lut_poly_size = accumulator.polynomial_size();
|
||||
let monomial_degree = pbs_modulus_switch(
|
||||
let monomial_degree = fast_pbs_modulus_switch(
|
||||
*lwe_body.data,
|
||||
lut_poly_size,
|
||||
ModulusSwitchOffset(0),
|
||||
@@ -558,7 +558,7 @@ pub fn multi_bit_deterministic_blind_rotate_assign<Scalar, InputCont, OutputCont
|
||||
assert_eq!(
|
||||
input.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.input_lwe_dimension(),
|
||||
"Mimatched input LweDimension. LweCiphertext input LweDimension {:?}. \
|
||||
"Mismatched input LweDimension. LweCiphertext input LweDimension {:?}. \
|
||||
FourierLweMultiBitBootstrapKey input LweDimension {:?}.",
|
||||
input.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.input_lwe_dimension(),
|
||||
@@ -567,7 +567,7 @@ pub fn multi_bit_deterministic_blind_rotate_assign<Scalar, InputCont, OutputCont
|
||||
assert_eq!(
|
||||
accumulator.glwe_size(),
|
||||
multi_bit_bsk.glwe_size(),
|
||||
"Mimatched GlweSize. Accumulator GlweSize {:?}. \
|
||||
"Mismatched GlweSize. Accumulator GlweSize {:?}. \
|
||||
FourierLweMultiBitBootstrapKey GlweSize {:?}.",
|
||||
accumulator.glwe_size(),
|
||||
multi_bit_bsk.glwe_size(),
|
||||
@@ -576,7 +576,7 @@ pub fn multi_bit_deterministic_blind_rotate_assign<Scalar, InputCont, OutputCont
|
||||
assert_eq!(
|
||||
accumulator.polynomial_size(),
|
||||
multi_bit_bsk.polynomial_size(),
|
||||
"Mimatched PolynomialSize. Accumulator PolynomialSize {:?}. \
|
||||
"Mismatched PolynomialSize. Accumulator PolynomialSize {:?}. \
|
||||
FourierLweMultiBitBootstrapKey PolynomialSize {:?}.",
|
||||
accumulator.polynomial_size(),
|
||||
multi_bit_bsk.polynomial_size(),
|
||||
@@ -617,7 +617,7 @@ pub fn multi_bit_deterministic_blind_rotate_assign<Scalar, InputCont, OutputCont
|
||||
let work_queue = &work_queue;
|
||||
|
||||
let lut_poly_size = accumulator.polynomial_size();
|
||||
let monomial_degree = pbs_modulus_switch(
|
||||
let monomial_degree = fast_pbs_modulus_switch(
|
||||
*lwe_body.data,
|
||||
lut_poly_size,
|
||||
ModulusSwitchOffset(0),
|
||||
@@ -700,7 +700,7 @@ pub fn multi_bit_deterministic_blind_rotate_assign<Scalar, InputCont, OutputCont
|
||||
monomial_degree.wrapping_add(selection_bit.wrapping_mul(mask_element));
|
||||
}
|
||||
|
||||
let switched_degree = pbs_modulus_switch(
|
||||
let switched_degree = fast_pbs_modulus_switch(
|
||||
monomial_degree,
|
||||
lut_poly_size,
|
||||
ModulusSwitchOffset(0),
|
||||
@@ -1025,7 +1025,7 @@ pub fn multi_bit_deterministic_blind_rotate_assign<Scalar, InputCont, OutputCont
|
||||
/// println!("Checking result...");
|
||||
/// assert_eq!(6, pbs_multiplication_result);
|
||||
/// println!(
|
||||
/// "Mulitplication via PBS result is correct! Expected 6, got {pbs_multiplication_result}"
|
||||
/// "Multiplication via PBS result is correct! Expected 6, got {pbs_multiplication_result}"
|
||||
/// );
|
||||
/// ```
|
||||
pub fn multi_bit_programmable_bootstrap_lwe_ciphertext<
|
||||
@@ -1051,7 +1051,7 @@ pub fn multi_bit_programmable_bootstrap_lwe_ciphertext<
|
||||
assert_eq!(
|
||||
input.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.input_lwe_dimension(),
|
||||
"Mimatched input LweDimension. LweCiphertext input LweDimension {:?}. \
|
||||
"Mismatched input LweDimension. LweCiphertext input LweDimension {:?}. \
|
||||
FourierLweMultiBitBootstrapKey input LweDimension {:?}.",
|
||||
input.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.input_lwe_dimension(),
|
||||
@@ -1060,7 +1060,7 @@ pub fn multi_bit_programmable_bootstrap_lwe_ciphertext<
|
||||
assert_eq!(
|
||||
output.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.output_lwe_dimension(),
|
||||
"Mimatched output LweDimension. LweCiphertext output LweDimension {:?}. \
|
||||
"Mismatched output LweDimension. LweCiphertext output LweDimension {:?}. \
|
||||
FourierLweMultiBitBootstrapKey output LweDimension {:?}.",
|
||||
output.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.output_lwe_dimension(),
|
||||
@@ -1069,7 +1069,7 @@ pub fn multi_bit_programmable_bootstrap_lwe_ciphertext<
|
||||
assert_eq!(
|
||||
accumulator.glwe_size(),
|
||||
multi_bit_bsk.glwe_size(),
|
||||
"Mimatched GlweSize. Accumulator GlweSize {:?}. \
|
||||
"Mismatched GlweSize. Accumulator GlweSize {:?}. \
|
||||
FourierLweMultiBitBootstrapKey GlweSize {:?}.",
|
||||
accumulator.glwe_size(),
|
||||
multi_bit_bsk.glwe_size(),
|
||||
@@ -1078,7 +1078,7 @@ pub fn multi_bit_programmable_bootstrap_lwe_ciphertext<
|
||||
assert_eq!(
|
||||
accumulator.polynomial_size(),
|
||||
multi_bit_bsk.polynomial_size(),
|
||||
"Mimatched PolynomialSize. Accumulator PolynomialSize {:?}. \
|
||||
"Mismatched PolynomialSize. Accumulator PolynomialSize {:?}. \
|
||||
FourierLweMultiBitBootstrapKey PolynomialSize {:?}.",
|
||||
accumulator.polynomial_size(),
|
||||
multi_bit_bsk.polynomial_size(),
|
||||
@@ -1145,7 +1145,7 @@ pub fn multi_bit_deterministic_programmable_bootstrap_lwe_ciphertext<
|
||||
assert_eq!(
|
||||
input.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.input_lwe_dimension(),
|
||||
"Mimatched input LweDimension. LweCiphertext input LweDimension {:?}. \
|
||||
"Mismatched input LweDimension. LweCiphertext input LweDimension {:?}. \
|
||||
FourierLweMultiBitBootstrapKey input LweDimension {:?}.",
|
||||
input.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.input_lwe_dimension(),
|
||||
@@ -1154,7 +1154,7 @@ pub fn multi_bit_deterministic_programmable_bootstrap_lwe_ciphertext<
|
||||
assert_eq!(
|
||||
output.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.output_lwe_dimension(),
|
||||
"Mimatched output LweDimension. LweCiphertext output LweDimension {:?}. \
|
||||
"Mismatched output LweDimension. LweCiphertext output LweDimension {:?}. \
|
||||
FourierLweMultiBitBootstrapKey output LweDimension {:?}.",
|
||||
output.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.output_lwe_dimension(),
|
||||
@@ -1163,7 +1163,7 @@ pub fn multi_bit_deterministic_programmable_bootstrap_lwe_ciphertext<
|
||||
assert_eq!(
|
||||
accumulator.glwe_size(),
|
||||
multi_bit_bsk.glwe_size(),
|
||||
"Mimatched GlweSize. Accumulator GlweSize {:?}. \
|
||||
"Mismatched GlweSize. Accumulator GlweSize {:?}. \
|
||||
FourierLweMultiBitBootstrapKey GlweSize {:?}.",
|
||||
accumulator.glwe_size(),
|
||||
multi_bit_bsk.glwe_size(),
|
||||
@@ -1172,7 +1172,7 @@ pub fn multi_bit_deterministic_programmable_bootstrap_lwe_ciphertext<
|
||||
assert_eq!(
|
||||
accumulator.polynomial_size(),
|
||||
multi_bit_bsk.polynomial_size(),
|
||||
"Mimatched PolynomialSize. Accumulator PolynomialSize {:?}. \
|
||||
"Mismatched PolynomialSize. Accumulator PolynomialSize {:?}. \
|
||||
FourierLweMultiBitBootstrapKey PolynomialSize {:?}.",
|
||||
accumulator.polynomial_size(),
|
||||
multi_bit_bsk.polynomial_size(),
|
||||
@@ -1257,7 +1257,7 @@ pub fn std_prepare_multi_bit_ggsw<Scalar, GgswBufferCont, TmpGgswBufferCont, Ggs
|
||||
monomial_degree.wrapping_add(selection_bit.wrapping_mul(mask_element));
|
||||
}
|
||||
|
||||
let switched_degree = pbs_modulus_switch(
|
||||
let switched_degree = fast_pbs_modulus_switch(
|
||||
monomial_degree,
|
||||
polynomial_size,
|
||||
ModulusSwitchOffset(0),
|
||||
@@ -1295,7 +1295,7 @@ pub fn std_multi_bit_blind_rotate_assign<Scalar, InputCont, OutputCont, KeyCont>
|
||||
assert_eq!(
|
||||
input.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.input_lwe_dimension(),
|
||||
"Mimatched input LweDimension. LweCiphertext input LweDimension {:?}. \
|
||||
"Mismatched input LweDimension. LweCiphertext input LweDimension {:?}. \
|
||||
FourierLweMultiBitBootstrapKey input LweDimension {:?}.",
|
||||
input.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.input_lwe_dimension(),
|
||||
@@ -1304,7 +1304,7 @@ pub fn std_multi_bit_blind_rotate_assign<Scalar, InputCont, OutputCont, KeyCont>
|
||||
assert_eq!(
|
||||
accumulator.glwe_size(),
|
||||
multi_bit_bsk.glwe_size(),
|
||||
"Mimatched GlweSize. Accumulator GlweSize {:?}. \
|
||||
"Mismatched GlweSize. Accumulator GlweSize {:?}. \
|
||||
FourierLweMultiBitBootstrapKey GlweSize {:?}.",
|
||||
accumulator.glwe_size(),
|
||||
multi_bit_bsk.glwe_size(),
|
||||
@@ -1313,7 +1313,7 @@ pub fn std_multi_bit_blind_rotate_assign<Scalar, InputCont, OutputCont, KeyCont>
|
||||
assert_eq!(
|
||||
accumulator.polynomial_size(),
|
||||
multi_bit_bsk.polynomial_size(),
|
||||
"Mimatched PolynomialSize. Accumulator PolynomialSize {:?}. \
|
||||
"Mismatched PolynomialSize. Accumulator PolynomialSize {:?}. \
|
||||
FourierLweMultiBitBootstrapKey PolynomialSize {:?}.",
|
||||
accumulator.polynomial_size(),
|
||||
multi_bit_bsk.polynomial_size(),
|
||||
@@ -1330,7 +1330,7 @@ pub fn std_multi_bit_blind_rotate_assign<Scalar, InputCont, OutputCont, KeyCont>
|
||||
assert_eq!(
|
||||
input.ciphertext_modulus(),
|
||||
multi_bit_bsk.ciphertext_modulus(),
|
||||
"Mimatched CiphertextModulus. LweCiphertext CiphertextModulus {:?}. \
|
||||
"Mismatched CiphertextModulus. LweCiphertext CiphertextModulus {:?}. \
|
||||
LweMultiBitBootstrapKey CiphertextModulus {:?}.",
|
||||
input.ciphertext_modulus(),
|
||||
multi_bit_bsk.ciphertext_modulus(),
|
||||
@@ -1363,7 +1363,7 @@ pub fn std_multi_bit_blind_rotate_assign<Scalar, InputCont, OutputCont, KeyCont>
|
||||
let work_queue = Mutex::new(work_queue);
|
||||
|
||||
let lut_poly_size = accumulator.polynomial_size();
|
||||
let monomial_degree = pbs_modulus_switch(
|
||||
let monomial_degree = fast_pbs_modulus_switch(
|
||||
*lwe_body.data,
|
||||
lut_poly_size,
|
||||
ModulusSwitchOffset(0),
|
||||
@@ -1577,7 +1577,7 @@ pub fn std_multi_bit_deterministic_blind_rotate_assign<Scalar, InputCont, Output
|
||||
assert_eq!(
|
||||
input.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.input_lwe_dimension(),
|
||||
"Mimatched input LweDimension. LweCiphertext input LweDimension {:?}. \
|
||||
"Mismatched input LweDimension. LweCiphertext input LweDimension {:?}. \
|
||||
FourierLweMultiBitBootstrapKey input LweDimension {:?}.",
|
||||
input.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.input_lwe_dimension(),
|
||||
@@ -1586,7 +1586,7 @@ pub fn std_multi_bit_deterministic_blind_rotate_assign<Scalar, InputCont, Output
|
||||
assert_eq!(
|
||||
accumulator.glwe_size(),
|
||||
multi_bit_bsk.glwe_size(),
|
||||
"Mimatched GlweSize. Accumulator GlweSize {:?}. \
|
||||
"Mismatched GlweSize. Accumulator GlweSize {:?}. \
|
||||
FourierLweMultiBitBootstrapKey GlweSize {:?}.",
|
||||
accumulator.glwe_size(),
|
||||
multi_bit_bsk.glwe_size(),
|
||||
@@ -1595,7 +1595,7 @@ pub fn std_multi_bit_deterministic_blind_rotate_assign<Scalar, InputCont, Output
|
||||
assert_eq!(
|
||||
accumulator.polynomial_size(),
|
||||
multi_bit_bsk.polynomial_size(),
|
||||
"Mimatched PolynomialSize. Accumulator PolynomialSize {:?}. \
|
||||
"Mismatched PolynomialSize. Accumulator PolynomialSize {:?}. \
|
||||
FourierLweMultiBitBootstrapKey PolynomialSize {:?}.",
|
||||
accumulator.polynomial_size(),
|
||||
multi_bit_bsk.polynomial_size(),
|
||||
@@ -1612,7 +1612,7 @@ pub fn std_multi_bit_deterministic_blind_rotate_assign<Scalar, InputCont, Output
|
||||
assert_eq!(
|
||||
accumulator.ciphertext_modulus(),
|
||||
multi_bit_bsk.ciphertext_modulus(),
|
||||
"Mimatched CiphertextModulus. Accumulator CiphertextModulus {:?}. \
|
||||
"Mismatched CiphertextModulus. Accumulator CiphertextModulus {:?}. \
|
||||
LweMultiBitBootstrapKey CiphertextModulus {:?}.",
|
||||
accumulator.ciphertext_modulus(),
|
||||
multi_bit_bsk.ciphertext_modulus(),
|
||||
@@ -1645,7 +1645,7 @@ pub fn std_multi_bit_deterministic_blind_rotate_assign<Scalar, InputCont, Output
|
||||
let work_queue = &work_queue;
|
||||
|
||||
let lut_poly_size = accumulator.polynomial_size();
|
||||
let monomial_degree = pbs_modulus_switch(
|
||||
let monomial_degree = fast_pbs_modulus_switch(
|
||||
*lwe_body.data,
|
||||
lut_poly_size,
|
||||
ModulusSwitchOffset(0),
|
||||
@@ -1856,7 +1856,7 @@ pub fn std_multi_bit_programmable_bootstrap_lwe_ciphertext<
|
||||
assert_eq!(
|
||||
input.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.input_lwe_dimension(),
|
||||
"Mimatched input LweDimension. LweCiphertext input LweDimension {:?}. \
|
||||
"Mismatched input LweDimension. LweCiphertext input LweDimension {:?}. \
|
||||
FourierLweMultiBitBootstrapKey input LweDimension {:?}.",
|
||||
input.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.input_lwe_dimension(),
|
||||
@@ -1865,7 +1865,7 @@ pub fn std_multi_bit_programmable_bootstrap_lwe_ciphertext<
|
||||
assert_eq!(
|
||||
output.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.output_lwe_dimension(),
|
||||
"Mimatched output LweDimension. LweCiphertext output LweDimension {:?}. \
|
||||
"Mismatched output LweDimension. LweCiphertext output LweDimension {:?}. \
|
||||
FourierLweMultiBitBootstrapKey output LweDimension {:?}.",
|
||||
output.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.output_lwe_dimension(),
|
||||
@@ -1874,7 +1874,7 @@ pub fn std_multi_bit_programmable_bootstrap_lwe_ciphertext<
|
||||
assert_eq!(
|
||||
accumulator.glwe_size(),
|
||||
multi_bit_bsk.glwe_size(),
|
||||
"Mimatched GlweSize. Accumulator GlweSize {:?}. \
|
||||
"Mismatched GlweSize. Accumulator GlweSize {:?}. \
|
||||
FourierLweMultiBitBootstrapKey GlweSize {:?}.",
|
||||
accumulator.glwe_size(),
|
||||
multi_bit_bsk.glwe_size(),
|
||||
@@ -1883,7 +1883,7 @@ pub fn std_multi_bit_programmable_bootstrap_lwe_ciphertext<
|
||||
assert_eq!(
|
||||
accumulator.polynomial_size(),
|
||||
multi_bit_bsk.polynomial_size(),
|
||||
"Mimatched PolynomialSize. Accumulator PolynomialSize {:?}. \
|
||||
"Mismatched PolynomialSize. Accumulator PolynomialSize {:?}. \
|
||||
FourierLweMultiBitBootstrapKey PolynomialSize {:?}.",
|
||||
accumulator.polynomial_size(),
|
||||
multi_bit_bsk.polynomial_size(),
|
||||
@@ -1950,7 +1950,7 @@ pub fn std_multi_bit_deterministic_programmable_bootstrap_lwe_ciphertext<
|
||||
assert_eq!(
|
||||
input.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.input_lwe_dimension(),
|
||||
"Mimatched input LweDimension. LweCiphertext input LweDimension {:?}. \
|
||||
"Mismatched input LweDimension. LweCiphertext input LweDimension {:?}. \
|
||||
FourierLweMultiBitBootstrapKey input LweDimension {:?}.",
|
||||
input.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.input_lwe_dimension(),
|
||||
@@ -1959,7 +1959,7 @@ pub fn std_multi_bit_deterministic_programmable_bootstrap_lwe_ciphertext<
|
||||
assert_eq!(
|
||||
output.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.output_lwe_dimension(),
|
||||
"Mimatched output LweDimension. LweCiphertext output LweDimension {:?}. \
|
||||
"Mismatched output LweDimension. LweCiphertext output LweDimension {:?}. \
|
||||
FourierLweMultiBitBootstrapKey output LweDimension {:?}.",
|
||||
output.lwe_size().to_lwe_dimension(),
|
||||
multi_bit_bsk.output_lwe_dimension(),
|
||||
@@ -1968,7 +1968,7 @@ pub fn std_multi_bit_deterministic_programmable_bootstrap_lwe_ciphertext<
|
||||
assert_eq!(
|
||||
accumulator.glwe_size(),
|
||||
multi_bit_bsk.glwe_size(),
|
||||
"Mimatched GlweSize. Accumulator GlweSize {:?}. \
|
||||
"Mismatched GlweSize. Accumulator GlweSize {:?}. \
|
||||
FourierLweMultiBitBootstrapKey GlweSize {:?}.",
|
||||
accumulator.glwe_size(),
|
||||
multi_bit_bsk.glwe_size(),
|
||||
@@ -1977,7 +1977,7 @@ pub fn std_multi_bit_deterministic_programmable_bootstrap_lwe_ciphertext<
|
||||
assert_eq!(
|
||||
accumulator.polynomial_size(),
|
||||
multi_bit_bsk.polynomial_size(),
|
||||
"Mimatched PolynomialSize. Accumulator PolynomialSize {:?}. \
|
||||
"Mismatched PolynomialSize. Accumulator PolynomialSize {:?}. \
|
||||
FourierLweMultiBitBootstrapKey PolynomialSize {:?}.",
|
||||
accumulator.polynomial_size(),
|
||||
multi_bit_bsk.polynomial_size(),
|
||||
|
||||
@@ -36,7 +36,7 @@ use dyn_stack::{PodStack, SizeOverflow, StackReq};
|
||||
/// ```
|
||||
/// use tfhe::core_crypto::prelude::*;
|
||||
///
|
||||
/// // This example recreates a PBS by combining a blind rotate and a smaple extract.
|
||||
/// // This example recreates a PBS by combining a blind rotate and a sample extract.
|
||||
///
|
||||
/// // DISCLAIMER: these toy example parameters are not guaranteed to be secure or yield correct
|
||||
/// // computations
|
||||
@@ -226,7 +226,7 @@ use dyn_stack::{PodStack, SizeOverflow, StackReq};
|
||||
/// println!("Checking result...");
|
||||
/// assert_eq!(6, pbs_multiplication_result);
|
||||
/// println!(
|
||||
/// "Mulitplication via PBS result is correct! Expected 6, got {pbs_multiplication_result}"
|
||||
/// "Multiplication via PBS result is correct! Expected 6, got {pbs_multiplication_result}"
|
||||
/// );
|
||||
/// ```
|
||||
pub fn blind_rotate_assign<Scalar, InputCont, OutputCont, KeyCont>(
|
||||
@@ -1011,7 +1011,7 @@ pub fn cmux_assign_mem_optimized_requirement<Scalar>(
|
||||
/// println!("Checking result...");
|
||||
/// assert_eq!(6, pbs_multiplication_result);
|
||||
/// println!(
|
||||
/// "Mulitplication via PBS result is correct! Expected 6, got {pbs_multiplication_result}"
|
||||
/// "Multiplication via PBS result is correct! Expected 6, got {pbs_multiplication_result}"
|
||||
/// );
|
||||
/// ```
|
||||
pub fn programmable_bootstrap_lwe_ciphertext<Scalar, InputCont, OutputCont, AccCont, KeyCont>(
|
||||
@@ -1321,7 +1321,7 @@ pub fn programmable_bootstrap_lwe_ciphertext_mem_optimized_requirement<Scalar>(
|
||||
/// println!("Checking result...");
|
||||
/// assert_eq!(6, pbs_multiplication_result);
|
||||
/// println!(
|
||||
/// "Mulitplication via PBS result is correct! Expected 6, got {pbs_multiplication_result}"
|
||||
/// "Multiplication via PBS result is correct! Expected 6, got {pbs_multiplication_result}"
|
||||
/// );
|
||||
/// ```
|
||||
pub fn programmable_bootstrap_f128_lwe_ciphertext<Scalar, InputCont, OutputCont, AccCont, KeyCont>(
|
||||
|
||||
@@ -132,7 +132,7 @@ pub fn generate_circuit_bootstrap_lwe_pfpksk_list<
|
||||
PolynomialCount(1),
|
||||
);
|
||||
// We apply the x -> -x function so instead of putting one in the first coeff of the
|
||||
// polynomial, we put Scalar::MAX == - Sclar::One so that we can use a single function in
|
||||
// polynomial, we put Scalar::MAX == - Scalar::One so that we can use a single function in
|
||||
// the loop avoiding branching
|
||||
last_polynomial_as_list.get_mut(0)[0] = Scalar::MAX;
|
||||
|
||||
@@ -269,7 +269,7 @@ pub fn par_generate_circuit_bootstrap_lwe_pfpksk_list<
|
||||
PolynomialCount(1),
|
||||
);
|
||||
// We apply the x -> -x function so instead of putting one in the first coeff of the
|
||||
// polynomial, we put Scalar::MAX == - Sclar::One so that we can use a single function in
|
||||
// polynomial, we put Scalar::MAX == - Scalar::One so that we can use a single function in
|
||||
// the loop avoiding branching
|
||||
last_polynomial_as_list.get_mut(0)[0] = Scalar::MAX;
|
||||
|
||||
|
||||
@@ -64,6 +64,74 @@ where
|
||||
y.wrapping_rem(Scalar::ONE.shl(log2_modulo))
|
||||
}
|
||||
|
||||
/// Compute the smallest signed difference between two torus elements
|
||||
pub fn torus_modular_diff<T: UnsignedInteger>(
|
||||
first: T,
|
||||
other: T,
|
||||
modulus: CiphertextModulus<T>,
|
||||
) -> f64 {
|
||||
if modulus.is_native_modulus() {
|
||||
let bits = T::BITS as i32;
|
||||
// Using the [0; 1[ torus to reason
|
||||
// Example with first = 0.1 and other = 0.9
|
||||
// d0 = first - other = -0.8 = 0.2 mod 1
|
||||
// d1 = other - first = 0.8
|
||||
// d0 < d1 return 0.2
|
||||
// if other and first are inverted we get
|
||||
// d0 = 0.8
|
||||
// d1 = 0.2
|
||||
// d1 <= d0 return -0.2, the minus here can be seen as taking first as a reference
|
||||
// In the first example adding 0.2 to other (0.9 + 0.2 mod 1 = 0.1) gets us to first
|
||||
// In the second example adding -0.2 to other (0.1 - 0.2 mod 1 = 0.9) gets us to first
|
||||
let d0 = first.wrapping_sub(other);
|
||||
let d1 = other.wrapping_sub(first);
|
||||
if d0 < d1 {
|
||||
let d: f64 = d0.cast_into();
|
||||
d / 2_f64.powi(bits)
|
||||
} else {
|
||||
let d: f64 = d1.cast_into();
|
||||
-d / 2_f64.powi(bits)
|
||||
}
|
||||
} else {
|
||||
let custom_modulus = T::cast_from(modulus.get_custom_modulus());
|
||||
let d0 = first.wrapping_sub_custom_mod(other, custom_modulus);
|
||||
let d1 = other.wrapping_sub_custom_mod(first, custom_modulus);
|
||||
if d0 < d1 {
|
||||
let d: f64 = d0.cast_into();
|
||||
let cm_f: f64 = custom_modulus.cast_into();
|
||||
d / cm_f
|
||||
} else {
|
||||
let d: f64 = d1.cast_into();
|
||||
let cm_f: f64 = custom_modulus.cast_into();
|
||||
-d / cm_f
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Our representation of non native power of 2 moduli puts the information in the MSBs and leaves
|
||||
// the LSBs empty, this is what this function is checking
|
||||
pub fn check_content_respects_mod<Scalar: UnsignedInteger, Input: AsRef<[Scalar]>>(
|
||||
input: &Input,
|
||||
modulus: CiphertextModulus<Scalar>,
|
||||
) -> bool {
|
||||
if modulus.is_native_modulus() {
|
||||
true
|
||||
} else if modulus.is_power_of_two() {
|
||||
// If our modulus is 2^60, the scaling is 2^4 = 00...00010000, minus 1 = 00...00001111
|
||||
// we want the bits under the mask to be 0
|
||||
let power_2_diff_mask = modulus.get_power_of_two_scaling_to_native_torus() - Scalar::ONE;
|
||||
input
|
||||
.as_ref()
|
||||
.iter()
|
||||
.all(|&x| (x & power_2_diff_mask) == Scalar::ZERO)
|
||||
} else {
|
||||
// non native, not power of two
|
||||
let scalar_modulus: Scalar = modulus.get_custom_modulus().cast_into();
|
||||
|
||||
input.as_ref().iter().all(|&x| x < scalar_modulus)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
@@ -105,6 +105,45 @@ pub fn polynomial_wrapping_add_multisum_assign<Scalar, OutputCont, InputCont1, I
|
||||
}
|
||||
}
|
||||
|
||||
fn polynomial_wrapping_add_mul_assign_schoolbook<Scalar, OutputCont, InputCont1, InputCont2>(
|
||||
output: &mut Polynomial<OutputCont>,
|
||||
lhs: &Polynomial<InputCont1>,
|
||||
rhs: &Polynomial<InputCont2>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
InputCont1: Container<Element = Scalar>,
|
||||
InputCont2: Container<Element = Scalar>,
|
||||
{
|
||||
fn implementation<Scalar: UnsignedInteger>(
|
||||
mut output: Polynomial<&mut [Scalar]>,
|
||||
lhs: Polynomial<&[Scalar]>,
|
||||
rhs: Polynomial<&[Scalar]>,
|
||||
) {
|
||||
let polynomial_size = output.polynomial_size();
|
||||
let degree = output.degree();
|
||||
|
||||
for (lhs_degree, &lhs_coeff) in lhs.iter().enumerate() {
|
||||
for (rhs_degree, &rhs_coeff) in rhs.iter().enumerate() {
|
||||
let target_degree = lhs_degree + rhs_degree;
|
||||
if target_degree <= degree {
|
||||
let output_coefficient = &mut output.as_mut()[target_degree];
|
||||
|
||||
*output_coefficient =
|
||||
(*output_coefficient).wrapping_add(lhs_coeff.wrapping_mul(rhs_coeff));
|
||||
} else {
|
||||
let target_degree = target_degree % polynomial_size.0;
|
||||
let output_coefficient = &mut output.as_mut()[target_degree];
|
||||
|
||||
*output_coefficient =
|
||||
(*output_coefficient).wrapping_sub(lhs_coeff.wrapping_mul(rhs_coeff));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
implementation(output.as_mut_view(), lhs.as_view(), rhs.as_view());
|
||||
}
|
||||
|
||||
/// Add the result of the product between two polynomials, reduced modulo $(X^{N}+1)$, to the
|
||||
/// output polynomial.
|
||||
///
|
||||
@@ -155,24 +194,7 @@ pub fn polynomial_wrapping_add_mul_assign<Scalar, OutputCont, InputCont1, InputC
|
||||
polynomial_karatsuba_wrapping_mul(&mut tmp, lhs, rhs);
|
||||
polynomial_wrapping_add_assign(output, &tmp);
|
||||
} else {
|
||||
let degree = output.degree();
|
||||
for (lhs_degree, &lhs_coeff) in lhs.iter().enumerate() {
|
||||
for (rhs_degree, &rhs_coeff) in rhs.iter().enumerate() {
|
||||
let target_degree = lhs_degree + rhs_degree;
|
||||
if target_degree <= degree {
|
||||
let output_coefficient = &mut output.as_mut()[target_degree];
|
||||
|
||||
*output_coefficient =
|
||||
(*output_coefficient).wrapping_add(lhs_coeff.wrapping_mul(rhs_coeff));
|
||||
} else {
|
||||
let target_degree = target_degree % polynomial_size.0;
|
||||
let output_coefficient = &mut output.as_mut()[target_degree];
|
||||
|
||||
*output_coefficient =
|
||||
(*output_coefficient).wrapping_sub(lhs_coeff.wrapping_mul(rhs_coeff));
|
||||
}
|
||||
}
|
||||
}
|
||||
polynomial_wrapping_add_mul_assign_schoolbook(output, lhs, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,6 +281,18 @@ pub fn polynomial_wrapping_monic_monomial_mul_assign<Scalar, OutputCont>(
|
||||
.for_each(|a| *a = a.wrapping_neg());
|
||||
}
|
||||
|
||||
/// performs the operation: dst = -src, with wrapping arithmetic
|
||||
fn copy_with_neg<Scalar: UnsignedInteger>(dst: &mut [Scalar], src: &[Scalar]) {
|
||||
for (dst, src) in dst.iter_mut().zip(src) {
|
||||
*dst = src.wrapping_neg();
|
||||
}
|
||||
}
|
||||
|
||||
/// performs the operation: dst = src
|
||||
fn copy_without_neg<Scalar: UnsignedInteger>(dst: &mut [Scalar], src: &[Scalar]) {
|
||||
dst.copy_from_slice(src);
|
||||
}
|
||||
|
||||
/// Divides (mod $(X^{N}+1)$), the input polynomial with a monic monomial of a given degree i.e.
|
||||
/// $X^{degree}$.
|
||||
///
|
||||
@@ -297,24 +331,25 @@ pub fn polynomial_wrapping_monic_monomial_div<Scalar, OutputCont, InputCont>(
|
||||
let polynomial_size = output.polynomial_size().0;
|
||||
let remaining_degree = monomial_degree.0 % polynomial_size;
|
||||
|
||||
let src_slice = &input[remaining_degree..];
|
||||
let src_slice_len = src_slice.len();
|
||||
let dst_slice = &mut output[..src_slice_len];
|
||||
dst_slice.copy_from_slice(src_slice);
|
||||
|
||||
for (dst, &src) in output[polynomial_size - remaining_degree..]
|
||||
.iter_mut()
|
||||
.zip(input[..remaining_degree].iter())
|
||||
{
|
||||
*dst = src.wrapping_neg();
|
||||
}
|
||||
|
||||
let full_cycles_count = monomial_degree.0 / polynomial_size;
|
||||
if full_cycles_count % 2 != 0 {
|
||||
output
|
||||
.as_mut()
|
||||
.iter_mut()
|
||||
.for_each(|a| *a = a.wrapping_neg());
|
||||
if full_cycles_count % 2 == 0 {
|
||||
copy_without_neg(
|
||||
&mut output[..polynomial_size - remaining_degree],
|
||||
&input[remaining_degree..],
|
||||
);
|
||||
copy_with_neg(
|
||||
&mut output[polynomial_size - remaining_degree..],
|
||||
&input[..remaining_degree],
|
||||
);
|
||||
} else {
|
||||
copy_with_neg(
|
||||
&mut output[..polynomial_size - remaining_degree],
|
||||
&input[remaining_degree..],
|
||||
);
|
||||
copy_without_neg(
|
||||
&mut output[polynomial_size - remaining_degree..],
|
||||
&input[..remaining_degree],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,24 +391,101 @@ pub fn polynomial_wrapping_monic_monomial_mul<Scalar, OutputCont, InputCont>(
|
||||
let polynomial_size = output.polynomial_size().0;
|
||||
let remaining_degree = monomial_degree.0 % polynomial_size;
|
||||
|
||||
for (dst, &src) in output[..remaining_degree]
|
||||
.iter_mut()
|
||||
.zip(input[polynomial_size - remaining_degree..].iter())
|
||||
{
|
||||
*dst = src.wrapping_neg();
|
||||
let full_cycles_count = monomial_degree.0 / polynomial_size;
|
||||
if full_cycles_count % 2 == 0 {
|
||||
copy_with_neg(
|
||||
&mut output[..remaining_degree],
|
||||
&input[polynomial_size - remaining_degree..],
|
||||
);
|
||||
copy_without_neg(
|
||||
&mut output[remaining_degree..],
|
||||
&input[..polynomial_size - remaining_degree],
|
||||
);
|
||||
} else {
|
||||
copy_without_neg(
|
||||
&mut output[..remaining_degree],
|
||||
&input[polynomial_size - remaining_degree..],
|
||||
);
|
||||
copy_with_neg(
|
||||
&mut output[remaining_degree..],
|
||||
&input[..polynomial_size - remaining_degree],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiply (mod $(X^{N}+1)$), the input polynomial with a monic monomial of a given degree i.e.
|
||||
/// $X^{degree}$, then subtract the input from the result and assign to the output.
|
||||
///
|
||||
/// output = input * X^degree - input
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Computations wrap around (similar to computing modulo $2^{n\_{bits}}$) when exceeding the
|
||||
/// unsigned integer capacity.
|
||||
pub(crate) fn polynomial_wrapping_monic_monomial_mul_and_subtract<Scalar, OutputCont, InputCont>(
|
||||
output: &mut Polynomial<OutputCont>,
|
||||
input: &Polynomial<InputCont>,
|
||||
monomial_degree: MonomialDegree,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
InputCont: Container<Element = Scalar>,
|
||||
{
|
||||
assert!(
|
||||
output.polynomial_size() == input.polynomial_size(),
|
||||
"Output polynomial size {:?} is not the same as input polynomial size {:?}.",
|
||||
output.polynomial_size(),
|
||||
input.polynomial_size(),
|
||||
);
|
||||
|
||||
/// performs the operation: dst = -src - src_orig, with wrapping arithmetic
|
||||
fn copy_with_neg_and_subtract<Scalar: UnsignedInteger>(
|
||||
dst: &mut [Scalar],
|
||||
src: &[Scalar],
|
||||
src_orig: &[Scalar],
|
||||
) {
|
||||
for ((dst, src), src_orig) in dst.iter_mut().zip(src).zip(src_orig) {
|
||||
*dst = src.wrapping_neg().wrapping_sub(*src_orig);
|
||||
}
|
||||
}
|
||||
|
||||
let dst_slice = &mut output[remaining_degree..];
|
||||
let dst_slice_len = dst_slice.len();
|
||||
let src_slice = &input[..dst_slice_len];
|
||||
dst_slice.copy_from_slice(src_slice);
|
||||
/// performs the operation: dst = src - src_orig, with wrapping arithmetic
|
||||
fn copy_without_neg_and_subtract<Scalar: UnsignedInteger>(
|
||||
dst: &mut [Scalar],
|
||||
src: &[Scalar],
|
||||
src_orig: &[Scalar],
|
||||
) {
|
||||
for ((dst, src), src_orig) in dst.iter_mut().zip(src).zip(src_orig) {
|
||||
*dst = src.wrapping_sub(*src_orig);
|
||||
}
|
||||
}
|
||||
|
||||
let polynomial_size = output.polynomial_size().0;
|
||||
let remaining_degree = monomial_degree.0 % polynomial_size;
|
||||
|
||||
let full_cycles_count = monomial_degree.0 / polynomial_size;
|
||||
if full_cycles_count % 2 != 0 {
|
||||
output
|
||||
.as_mut()
|
||||
.iter_mut()
|
||||
.for_each(|a| *a = a.wrapping_neg());
|
||||
if full_cycles_count % 2 == 0 {
|
||||
copy_with_neg_and_subtract(
|
||||
&mut output[..remaining_degree],
|
||||
&input[polynomial_size - remaining_degree..],
|
||||
&input[..remaining_degree],
|
||||
);
|
||||
copy_without_neg_and_subtract(
|
||||
&mut output[remaining_degree..],
|
||||
&input[..polynomial_size - remaining_degree],
|
||||
&input[remaining_degree..],
|
||||
);
|
||||
} else {
|
||||
copy_without_neg_and_subtract(
|
||||
&mut output[..remaining_degree],
|
||||
&input[polynomial_size - remaining_degree..],
|
||||
&input[..remaining_degree],
|
||||
);
|
||||
copy_with_neg_and_subtract(
|
||||
&mut output[remaining_degree..],
|
||||
&input[..polynomial_size - remaining_degree],
|
||||
&input[remaining_degree..],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -419,6 +531,45 @@ pub fn polynomial_wrapping_sub_multisum_assign<Scalar, OutputCont, InputCont1, I
|
||||
}
|
||||
}
|
||||
|
||||
fn polynomial_wrapping_sub_mul_assign_schoolbook<Scalar, OutputCont, InputCont1, InputCont2>(
|
||||
output: &mut Polynomial<OutputCont>,
|
||||
lhs: &Polynomial<InputCont1>,
|
||||
rhs: &Polynomial<InputCont2>,
|
||||
) where
|
||||
Scalar: UnsignedInteger,
|
||||
OutputCont: ContainerMut<Element = Scalar>,
|
||||
InputCont1: Container<Element = Scalar>,
|
||||
InputCont2: Container<Element = Scalar>,
|
||||
{
|
||||
fn implementation<Scalar: UnsignedInteger>(
|
||||
mut output: Polynomial<&mut [Scalar]>,
|
||||
lhs: Polynomial<&[Scalar]>,
|
||||
rhs: Polynomial<&[Scalar]>,
|
||||
) {
|
||||
let polynomial_size = output.polynomial_size();
|
||||
let degree = output.degree();
|
||||
|
||||
for (lhs_degree, &lhs_coeff) in lhs.iter().enumerate() {
|
||||
for (rhs_degree, &rhs_coeff) in rhs.iter().enumerate() {
|
||||
let target_degree = lhs_degree + rhs_degree;
|
||||
if target_degree <= degree {
|
||||
let output_coefficient = &mut output.as_mut()[target_degree];
|
||||
|
||||
*output_coefficient =
|
||||
(*output_coefficient).wrapping_sub(lhs_coeff.wrapping_mul(rhs_coeff));
|
||||
} else {
|
||||
let target_degree = target_degree % polynomial_size.0;
|
||||
let output_coefficient = &mut output.as_mut()[target_degree];
|
||||
|
||||
*output_coefficient =
|
||||
(*output_coefficient).wrapping_add(lhs_coeff.wrapping_mul(rhs_coeff));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
implementation(output.as_mut_view(), lhs.as_view(), rhs.as_view());
|
||||
}
|
||||
|
||||
/// Subtract the result of the product between two polynomials, reduced modulo $(X^{N}+1)$, to the
|
||||
/// output polynomial.
|
||||
///
|
||||
@@ -469,28 +620,11 @@ pub fn polynomial_wrapping_sub_mul_assign<Scalar, OutputCont, InputCont1, InputC
|
||||
polynomial_karatsuba_wrapping_mul(&mut tmp, lhs, rhs);
|
||||
polynomial_wrapping_sub_assign(output, &tmp);
|
||||
} else {
|
||||
let degree = output.degree();
|
||||
for (lhs_degree, &lhs_coeff) in lhs.iter().enumerate() {
|
||||
for (rhs_degree, &rhs_coeff) in rhs.iter().enumerate() {
|
||||
let target_degree = lhs_degree + rhs_degree;
|
||||
if target_degree <= degree {
|
||||
let output_coefficient = &mut output.as_mut()[target_degree];
|
||||
|
||||
*output_coefficient =
|
||||
(*output_coefficient).wrapping_sub(lhs_coeff.wrapping_mul(rhs_coeff));
|
||||
} else {
|
||||
let target_degree = target_degree % polynomial_size.0;
|
||||
let output_coefficient = &mut output.as_mut()[target_degree];
|
||||
|
||||
*output_coefficient =
|
||||
(*output_coefficient).wrapping_add(lhs_coeff.wrapping_mul(rhs_coeff));
|
||||
}
|
||||
}
|
||||
}
|
||||
polynomial_wrapping_sub_mul_assign_schoolbook(output, lhs, rhs);
|
||||
}
|
||||
}
|
||||
|
||||
/// Fill the ouptut polynomial, with the result of the product of two polynomials, reduced modulo
|
||||
/// Fill the output polynomial, with the result of the product of two polynomials, reduced modulo
|
||||
/// $(X^{N} + 1)$ with the schoolbook algorithm Complexity: $O(N^{2})$
|
||||
///
|
||||
/// # Note
|
||||
@@ -707,7 +841,7 @@ mod test {
|
||||
/// for random polynomial multiplication
|
||||
fn test_multiply_karatsuba<T: UnsignedTorus>() {
|
||||
// 50 times the test
|
||||
for _i in 0..50 {
|
||||
for _ in 0..50 {
|
||||
// random source
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
@@ -730,7 +864,7 @@ mod test {
|
||||
let mut ka_mul = Polynomial::new(T::ZERO, polynomial_size);
|
||||
|
||||
// compute the schoolbook
|
||||
polynomial_wrapping_mul(&mut sb_mul, &poly_1, &poly_2);
|
||||
polynomial_wrapping_add_mul_assign_schoolbook(&mut sb_mul, &poly_1, &poly_2);
|
||||
|
||||
// compute the karatsuba
|
||||
polynomial_karatsuba_wrapping_mul(&mut ka_mul, &poly_1, &poly_2);
|
||||
@@ -740,6 +874,72 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
/// test if we have the same result when using schoolbook or ntt/karatsuba
|
||||
/// for random polynomial multiplication
|
||||
fn test_add_mul<T: UnsignedTorus>() {
|
||||
for polynomial_log in 4..=12 {
|
||||
for _ in 0..10 {
|
||||
let polynomial_size = PolynomialSize(1 << polynomial_log);
|
||||
let mut generator = new_random_generator();
|
||||
|
||||
// generate two random Torus polynomials
|
||||
let mut poly_1 = Polynomial::new(T::ZERO, polynomial_size);
|
||||
generator.fill_slice_with_random_uniform::<T>(poly_1.as_mut());
|
||||
let poly_1 = poly_1;
|
||||
|
||||
let mut poly_2 = Polynomial::new(T::ZERO, polynomial_size);
|
||||
generator.fill_slice_with_random_uniform::<T>(poly_2.as_mut());
|
||||
let poly_2 = poly_2;
|
||||
|
||||
// copy this polynomial
|
||||
let mut sb_mul = Polynomial::new(T::ZERO, polynomial_size);
|
||||
let mut ka_mul = Polynomial::new(T::ZERO, polynomial_size);
|
||||
|
||||
// compute the schoolbook
|
||||
polynomial_wrapping_add_mul_assign_schoolbook(&mut sb_mul, &poly_1, &poly_2);
|
||||
|
||||
// compute the ntt/karatsuba
|
||||
polynomial_wrapping_add_mul_assign(&mut ka_mul, &poly_1, &poly_2);
|
||||
|
||||
// test
|
||||
assert_eq!(&sb_mul, &ka_mul);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// test if we have the same result when using schoolbook or ntt/karatsuba
|
||||
/// for random polynomial multiplication
|
||||
fn test_sub_mul<T: UnsignedTorus>() {
|
||||
for polynomial_log in 4..=12 {
|
||||
for _ in 0..10 {
|
||||
let polynomial_size = PolynomialSize(1 << polynomial_log);
|
||||
let mut generator = new_random_generator();
|
||||
|
||||
// generate two random Torus polynomials
|
||||
let mut poly_1 = Polynomial::new(T::ZERO, polynomial_size);
|
||||
generator.fill_slice_with_random_uniform::<T>(poly_1.as_mut());
|
||||
let poly_1 = poly_1;
|
||||
|
||||
let mut poly_2 = Polynomial::new(T::ZERO, polynomial_size);
|
||||
generator.fill_slice_with_random_uniform::<T>(poly_2.as_mut());
|
||||
let poly_2 = poly_2;
|
||||
|
||||
// copy this polynomial
|
||||
let mut sb_mul = Polynomial::new(T::ZERO, polynomial_size);
|
||||
let mut ka_mul = Polynomial::new(T::ZERO, polynomial_size);
|
||||
|
||||
// compute the schoolbook
|
||||
polynomial_wrapping_sub_mul_assign_schoolbook(&mut sb_mul, &poly_1, &poly_2);
|
||||
|
||||
// compute the ntt/karatsuba
|
||||
polynomial_wrapping_sub_mul_assign(&mut ka_mul, &poly_1, &poly_2);
|
||||
|
||||
// test
|
||||
assert_eq!(&sb_mul, &ka_mul);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_multiply_divide_unit_monomial_u32() {
|
||||
test_multiply_divide_unit_monomial::<u32>()
|
||||
@@ -759,4 +959,24 @@ mod test {
|
||||
pub fn test_multiply_karatsuba_u64() {
|
||||
test_multiply_karatsuba::<u64>()
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_add_mul_u32() {
|
||||
test_add_mul::<u32>()
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_add_mul_u64() {
|
||||
test_add_mul::<u64>()
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_sub_mul_u32() {
|
||||
test_sub_mul::<u32>()
|
||||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_sub_mul_u64() {
|
||||
test_sub_mul::<u64>()
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user