Bläddra i källkod

Preview of First Release (#32)

* adding CMakeListsUser.txt template

* updating cmakelist with python/pybind

* initial bindings for BFV CCParams

* gitignore file

* getters/setters example

* minimal CC + GenCryptoContext

* binding enums for PScheme Features

* key bindings

* Plaintext and Ciphertext bindings

* binding rotations + rotate keygen

* encryption working

* cmake for decryption

* evaladd, evalmult, print method for plaintexts

* plaintext printing working

* cleaning comments

* serialization interface + reorganizing src/includes

* cleaning example

* initial python serial example

* CERAL_REGISTER bugfix

* directly exposing SerTypes + SerializeToFile

* finished SerializeEvalMultKey lambda interface

* deserialization

* simple integers BGV example

* erasing files

* deserialize bindings

* changing file name for consistency

* serial for BGV

* starting ckks

* self parameter

* trying different impl for MakeCKKSPPackedlaintext

* adding self parameter

* unlocking simple ckks example

* typo in readme

* first readme with building instructions

* finishing simple ckks example

* fix pybind link

* fixing makeckks parameters

* wrapper for makeckksplaintext + changing includes

* autoreescaledemo

* autorescale + manual + hybridkeyswitch

* EvalFastRotation Wrappers

* Secret Key Dist enums

* SecurityLevel enums

* parameters for bootstrapping example

* EvalBootstrap + FHECKKS + typos fix

* bootstrapping python script

* fix decrypt syntax error

* cleaning 128 part

* updated documentation

* updated readme to work with conda

* interative bootstrapping + evallogistic

* chebyshev example

* bindings for eval functions

* polynomail evaluation example

* bindings for polynomial

* Erasing wrapper layer for MakeCKKSPackedPlaintext

* Initial binfhe bindings

* binfhe cryptocontext + keys + encryption

* separated file for enc/dec wrappers

* boolean examples

* including decrypt inside cryptocontext class

* get_native_int

* EvalSub(cc,double) + Advanced CKKS 128

* changing namestyle

* keyPair -> key_pair

* fix typo

* new decrypt wrappers

* erasing old dec wrapper + refactoring examples

* advanced real number example

* refactor style 128

* new cryptoparameters

* new enums, remove deprecated cryptoparameters

* EvalAddInPlace, EvalNegate, EvalAddMutable

* more eval functions + EvalKeyImpl exposure

* EvalAutomorphismKeyGen

* EvalFastRotationExtWrapper

* EvalFastRotationExt + EvalAtIndexKeyGen bindings

* clear Params from python interface + examp bug fix

* more evals (sin, cos, divide, etc)

* finish easy evals

* merge conflict fix

* todo mark

* installation path

* cryptocontext docstrings file

* new gitignore

* ciphertext_docs.h

* finished cryptocontext_docs.h

* cryptoparameters_docs.h

* plaintext_docs.h

* add arguments signatures

* binfhecontext docs

* code examples links

* new readme + bugfixes in examples

* updating comments

---------

Co-authored-by: Ian Quah <iquah@dualitytech.com>
Rener Oliveira 1 år sedan
förälder
incheckning
8a09d5998e

+ 6 - 0
.gitignore

@@ -0,0 +1,6 @@
+build/
+.vscode/
+.idea
+.png
+.json
+.txt

+ 69 - 0
CMakeLists.txt

@@ -0,0 +1,69 @@
+cmake_minimum_required (VERSION 3.5.1)
+
+project(openfhe)
+set(CMAKE_CXX_STANDARD 17)
+option( BUILD_STATIC "Set to ON to include static versions of the library" OFF)
+
+find_package(OpenFHE)
+find_package(pybind11 REQUIRED)
+
+set( CMAKE_CXX_FLAGS ${OpenFHE_CXX_FLAGS} )
+set( OpenFHE_Py_SOURCES src)
+set( OpenFHE_Py_INCLUDES include)
+
+include_directories( ${OPENMP_INCLUDES} )
+include_directories( ${OpenFHE_INCLUDE} )
+include_directories( ${OpenFHE_INCLUDE}/third-party/include )
+include_directories( ${OpenFHE_INCLUDE}/core )
+include_directories( ${OpenFHE_INCLUDE}/pke )
+include_directories( ${OpenFHE_INCLUDE}/binfhe )
+# include_directories( ${OpenFHE_Py_SOURCES} )
+include_directories( ${OpenFHE_Py_INCLUDES}/pke )
+include_directories( ${OpenFHE_Py_INCLUDES}/binfhe )
+include_directories( ${OpenFHE_Py_INCLUDES}/docstrings )
+include_directories( ${OpenFHE_Py_INCLUDES} )
+### add directories for other OpenFHE modules as needed for your project
+
+link_directories( ${OpenFHE_LIBDIR} )
+link_directories( ${OPENMP_LIBRARIES} )
+if(BUILD_STATIC)
+    set( CMAKE_EXE_LINKER_FLAGS "${OpenFHE_EXE_LINKER_FLAGS} -static")
+    link_libraries( ${OpenFHE_STATIC_LIBRARIES} )
+else()
+    set( CMAKE_EXE_LINKER_FLAGS ${OpenFHE_EXE_LINKER_FLAGS} )
+    link_libraries( ${OpenFHE_SHARED_LIBRARIES} )
+endif()
+
+### ADD YOUR EXECUTABLE(s) HERE
+### add_executable( EXECUTABLE-NAME SOURCES )
+###
+### EXAMPLE:
+### add_executable( test demo-simple-example.cpp )
+
+### Pybind Modules
+pybind11_add_module(openfhe 
+                    src/bindings.cpp 
+                    src/binfhe_bindings.cpp
+                    src/binfhe/binfhecontext_wrapper.cpp
+                    src/pke/serialization.cpp 
+                    src/pke/cryptocontext_wrapper.cpp
+                    )
+### Python installation 
+# Allow the user to specify the path to Python executable (if not provided, find it)
+option(PYTHON_EXECUTABLE_PATH "Path to Python executable" "")
+
+if(NOT PYTHON_EXECUTABLE_PATH)
+    # Find Python and its development components
+    find_package(Python REQUIRED COMPONENTS Interpreter Development)
+else()
+    # Set Python_EXECUTABLE to the specified path
+    set(Python_EXECUTABLE "${PYTHON_EXECUTABLE_PATH}")
+endif()
+execute_process(
+    COMMAND "${Python_EXECUTABLE}" -c "from distutils.sysconfig import get_python_lib; print(get_python_lib())"
+    OUTPUT_VARIABLE PYTHON_SITE_PACKAGES
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+)
+
+message(STATUS "Python site packages directory: ${PYTHON_SITE_PACKAGES}")
+install(TARGETS openfhe LIBRARY DESTINATION ${PYTHON_SITE_PACKAGES})

+ 113 - 0
README.md

@@ -0,0 +1,113 @@
+# Official Python wrapper for OpenFHE
+
+## Table of Contents
+
+- [Building](#building)
+  - [Prerequisites](#requirements)
+  - [Linux Install](#linux)
+    - [Installing the .so: Conda](#conda)
+    - [Installing the .so: System](#system-install)
+- [Running Examples](#code-examples)
+
+## Building
+
+### Requirements
+
+Before building, make sure you have the following dependencies installed:
+
+- [OpenFHE](https://openfhe-development.readthedocs.io/en/latest/sphinx_rsts/intro/installation/installation.html)
+- [Python 3.6+](https://www.python.org/)
+- [pybind11](https://pybind11.readthedocs.io/en/stable/installing.html)
+
+We recommend following OpenFHE C++ installation instructions first (which covers Linux, Windows and MacOS) and then get back to this repo.
+
+You can install pybind11 by runnning:
+  
+```bash
+pip install pybind11 # or alternatively, if you use conda:
+conda install -c conda-forge pybind11
+```
+For custom installation or any other issues, please refer to the official pybind11 documentation in the link above.
+
+### Linux
+
+With all the dependencies set up, clone the repository, open a terminal in the repo folder and run the following commands:
+
+```bash
+mkdir build
+cd build
+cmake ..  # Alternatively, cmake .. -DOpenFHE_DIR=/path/to/installed/openfhe
+make
+```
+
+At this point the `.so` file has been built. Your exact installation process will depend on your virtual environment.
+Cmake will automatically find the python installation path, if unwanted, you can specify the python path by adding `-DPYTHON_EXECUTABLE_PATH=/path/to/python` to the cmake command.
+
+#### Conda
+
+```bash
+conda create -n ${ENV_NAME} python=3.{X} anaconda
+```
+
+where `${ENV_NAME}` should be replaced with the name of your environment, and `{X}` should be replaced with your desired python version. For example you might have `
+conda create -n openfhe_python python=3.9 anaconda`
+
+It's recommended to specify the python path to avoid any issues with conda environments.
+To do this, run the following commands:
+
+```bash
+mkdir build
+cd build
+cmake ..  # Or cmake .. -DPYTHON_EXECUTABLE_PATH=$CONDA_PREFIX/bin/python
+make
+```
+
+The CONDA_PREFIX variable is set by conda, and points to the root of your active conda environment.
+
+Then, you can develop the library:
+
+```
+mkdir lib
+mv *.so lib
+conda develop lib
+```
+
+which creates a lib folder, moves the built `.so` file into that lib folder, and tells conda where to look for external libraries.
+
+**Note** You may wish to copy the `.so` file to any projects of your own, or add it to your system path to source from.
+
+#### System install
+
+```
+make install  # You may have to run sudo make install
+```
+
+## Code Examples
+
+To get familiar with the OpenFHE Python API, check out the examples:
+
+- FHE for arithmetic over integers (BFV):
+  - [Simple Code Example](src/pke/examples/simple-integers.py)
+  <!-- - [Simple Code Example with Serialization](src/pke/examples/simple-integers-serial.py) -->
+- FHE for arithmetic over integers (BGV):
+  - [Simple Code Example](src/pke/examples/simple-integers-bgvrns.py)
+  <!-- - [Simple Code Example with Serialization](src/pke/examples/simple-integers-serial-bgvrns.py) -->
+- FHE for arithmetic over real numbers (CKKS):
+  - [Simple Code Example](src/pke/examples/simple-real-numbers.py)
+  - [Advanced Code Example](src/pke/examples/advanced-real-numbers.py)
+  - [Advanced Code Example for High-Precision CKKS](src/pke/examples/advanced-real-numbers-128.py)
+  - [Arbitrary Smooth Function Evaluation](src/pke/examples/function-evaluation.py)
+  - [Simple CKKS Bootstrapping Example](src/pke/examples/simple-ckks-bootstrapping.py)
+  - [Advanced CKKS Bootstrapping Example](src/pke/examples/advanced-ckks-bootstrapping.cpp)
+  - [Double-Precision (Iterative) Bootstrapping Example](src/pke/examples/iterative-ckks-bootstrapping.py)
+- FHE for Boolean circuits and larger plaintext spaces (FHEW/TFHE):
+  - [Simple Code Example](src/binfhe/examples/boolean.py)
+  - [Truth Table Example](src/binfhe/examples/boolean-truth-table.py)
+  <!-- - [Code with JSON serialization](src/binfhe/examples/boolean-serial-json.py) -->
+  <!-- - [Code with Binary Serialization](src/binfhe/examples/boolean-serial-binary.py) -->
+  <!-- - [Large-Precision Comparison](src/binfhe/examples/eval-sign.py) -->
+  <!-- - [Small-Precison Arbitrary Function Evaluation](src/binfhe/examples/eval-function.py) -->
+<!-- - Threshold FHE:
+  - [Code Example for BGV, BFV, and CKKS](src/pke/examples/threshold-fhe.py)
+  - [Code Example for BFV with 5 parties](src/pke/examples/threshold-fhe-5p.py) -->
+

+ 14 - 0
include/bindings.h

@@ -0,0 +1,14 @@
+#ifndef OPENFHE_BINDINGS_H
+#define OPENFHE_BINDINGS_H
+
+#include <pybind11/pybind11.h>
+
+void bind_parameters(pybind11::module &m);
+void bind_crypto_context(pybind11::module &m);
+void bind_enums_and_constants(pybind11::module &m);
+void bind_keys(pybind11::module &m);
+void bind_encodings(pybind11::module &m);
+void bind_ciphertext(pybind11::module &m);
+void bind_serialization(pybind11::module &m);
+void bind_schemes(pybind11::module &m);
+#endif // OPENFHE_BINDINGS_H

+ 22 - 0
include/binfhe/binfhecontext_wrapper.h

@@ -0,0 +1,22 @@
+#ifndef BINFHE_CRYPTOCONTEXT_BINDINGS_H
+#define BINFHE_CRYPTOCONTEXT_BINDINGS_H
+
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+#include "openfhe.h"
+#include "binfhecontext.h"
+
+namespace py = pybind11;
+using namespace lbcrypto;
+LWECiphertext binfhe_EncryptWrapper(BinFHEContext &self,
+                                    ConstLWEPrivateKey sk,
+                                    const LWEPlaintext &m,
+                                    BINFHE_OUTPUT output,
+                                    LWEPlaintextModulus p,
+                                    uint64_t mod);
+LWEPlaintext binfhe_DecryptWrapper(BinFHEContext &self,
+                                   ConstLWEPrivateKey sk,
+                                   ConstLWECiphertext ct,
+                                   LWEPlaintextModulus p);
+
+#endif // BINFHE_CRYPTOCONTEXT_BINDINGS_H

+ 10 - 0
include/binfhe_bindings.h

@@ -0,0 +1,10 @@
+#ifndef BINFHE_BINDINGS_H
+#define BINFHE_BINDINGS_H
+
+#include <pybind11/pybind11.h>
+
+void bind_binfhe_enums(pybind11::module &m);
+void bind_binfhe_context(pybind11::module &m);
+void bind_binfhe_keys(pybind11::module &m);
+void bind_binfhe_ciphertext(pybind11::module &m);
+#endif // BINFHE_BINDINGS_H

+ 91 - 0
include/docstrings/binfhecontext_docs.h

@@ -0,0 +1,91 @@
+#ifndef BINFHECONTEXT_DOCSTRINGS_H
+#define BINFHECONTEXT_DOCSTRINGS_H
+
+// BinFHEContext Docs:
+const char* binfhe_GenerateBinFHEContext_parset_docs = R"doc(
+    Creates a crypto context using predefined parameters sets. Recommended for most users.
+
+    Parameters:
+    ----------
+        set (BINFHE_PARAMSET): the parameter set: TOY, MEDIUM, STD128, STD192, STD256.
+        method (BINFHE_METHOD):  the bootstrapping method (DM or CGGI).
+
+    Returns:
+    --------
+        create the crypto context
+)doc";
+
+const char* binfhe_KeyGen_docs = R"doc(
+    Generates a secret key for the main LWE scheme
+
+    Returns:
+    --------
+        LWEPrivateKey: the secret key
+)doc";
+
+const char* binfhe_BTKeyGen_docs = R"doc(
+    Generates bootstrapping keys
+
+    Psrameters:
+    -----------
+        sk (LWEPrivateKey): secret key
+)doc";
+
+const char* binfhe_Encrypt_docs = R"doc(
+    Encrypts a bit using a secret key (symmetric key encryption)
+
+    Parameters:
+    -----------
+        sk (LWEPrivateKey): the secret key
+        m (int): the plaintext
+        output (BINFHE_OUTPUT):  FRESH to generate fresh ciphertext, BOOTSTRAPPED to generate a refreshed ciphertext (default)
+        p (int): plaintext modulus (default 4)
+        mod (int): Encrypt according to mod instead of m_q if mod != 0
+
+    Returns:
+    --------
+        LWECiphertext: the ciphertext
+)doc";
+
+const char* binfhe_Decrypt_docs = R"doc(
+    Encrypt according to mod instead of m_q if mod != 0
+
+    Parameters:
+    -----------
+        sk (LWEPrivateKey): the secret key
+        ct (LWECiphertext): the ciphertext
+        p (int): plaintext modulus (default 4)
+
+    Returns:
+    --------
+       int: the plaintext
+)doc";
+
+const char* binfhe_EvalBinGate_docs = R"doc(
+    Evaluates a binary gate (calls bootstrapping as a subroutine)
+
+    Parameters:
+    -----------
+        gate (BINGATE): the gate; can be AND, OR, NAND, NOR, XOR, or XNOR
+        ct1 (LWECiphertext): first ciphertext
+        ct2 (LWECiphertext): second ciphertext
+
+    Returns:
+    --------
+        LWECiphertext: the resulting ciphertext
+)doc";
+
+const char* binfhe_EvalNOT_docs = R"doc(
+    Evaluates NOT gate
+
+    Parameters:
+    -----------
+        ct (LWECiphertext): the input ciphertext
+
+    Returns:
+    --------
+        LWECiphertext: the resulting ciphertext
+)doc";
+
+
+#endif // BINFHECONTEXT_DOCSTRINGS_H

+ 19 - 0
include/docstrings/ciphertext_docs.h

@@ -0,0 +1,19 @@
+#ifndef CIPHERTEXT_DOCSTRINGS_H
+#define CIPHERTEXT_DOCSTRINGS_H
+
+const char* ctx_GetLevel_docs = R"doc(
+    Get the number of scalings performed
+
+    Returns
+    -------
+        int: The level of the ciphertext.
+)doc";
+
+const char* ctx_SetLevel_docs = R"doc(
+    Set the number of scalings
+
+    Parameters
+    ----------
+        level (int): The level to set.
+)doc";
+#endif // CIPHERTEXT_DOCSTRINGS_H

+ 1184 - 0
include/docstrings/cryptocontext_docs.h

@@ -0,0 +1,1184 @@
+#ifndef CRYPTOCONTEXT_DOCSTRINGS_H
+#define CRYPTOCONTEXT_DOCSTRINGS_H
+
+const char* cc_SetKeyGenLevel_docs = R"doc(
+    Parameters:
+    ----------
+        level (int): the level to set the key generation to
+)doc";
+
+const char* cc_GetKeyGenLevel_docs = R"doc(
+    Get the level used for key generation
+
+    Returns:
+        int: The level used for key generation
+)doc";
+
+const char* cc_GetRingDimension_docs = R"doc(
+    Get the ring dimension used for this context
+
+    Returns:
+        int: The ring dimension
+)doc";
+
+const char* cc_Enable_docs = R"doc(
+    Enable a particular feature for use with this CryptoContext
+
+    Parameters:
+    ----------
+        feature (PKESchemeFeature): the feature that should be enabled. 
+            The list of available features is defined in the PKESchemeFeature enum.
+    
+)doc";
+
+const char* cc_KeyGen_docs = R"doc(
+    Generate a public and private key pair
+
+    Returns:
+        KeyPair: a public/secret key pair
+)doc";
+
+const char* cc_EvalMultKeyGen_docs = R"doc(
+    EvalMultKeyGen creates a key that can be used with the OpenFHE EvalMult operator the new evaluation key is stored in cryptocontext.
+
+    Parameters:
+    ----------
+        privateKey (PrivateKey): the private key
+)doc";
+
+const char* cc_EvalMultKeysGen_docs = R"doc(
+    EvalMultsKeyGen creates a vector evalmult keys that can be used with the OpenFHE EvalMult operator 1st key (for s^2) is used for multiplication of ciphertexts of depth 1 2nd key (for s^3) is used for multiplication of ciphertexts of depth 2, etc. a vector of new evaluation keys is stored in crytpocontext
+
+    Parameters:
+    ----------
+        privateKey (PrivateKey): the private key
+)doc";
+
+const char* cc_EvalRotateKeyGen_docs = R"doc(
+    EvalRotateKeyGen generates evaluation keys for a list of indices
+
+    Parameters:
+    ----------
+        privateKey (PrivateKey): private key
+        indexList (list): list of (integers) indices
+        publicKey (PublicKey): public key (used in NTRU schemes)
+)doc";
+
+// MakeStringPlaintext
+const char* cc_MakeStringPlaintext_docs = R"doc(
+    MakeStringPlaintext constructs a StringEncoding in this context
+
+    Parameters:
+    ----------
+        str (str): the string to convert
+
+    Returns:
+    ----------
+        Plaintext: plaintext
+)doc";
+
+//MakePackedPlaintext
+const char* cc_MakePackedPlaintext_docs = R"doc(
+    MakePackedPlaintext constructs a PackedEncoding in this context
+
+    Parameters:
+    ----------
+        value (list): the vector (of integers) to convert
+        depth (int): is the multiplicative depth to encode the plaintext at
+        level (int): is the level to encode the plaintext at
+
+    Returns:
+    ----------
+        Plaintext: plaintext
+)doc";
+
+//inline Plaintext MakeCoefPackedPlaintext(const std::vector<int64_t> &value, size_t depth = 1, uint32_t level = 0) const
+const char* cc_MakeCoefPackedPlaintext_docs = R"doc(
+    MakeCoefPackedPlaintext constructs a CoefPackedEncoding in this context
+
+    Parameters:
+    ----------
+        value (list): the vector (of integers) to convert
+        depth (int): is the multiplicative depth to encode the plaintext at
+        level (int): is the level to encode the plaintext at
+
+    Returns:
+    ----------
+        Plaintext: plaintext
+)doc";
+//MakeCKKSPackedPlaintext(const std::vector<std::complex<double>> &value, size_t depth = 1, uint32_t level = 0, const std::shared_ptr<ParmType> params = nullptr, usint slots = 0)
+const char* cc_MakeCKKSPackedPlaintextComplex_docs = R"doc(
+    COMPLEX ARITHMETIC IS NOT AVAILABLE STARTING WITH OPENFHE 1.10.6, AND THIS METHOD BE DEPRECATED. USE THE REAL-NUMBER METHOD INSTEAD. MakeCKKSPackedPlaintext constructs a CKKSPackedEncoding in this context from a vector of complex numbers
+
+    Parameters:
+    ----------
+        value (list): input vector (of complex numbers)
+        depth (int): depth used to encode the vector
+        level (int): level at each the vector will get encrypted
+        params (openfhe.ParmType): parameters to be used for the ciphertext (Only accepting params = None in this version)
+        slots (int): number of slots
+
+    Returns:
+    ----------
+        Plaintext: plaintext
+)doc";
+
+//MakeCKKSPlaintextReal
+const char* cc_MakeCKKSPlaintextReal_docs = R"doc(
+    MakeCKKSPlaintext constructs a CKKSPackedEncoding in this context from a vector of real numbers
+
+    Parameters:
+    ----------
+        value (list): input vector (of floats)
+        depth (int): depth used to encode the vector
+        level (int): level at each the vector will get encrypted
+        params (openfhe.ParmType): parameters to be used for the ciphertext (Only accepting params = None in this version)
+        slots (int): number of slots
+
+    Returns:
+    ----------
+        Plaintext: plaintext
+)doc";
+
+//EvalRotate
+const char* cc_EvalRotate_docs = R"doc(
+    EvalRotate rotates a ciphertext by a given index
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): the ciphertext to rotate
+        index (int): the index of the rotation. Positive indices correspond to left rotations and negative indices correspond to right rotations.
+
+    Returns:
+    ----------
+        Ciphertext: the rotated ciphertext
+)doc";
+
+//EvalFastRotationPreCompute
+const char* cc_EvalFastRotationPreCompute_docs = R"doc(
+    EvalFastRotationPrecompute implements the precomputation step of hoisted automorphisms.
+
+    Please refer to Section 5 of Halevi and Shoup, "Faster Homomorphic
+    linear transformations in HELib." for more details, link:
+    https://eprint.iacr.org/2018/244.
+
+    Generally, automorphisms are performed with three steps:
+    (1) The automorphism is applied to the ciphertext.
+    (2) The automorphed values are decomposed into digits.
+    (3) Key switching is applied to enable further computations on the ciphertext.
+
+    Hoisted automorphisms are a technique that performs the digit decomposition for the original ciphertext first,
+    and then performs the automorphism and the key switching on the decomposed digits.
+    The benefit of this is that the digit decomposition is independent of the automorphism rotation index,
+    so it can be reused for multiple different indices.
+    This can greatly improve performance when we have to compute many automorphisms on the same ciphertext.
+    This routinely happens when we do permutations (EvalPermute).
+
+    EvalFastRotationPrecompute implements the digit decomposition step of hoisted automorphisms.
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): the input ciphertext on which to do the precomputation (digit decomposition)
+
+    Returns:
+    ----------
+        Ciphertext: the precomputed ciphertext created using the digit decomposition
+)doc";
+
+//EvalFastRotation
+const char* cc_EvalFastRotation_docs = R"doc(
+    EvalFastRotation implements the automorphism and key switching step of hoisted automorphisms.
+
+    Please refer to Section 5 of Halevi and Shoup, "Faster Homomorphic
+    linear transformations in HELib." for more details, link:
+    https://eprint.iacr.org/2018/244.
+
+    Generally, automorphisms are performed with three steps:
+    (1) The automorphism is applied to the ciphertext.
+    (2) The automorphed values are decomposed into digits.
+    (3) Key switching is applied to enable further computations on the ciphertext.
+
+    Hoisted automorphisms are a technique that performs the digit decomposition for the original ciphertext first,
+    and then performs the automorphism and the key switching on the decomposed digits.
+    The benefit of this is that the digit decomposition is independent of the automorphism rotation index,
+    so it can be reused for multiple different indices.
+    This can greatly improve performance when we have to compute many automorphisms on the same ciphertext.
+    This routinely happens when we do permutations (EvalPermute).
+
+    EvalFastRotation implements the automorphism and key switching step of hoisted automorphisms.
+
+    This method assumes that all required rotation keys exist.
+    This may not be true if we are using baby-step/giant-step key switching.
+    Please refer to Section 5.1 of the above reference and EvalPermuteBGStepHoisted to see how to deal with this issue.
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext):  the input ciphertext to perform the automorphism on
+        index (int): the index of the rotation. Positive indices correspond to left rotations and negative indices correspond to right rotations.
+        m (int): is the cyclotomic order
+        digits (Ciphertext): the precomputed ciphertext created by EvalFastRotationPrecompute using the digit decomposition at the precomputation step
+)doc";
+
+//EvalFastRotationExt
+const char* cc_EvalFastRotationExt_docs = R"doc(
+    Only supported for hybrid key switching. Performs fast (hoisted) rotation and returns the results in the extended CRT basis P*Q
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): input ciphertext
+        index (int): the rotation index
+        digits (Ciphertext): the precomputed ciphertext created by EvalFastRotationPrecompute
+        addFirst (bool): if true, the the first element c0 is also computed (otherwise ignored)
+    
+    Returns:
+    ----------
+        Ciphertext: resulting ciphertext
+)doc";
+//phertext<Element> EvalAtIndex(ConstCiphertext<Element> ciphertext, int32_t index) const
+const char* cc_EvalAtIndex_docs = R"doc(
+    Moves i-th slot to slot 0
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): the ciphertext
+        i (int): the index
+    
+    Returns:
+    ----------
+        Ciphertext: resulting ciphertext
+)doc";
+//void EvalAtIndexKeyGen(const PrivateKey<Element> privateKey, const std::vector<int32_t> &indexList, const PublicKey<Element> publicKey = nullptr)
+const char* cc_EvalAtIndexKeyGen_docs = R"doc(
+    EvalAtIndexKeyGen generates evaluation keys for a list of indices
+
+    Parameters:
+    ----------
+        privateKey (PrivateKey): the private key
+        indexList (list): list of indices
+        publicKey (PublicKey): the public key (used in NTRU schemes)
+    
+    Returns:
+    ----------
+        None
+)doc";
+
+//Encrypt
+const char* cc_Encrypt_docs = R"doc(
+    Encrypt a plaintext using a given public key
+
+    Parameters:
+    ----------
+        plaintext (Plaintext): the plaintext to encrypt
+        publicKey (PublicKey): the public key
+
+    Returns:
+    ----------
+        Ciphertext: ciphertext (or null on failure)
+)doc";
+
+//Decrypt
+const char* cc_Decrypt_docs = R"doc(
+    Decrypt a single ciphertext into the appropriate plaintext
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): ciphertext to decrypt
+        privateKey (PrivateKey): decryption key
+
+    Returns:
+    ----------
+        Plaintext: decrypted plaintext
+)doc";
+
+//EvalAdd
+const char* cc_EvalAdd_docs = R"doc(
+    Add two ciphertexts
+
+    Parameters:
+    ----------
+        ct1 (Ciphertext): first ciphertext
+        ct2 (Ciphertext): second ciphertext
+
+    Returns:
+    ----------
+        Ciphertext: resulting ciphertext
+)doc";
+
+//EvalAdd(ciphertext,double)
+const char* cc_EvalAddfloat_docs = R"doc(
+    EvalAdd - OpenFHE EvalAdd method for a ciphertext and constant
+
+    Parameters:
+    ----------
+        ct (Ciphertext): ciphertext
+        constant (float): constant to add
+
+    Returns:
+    ----------
+        Ciphertext: new ciphertext for ciphertext + constant
+)doc";
+
+//EvalAddInPlace
+const char* cc_EvalAddInPlace_docs = R"doc(
+    EvalAdd - OpenFHE EvalAddInPlace method for a pair of ciphertexts
+
+    Parameters:
+    ----------
+        ct1 (Ciphertext): Input/output ciphertext
+        ct2 (Ciphertext): Input cipherext
+
+    Returns:
+    ----------
+        ct1 contains ct1 + ct2
+)doc";
+
+//EvalAddInPlace(ciphertext,plaintext)
+const char* cc_EvalAddInPlacePlaintext_docs = R"doc(
+    EvalAdd - OpenFHE EvalAddInPlace method for a ciphertext and plaintext
+
+    Parameters:
+    ----------
+        ct (Ciphertext): Input/output ciphertext
+        pt (Plaintext): Input plaintext
+
+    Returns:
+    ----------
+        ct contains ct + pt
+)doc";
+
+//EvalAddMutable
+const char* cc_EvalAddMutable_docs = R"doc(
+    EvalAdd - OpenFHE EvalAddMutable method for a pair of ciphertexts. This is a mutable version - input ciphertexts may get automatically rescaled, or level-reduced.
+
+    Parameters:
+    ----------
+        ct1 (Ciphertext): first ciphertext
+        ct2 (Ciphertext): second ciphertext
+
+    Returns:
+    ----------
+        Ciphertext: new ciphertext for ct1 + ct2
+)doc";
+
+//EvalAddMutable(ciphertext,plaintext)
+const char* cc_EvalAddMutablePlaintext_docs = R"doc(
+    EvalAdd - OpenFHE EvalAddMutable method for a ciphertext and plaintext. This is a mutable version - input ciphertexts may get automatically rescaled, or level-reduced.
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): ciphertext
+        plaintext (Plaintext): plaintext
+
+    Returns:
+    ----------
+        Ciphertext: new ciphertext for ciphertext + plaintext
+)doc";
+
+//EvalAddMutableInPlace
+const char* cc_EvalAddMutableInPlace_docs = R"doc(
+    EvalAdd - Inplace version of EvalAddMutable
+
+    Parameters:
+    ----------
+        ct1 (Ciphertext): Input/output ciphertext
+        ct2 (Ciphertext): Input cipherext
+
+    Returns:
+    ----------
+        ct1 contains ct1 + ct2
+)doc";
+
+//EvalSub
+const char* cc_EvalSub_docs = R"doc(
+    EvalSub - OpenFHE EvalSub method for a pair of ciphertexts
+
+    Parameters:
+    ----------
+        ct1 (Ciphertext): first ciphertext
+        ct2 (Ciphertext): second ciphertext
+
+    Returns:
+    ----------
+        Ciphertext: new ciphertext for ct1 - ct2
+)doc";
+
+//EvalSub(ciphertext,double)
+const char* cc_EvalSubfloat_docs = R"doc(
+    EvalSub - OpenFHE EvalSub method for a ciphertext and constant
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): ciphertext
+        constant (float): constant to subtract
+
+    Returns:
+    ----------
+        Ciphertext: new ciphertext for ciphertext - constant
+)doc";
+
+//EvalSub(ciphertext,plaintext)
+const char* cc_EvalSubPlaintext_docs = R"doc(
+    EvalSub - OpenFHE EvalSub method for a ciphertext and plaintext
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): ciphertext
+        plaintext (Plaintext): plaintext
+
+    Returns:
+    ----------
+        Ciphertext: new ciphertext for ciphertext - plaintext
+)doc";
+
+//EvalSubInPlace
+const char* cc_EvalSubInPlace_docs = R"doc(
+    Inplace version of EvalSub for a pair of ciphertexts
+
+    Parameters:
+    ----------
+        ct1 (Ciphertext): Input/output ciphertext
+        ct2 (Ciphertext): Input cipherext
+
+    Returns:
+    ----------
+        ct1 contains ct1 - ct2
+)doc";
+
+//EvalSubInPlace(ciphertext,double)
+const char* cc_EvalSubInPlacefloat_docs = R"doc(
+    Inplace version of EvalSub for a ciphertext and constant
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): Input/output ciphertext
+        constant (float): constant to subtract
+
+    Returns:
+    ----------
+        ciphertext contains ciphertext - constant
+)doc";
+
+//EvalSubMutable
+const char* cc_EvalSubMutable_docs = R"doc(
+    EvalSub - OpenFHE EvalSubMutable method for a pair of ciphertexts. This is a mutable version - input ciphertexts may get automatically rescaled, or level-reduced.
+
+    Parameters:
+    ----------
+        ct1 (Ciphertext): first ciphertext
+        ct2 (Ciphertext): second ciphertext
+
+    Returns:
+    ----------
+        Ciphertext: new ciphertext for ct1 - ct2
+)doc";
+
+const char* cc_EvalSubMutableInPlace_docs = R"doc(
+    EvalSub - Inplace variant for EvalSubMutable
+)doc";
+//EvalSubMutable(ciphertext,plaintext)
+const char* cc_EvalSubMutablePlaintext_docs = R"doc(
+    EvalSub - OpenFHE EvalSubMutable method for a ciphertext and plaintext. This is a mutable version - input ciphertexts may get automatically rescaled, or level-reduced.
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): 
+        plaintext (Plaintext): 
+
+    Returns:
+    ----------
+        Ciphertext: new ciphertext for ciphertext - plaintext
+)doc";
+
+//EvalMult
+const char* cc_EvalMult_docs = R"doc(
+    EvalMult - OpenFHE EvalMult method for a pair of ciphertexts - with key switching
+
+    Parameters:
+    ----------
+        ct1 (Ciphertext): first ciphertext
+        ct2 (Ciphertext): second ciphertext
+
+    Returns:
+    ----------
+        Ciphertext: new ciphertext for ct1 * ct2
+)doc";
+
+//EvalMult(ciphertext,double)
+const char* cc_EvalMultfloat_docs = R"doc(
+    EvalMult - OpenFHE EvalMult method for a ciphertext and constant
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): the ciphertext
+        constant (float): constant to multiply
+
+    Returns:
+    ----------
+        Ciphertext: new ciphertext for ciphertext * constant
+)doc";
+
+//EvalMult(ciphertext,plaintext)
+const char* cc_EvalMultPlaintext_docs = R"doc(
+    EvalMult - OpenFHE EvalMult method for a ciphertext and plaintext
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): the ciphertext
+        plaintext (Plaintext): the plaintext
+
+    Returns:
+    ----------
+        Ciphertext: new ciphertext for ciphertext * plaintext
+)doc";
+
+//EvalMultMutable
+const char* cc_EvalMultMutable_docs = R"doc(
+    EvalMult - OpenFHE EvalMultMutable method for a pair of ciphertexts. This is a mutable version - input ciphertexts may get automatically rescaled, or level-reduced.
+
+    Parameters:
+    ----------
+        ct1 (Ciphertext): first ciphertext
+        ct2 (Ciphertext): second ciphertext
+
+    Returns:
+    ----------
+        Ciphertext: new ciphertext for ct1 * ct2
+)doc";
+
+//EvalMultMutable(ciphertext,plaintext)
+const char* cc_EvalMultMutablePlaintext_docs = R"doc(
+    EvalMult - OpenFHE EvalMultMutable method for a ciphertext and plaintext. This is a mutable version - input ciphertexts may get automatically rescaled, or level-reduced.
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): the ciphertext
+        plaintext (Plaintext): the plaintext
+
+    Returns:
+    ----------
+        Ciphertext: new ciphertext for ciphertext * plaintext
+)doc";
+
+//EvalMultMutableInPlace
+const char* cc_EvalMultMutableInPlace_docs = R"doc(
+    EvalMult - OpenFHE EvalMult method for a pair of ciphertexts - with key switching This is a mutable version - input ciphertexts may get automatically rescaled, or level-reduced.
+
+    Parameters:
+    ----------
+        ct1 (Ciphertext): Input/output ciphertext
+        ct2 (Ciphertext): Input cipherext
+
+    Returns:
+    ----------
+        ct1 contains ct1 * ct2
+)doc";
+
+//EvalSquare
+const char* cc_EvalSquare_docs = R"doc(
+    EvalSquare - OpenFHE EvalSquare method for a ciphertext
+
+    Parameters:
+    ----------
+        ct (Ciphertext): the ciphertext to square
+
+    Returns:
+    ----------
+        Ciphertext: new ciphertext for ct^2 = ct * ct
+)doc";
+
+//EvalSquareMutable
+const char* cc_EvalSquareMutable_docs = R"doc(
+    EvalSquare - OpenFHE EvalSquareMutable method for a ciphertext. This is a mutable version - input ciphertexts may get automatically rescaled, or level-reduced.
+
+    Parameters:
+    ----------
+        ct (Ciphertext): the ciphertext to square
+
+    Returns:
+    ----------
+        Ciphertext: new ciphertext for ct^2 = ct * ct
+)doc";
+
+//EvalSquareInPlace
+const char* cc_EvalSquareInPlace_docs = R"doc(
+    EvalSquare - OpenFHE EvalSquare method for a ciphertext. This is a mutable version - input ciphertexts may get automatically rescaled, or level-reduced.
+
+    Parameters:
+    ----------
+        ct (Ciphertext): Input/output ciphertext
+
+    Returns:
+    ----------
+        ct contains ct^2 = ct * ct
+)doc";
+
+//EvalMultNoRelin
+const char* cc_EvalMultNoRelin_docs = R"doc(
+    EvalMultNoRelin - OpenFHE EvalMult method for a pair of ciphertexts - no key switching (relinearization)
+
+    Parameters:
+    ----------
+        ct1 (Ciphertext): first ciphertext
+        ct2 (Ciphertext): second ciphertext
+
+    Returns:
+    ----------
+        Ciphertext: new ciphertext for ct1 * ct2
+)doc";
+
+//Relinearize
+const char* cc_Relinearize_docs = R"doc(
+    Function for relinearization of a ciphertext.
+
+    Parameters:
+    ----------
+        ct (Ciphertext): input ciphertext
+
+    Returns:
+    ----------
+        Ciphertext: relienarized ciphertext
+)doc";
+
+//RelinearizeInPlace
+const char* cc_RelinearizeInPlace_docs = R"doc(
+    Function for inplace relinearization of a ciphertext.
+
+    Parameters:
+    ----------
+        ct (Ciphertext): input/output ciphertext
+
+    Returns:
+    ----------
+        ct contains relienarized ciphertext
+)doc";
+
+//EvalMultAndRelinearize
+const char* cc_EvalMultAndRelinearize_docs = R"doc(
+    Function for evaluating multiplication on ciphertext followed by relinearization operation. Currently it assumes that the input arguments have total depth smaller than the supported depth. Otherwise, it throws an error
+
+    Parameters:
+    ----------
+        ct1 (Ciphertext): first input ciphertext
+        ct2 (Ciphertext): second input ciphertext
+
+    Returns:
+    ----------
+        Ciphertext: new ciphertext
+)doc";
+
+//EvalNegate
+const char* cc_EvalNegate_docs = R"doc(
+    EvalSub - OpenFHE Negate method for a ciphertext
+
+    Parameters:
+    ----------
+        ct (Ciphertext): input ciphertext
+
+    Returns:
+    ----------
+        Ciphertext: new ciphertext -ct
+)doc";
+
+//EvalNegateInPlace
+const char* cc_EvalNegateInPlace_docs = R"doc(
+    EvalSub - Inplace OpenFHE Negate method for a ciphertext
+
+    Parameters:
+    ----------
+        ct (Ciphertext): input/output ciphertext
+
+    Returns:
+    ----------
+        ct contains -ct
+)doc";
+
+//EvalLogistic((ConstCiphertext<Element> ciphertext, double a, double b, uint32_t degree)
+// const char* cc_EvalLogistic_docs = R"doc(
+//     Evaluate approximate logistic function 1/(1 + exp(-x)) on a ciphertext using the Chebyshev approximation.
+
+//     Parameters:
+//     ----------
+//         ciphertext (Ciphertext): input ciphertext
+//         a (float): lower bound of argument for which the coefficients were found
+//         b (float): upper bound of argument for which the coefficients were found
+//         degree (int): Desired degree of approximation
+
+//     Returns:
+//     ----------
+//         Ciphertext: the result of polynomial evaluation
+// )doc";
+
+//EvalChebyshevSeries(ConstCiphertext<Element> ciphertext, const std::vector<double> &coefficients, double a, double b)
+const char* cc_EvalChebyshevSeries_docs = R"doc(
+    Method for evaluating Chebyshev polynomial interpolation; first the range [a,b] is mapped to [-1,1] using linear transformation 1 + 2 (x-a)/(b-a) If the degree of the polynomial is less than 5, use EvalChebyshevSeriesLinear, otherwise, use EvalChebyshevSeriesPS.
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): input ciphertext
+        coefficients (list): is the list of coefficients in Chebyshev expansion
+        a (float): lower bound of argument for which the coefficients were found
+        b (float): upper bound of argument for which the coefficients were found
+
+    Returns:
+    ----------
+        Ciphertext: the result of polynomial evaluation
+)doc";
+
+//EvalChebyshevSeriesLinear(ConstCiphertext<Element> ciphertext, const std::vector<double> &coefficients, double a, double b)
+const char* cc_EvalChebyshevSeriesLinear_docs = R"doc(
+    Evaluate Chebyshev polynomial of degree less than 5.
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): input ciphertext
+        coefficients (list): is the list of coefficients in Chebyshev expansion
+        a (float): lower bound of argument for which the coefficients were found
+        b (float): upper bound of argument for which the coefficients were found
+
+    Returns:
+    ----------
+        Ciphertext: the result of polynomial evaluation
+)doc";
+
+//EvalChebyshevSeriesPS(ConstCiphertext<Element> ciphertext, const std::vector<double> &coefficients, double a, double b)
+const char* cc_EvalChebyshevSeriesPS_docs = R"doc(
+    Evaluate Chebyshev polynomial of degree greater or equal to 5.
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): input ciphertext
+        coefficients (list): is the list of coefficients in Chebyshev expansion
+        a (float): lower bound of argument for which the coefficients were found
+        b (float): upper bound of argument for which the coefficients were found
+
+    Returns:
+    ----------
+        Ciphertext: the result of polynomial evaluation
+)doc";
+
+//EvalChebyshevFunction(std::function<double(double)> func, ConstCiphertext<Element> ciphertext, double a, double b, uint32_t degree)
+const char* cc_EvalChebyshevFunction_docs = R"doc(
+    Method for calculating Chebyshev evaluation on a ciphertext for a smooth input function over the range [a,b].
+
+    Parameters:
+    ----------
+        func (function): is the function to be approximated
+        ciphertext (Ciphertext): input ciphertext
+        a (float): lower bound of argument for which the coefficients were found
+        b (float): upper bound of argument for which the coefficients were found
+        degree (int): Desired degree of approximation
+
+    Returns:
+    ----------
+        Ciphertext: the coefficients of the Chebyshev approximation.
+)doc";
+
+//EvanSin(ciphertext,double,double,degree)
+const char* cc_EvalSin_docs = R"doc(
+    Evaluate approximate sine function on a ciphertext using the Chebyshev approximation.
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): input ciphertext
+        a (float): lower bound of argument for which the coefficients were found
+        b (float): upper bound of argument for which the coefficients were found
+        degree (int): Desired degree of approximation
+
+    Returns:
+    ----------
+        Ciphertext: the result of polynomial evaluation.
+)doc";
+
+//EvalCos(ciphertext,double,double,degree)
+const char* cc_EvalCos_docs = R"doc(
+    Evaluate approximate cosine function on a ciphertext using the Chebyshev approximation.
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): input ciphertext
+        a (float): lower bound of argument for which the coefficients were found
+        b (float): upper bound of argument for which the coefficients were found
+        degree (int): Desired degree of approximation
+
+    Returns:
+    ----------
+        Ciphertext: the result of polynomial evaluation.
+)doc";
+
+//EvalLogistic(ciphertext,double,double,degree)
+const char* cc_EvalLogistic_docs = R"doc(
+    Evaluate approximate logistic function 1/(1 + exp(-x)) on a ciphertext using the Chebyshev approximation.
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): input ciphertext
+        a (float): lower bound of argument for which the coefficients were found
+        b (float): upper bound of argument for which the coefficients were found
+        degree (int): Desired degree of approximation
+
+    Returns:
+    ----------
+        Ciphertext: the result of polynomial evaluation.
+)doc";
+
+//EvalDivide(ciphertext,double,double,degree)
+const char* cc_EvalDivide_docs = R"doc(
+    Evaluate approximate division function 1/x where x >= 1 on a ciphertext using the Chebyshev approximation.
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): input ciphertext
+        a (float): lower bound of argument for which the coefficients were found
+        b (float): upper bound of argument for which the coefficients were found
+        degree (int): Desired degree of approximation
+
+    Returns:
+    ----------
+        Ciphertext: the result of polynomial evaluation.
+)doc";
+
+//EvalSumKeyGen(const PrivateKey<Element> privateKey, const PublicKey<Element> publicKey = nullptr)
+const char* cc_EvalSumKeyGen_docs = R"doc(
+    EvalSumKeyGen generates the key map to be used by evalsum
+
+    Parameters:
+    ----------
+        privateKey (PrivateKey): private key
+        publicKey (PublicKey): public key (used in NTRU schemes)
+    
+    Returns:
+    ----------
+        None
+)doc";
+
+//EvalSumRowsKeyGen(const PrivateKey<Element> privateKey, const PublicKey<Element> publicKey = nullptr, usint rowSize = 0, usint subringDim = 0)
+const char* cc_EvalSumRowsKeyGen_docs = R"doc(
+    EvalSumRowsKeyGen generates the key map to be used by EvalSumRows
+
+    Parameters:
+    ----------
+        privateKey (PrivateKey): private key
+        publicKey (PublicKey): public key (used in NTRU schemes)
+        rowSize (int): number of rows
+        subringDim (int): dimension of the subring
+    
+    Returns:
+    ----------
+        dict: Evaluation key map, where the keys being integer indexes and values being EvalKey objects
+)doc";
+
+//EvalSumColsKeyGen(const PrivateKey<Element> privateKey, const PublicKey<Element> publicKey = nullptr)
+const char* cc_EvalSumColsKeyGen_docs = R"doc(
+    EvalSumColsKeyGen generates the key map to be used by EvalSumCols
+
+    Parameters:
+    ----------
+        privateKey (PrivateKey): private key
+        publicKey (PublicKey): public key (used in NTRU schemes)
+    
+    Returns:
+    ----------
+        dict: Evaluation key map, where the keys being integer indexes and values being EvalKey objects
+)doc";
+
+//Ciphertext<Element> EvalSumRows(ConstCiphertext<Element> ciphertext, usint rowSize, const std::map<usint, EvalKey<Element>> &evalSumKeyMap, usint subringDim = 0)
+const char* cc_EvalSumRows_docs = R"doc(
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): input ciphertext
+        rowSize (int): number of rows
+        evalSumKeyMap (dict): evaluation key map, where the keys being integer indexes and values being EvalKey objects
+        subringDim (int): dimension of the subring
+    
+    Returns:
+    ----------
+        Ciphertext: resulting ciphertext
+)doc";
+
+//Ciphertext<Element> EvalSumCols(ConstCiphertext<Element> ciphertext, usint rowSize, const std::map<usint, EvalKey<Element>> &evalSumKeyMap
+const char* cc_EvalSumCols_docs = R"doc(
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): input ciphertext
+        rowSize (int): number of rows
+        evalSumKeyMap (dict): evaluation key map, where the keys being integer indexes and values being EvalKey objects
+    
+    Returns:
+    ----------
+        Ciphertext: resulting ciphertext
+)doc";
+//EvalInnerProduct(ciphertext,ciphertext,batchSize)
+const char* cc_EvalInnerProduct_docs = R"doc(
+    Evaluates inner product in batched encoding
+
+    Parameters:
+    ----------
+        ciphertext1 (Ciphertext): first vector
+        ciphertext2 (Ciphertext): second vector
+        batchSize (int): size of the batch to be summed up
+
+    Returns:
+    ----------
+        Ciphertext: resulting ciphertext
+)doc";
+
+//EvalInnerProduct(cipher,plain,batchsize)
+const char* cc_EvalInnerProductPlaintext_docs = R"doc(
+    Evaluates inner product in batched encoding
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): first vector - ciphertext
+        plaintext (Plaintext): second vector - plaintext
+        batchSize (int): size of the batch to be summed up
+
+    Returns:
+    ----------
+        Ciphertext: resulting ciphertext
+)doc";
+
+//Ciphertext<Element> EvalMerge(const std::vector<Ciphertext<Element>> &ciphertextVec) const
+const char* cc_EvalMerge_docs = R"doc(
+    Merges multiple ciphertexts with encrypted results in slot 0 into a single ciphertext The slot assignment is done based on the order of ciphertexts in the vector
+
+    Parameters:
+    ----------
+        ciphertextVec (list): vector of ciphertexts to be merged.
+
+    Returns:
+    ----------
+        Ciphertext: resulting ciphertext
+)doc";
+
+//inline virtual Ciphertext<Element> EvalPoly(ConstCiphertext<Element> ciphertext, const std::vector<double> &coefficients) const
+const char* cc_EvalPoly_docs = R"doc(
+    Method for polynomial evaluation for polynomials represented as power series.
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): input ciphertext
+        coefficients (list): is the vector of coefficients in the polynomial; the size of the vector is the degree of the polynomial + 1
+
+    Returns:
+    ----------
+        Ciphertext: the result of polynomial evaluation.
+)doc";
+
+//inline Ciphertext<Element> EvalPolyLinear(ConstCiphertext<Element> ciphertext, const std::vector<double> &coefficients)
+const char* cc_EvalPolyLinear_docs = R"doc(
+    Method for polynomial evaluation for polynomials represented in the power series. This uses EvalPolyLinear, which uses a binary tree computation of the polynomial powers.
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): input ciphertext
+        coefficients (list): is the vector of coefficients in the polynomial; the size of the vector is the degree of the polynomial
+
+    Returns:
+    ----------
+        Ciphertext: the result of polynomial evaluation.
+)doc";
+
+//inline Ciphertext<Element> EvalPolyPS(ConstCiphertext<Element> ciphertext, const std::vector<double> &coefficients) const
+const char* cc_EvalPolyPS_docs = R"doc(
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): input ciphertext
+        coefficients (list): is the vector of coefficients in the polynomial; the size of the vector is the degree of the polynomial
+
+    Returns:
+    ----------
+        Ciphertext: the result of polynomial evaluation.
+)doc";
+
+//Rescale(cipher)
+const char* cc_Rescale_docs = R"doc(
+    Rescale - An alias for OpenFHE ModReduce method. This is because ModReduce is called Rescale in CKKS.
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): ciphertext
+
+    Returns:
+    ----------
+        Ciphertext: mod reduced ciphertext
+)doc";
+
+//void EvalBootstrapSetup(std::vector<uint32_t> levelBudget = {5, 4}, std::vector<uint32_t> dim1 = {0, 0}, uint32_t slots = 0, uint32_t correctionFactor = 0)
+const char* cc_EvalBootstrapSetup_docs = R"doc(
+    Bootstrap functionality: There are three methods that have to be called in this specific order:
+
+    1. EvalBootstrapSetup: computes and encodes the coefficients for encoding and decoding and stores the necessary parameters
+
+    2. EvalBootstrapKeyGen: computes and stores the keys for rotations and conjugation
+
+    3. EvalBootstrap: refreshes the given ciphertext Sets all parameters for the linear method for the FFT-like method
+
+    Parameters:
+    ----------
+        levelBudget (list):  vector of budgets for the amount of levels in encoding and decoding
+        dim1 (list): vector of inner dimension in the baby-step giant-step routine for encoding and decodingl
+        slots (int): number of slots to be bootstraped
+        correctionFactor (int): alue to rescale message by to improve precision. If set to 0, we use the default logic. This value is only used when get_native_int()=64
+
+    Returns:
+    ----------
+        None
+)doc";
+
+//void EvalBootstrapKeyGen(const PrivateKey<Element> privateKey, uint32_t slots)
+const char* cc_EvalBootstrapKeyGen_docs = R"doc(
+    Generates all automorphism keys for EvalBT. EvalBootstrapKeyGen uses the baby-step/giant-step strategy.
+
+    Parameters:
+    ----------
+        privateKey (PrivateKey): private key.
+        slots (int): number of slots to support permutations on.
+
+    Returns:
+    ----------
+        None
+)doc";
+
+//Ciphertext<Element> EvalBootstrap(ConstCiphertext<Element> ciphertext, uint32_t numIterations = 1, uint32_t precision = 0)
+const char* cc_EvalBootstrap_docs = R"doc(
+    Defines the bootstrapping evaluation of ciphertext using either the FFT-like method or the linear method
+
+    Parameters:
+    ----------
+        ciphertext (Ciphertext): the input ciphertext
+        numIterations (int): number of iterations to run iterative bootstrapping (Meta-BTS). Increasing the iterations increases the precision of bootstrapping
+        precision (int): precision of initial bootstrapping algorithm. This value is determined by the user experimentally by first running EvalBootstrap with numIterations = 1 and precision = 0 (unused).
+
+    Returns:
+    ----------
+        Ciphertext: the refreshed ciphertext
+)doc";
+
+//inline std::shared_ptr<std::map<usint, EvalKey<Element>>> EvalAutomorphismKeyGen(const PrivateKey<Element> privateKey, const std::vector<usint> &indexLis
+const char* cc_EvalAutomorphismKeyGen_docs = R"doc(
+    Generate automophism keys for a given private key; Uses the private key for encryption
+
+    Parameters:
+    ----------
+        privateKey (PrivateKey): private key.
+        indexList (list): list of automorphism indices to be computed.
+
+    Returns:
+    ----------
+        dict: returns the evaluation key
+)doc";
+
+//inline std::shared_ptr<std::map<usint, EvalKey<Element>>> EvalAutomorphismKeyGen(const PublicKey<Element> publicKey, const PrivateKey<Element> privateKey, const std::vector<usint> &indexList) const
+const char* cc_EvalAutomorphismKeyGenPublic_docs = R"doc(
+    Generate automophism keys for a given private key.
+
+    Parameters:
+    ----------
+        publicKey (PublicKey): original public key.
+        privateKey (PrivateKey): original private key.
+        indexList (list): list of automorphism indices to be computed.
+
+    Returns:
+    ----------
+        dict: returns the evaluation keys; index 0 of the vector corresponds to plaintext index 2, index 1 to plaintex index 3, etc.
+)doc";
+
+//inline usint FindAutomorphismIndex(const usint idx) const
+const char* cc_FindAutomorphismIndex_docs = R"doc(
+    Find the automorphism index for a given plaintext index
+
+    Parameters:
+    ----------
+        idx (int): plaintext index
+
+    Returns:
+    ----------
+        int: automorphism index
+)doc";
+
+//inline std::vector<usint> FindAutomorphismIndices(const std::vector<usint> idxList) const
+const char* cc_FindAutomorphismIndices_docs = R"doc(
+    Find the automorphism indices for a given list of plaintext indices
+
+    Parameters:
+    ----------
+        idxList (list): list of plaintext indices
+
+    Returns:
+    ----------
+        list: list of automorphism indices
+)doc";
+
+//ClearEvalMultKeys()
+const char* cc_ClearEvalMultKeys_docs = R"doc(
+    ClearEvalMultKeys - flush EvalMultKey cache
+)doc";
+
+//ClearEvalAutomorphismKeys()
+const char* cc_ClearEvalAutomorphismKeys_docs = R"doc(
+    ClearEvalAutomorphismKeys - flush EvalAutomorphismKey cache
+)doc";
+
+//static inline bool SerializeEvalAutomorphismKey(std::ostream &ser, const ST &sertype, std::string id = "")
+const char* cc_SerializeEvalAutomorphismKey_docs = R"doc(
+    SerializeEvalAutomorphismKey for a single EvalAuto key or all of the EvalAuto keys
+
+    Parameters:
+    ----------
+        filename (str): output file
+        sertype (SERJSON, SERBINARY): serialization type
+        id (str): key to serialize; empty string means all keys
+
+    Returns:
+    ----------
+        bool: true on success
+)doc";
+
+//SerializeEvalMultKey(filename,sertype,id)
+const char* cc_SerializeEvalMultKey_docs = R"doc(
+    SerializeEvalMultKey for a single EvalMult key or all of the EvalMult keys
+
+    Parameters:
+    ----------
+        filename (str): output file
+        sertype (SERJSON, SERBINARY): type of serialization
+        id (str): for key to serialize - if empty string, serialize them all
+
+    Returns:
+    ----------
+        bool: true on success
+)doc";
+
+//DeserializeEvalAutomorphismKey(filename,sertype)
+const char* cc_DeserializeEvalAutomorphismKey_docs = R"doc(
+    DeserializeEvalAutomorphismKey deserialize all keys in the serialization deserialized keys silently replace any existing matching keys deserialization will create CryptoContext if necessary
+
+    Parameters:
+    ----------
+        filename (str): path for the file to deserialize from
+        sertype (SERJSON, SERBINARY): type of serialization
+
+    Returns:
+    ----------
+        bool: true on success
+)doc";
+
+//DeserializeEvalMultKey(filename,sertype)
+const char* cc_DeserializeEvalMultKey_docs = R"doc(
+    DeserializeEvalMultKey deserialize all keys in the serialization deserialized keys silently replace any existing matching keys deserialization will create CryptoContext if necessary
+
+    Parameters:
+    ----------
+        filename (str): path for the file to deserialize from
+        sertype (SERJSON, SERBINARY): type of serialization
+
+    Returns:
+    ----------
+        bool: true on success
+)doc";
+
+
+#endif //CRYPTOCONTEXT_DOCSTRINGS_H

+ 38 - 0
include/docstrings/cryptoparameters_docs.h

@@ -0,0 +1,38 @@
+#ifndef CRYPTOPARAMS_DOCSTRINGS_H
+#define CRYPTOPARAMS_DOCSTRINGS_H
+
+const char* ccparams_doc = R"doc(
+    Crypto parameters for the BFV, BGV and CKKS scheme.
+
+    :ivar SCHEME scheme: Scheme ID
+    :ivar PlaintextModulus ptModulus: PlaintextModulus ptModulus is used in BGV/BFV type schemes and impacts noise growth
+    :ivar int digitSize: digitSize is used in BV Key Switching only (KeySwitchTechnique = BV) and impacts noise growth
+    :ivar float standardDeviation: standardDeviation is used for Gaussian error generation
+    :ivar SecretKeyDist secretKeyDist: Secret key distribution: GAUSSIAN, UNIFORM_TERNARY, etc.
+    :ivar int maxRelinSkDeg: Max relinearization degree of secret key polynomial (used for lazy relinearization)
+    :ivar KeySwitchTechnique ksTech: key switching technique: BV or HYBRID currently
+    :ivar ScalingTechnique scalTech: rescaling/modulus switching technique used in CKKS/BGV: FLEXIBLEAUTOEXT, FIXEDMANUL, FLEXIBLEAUTO, etc.
+    :ivar int batchSize: max batch size of messages to be packed in encoding (number of slots)
+    :ivar ProxyReEncryptionMode PREMode: PRE security mode
+    :ivar MultipartyMode multipartyMode: Multiparty security mode in BFV/BGV
+    :ivar ExecutionMode executionMode: Execution mode in CKKS
+    :ivar DecryptionNoiseMode decryptionNoiseMode: Decryption noise mode in CKKS
+    :ivar float noiseEstimate: Noise estimate in CKKS for NOISE_FLOODING_DECRYPT mode.
+    :ivar float desiredPrecision: Desired precision for 128-bit CKKS. We use this value in NOISE_FLOODING_DECRYPT mode to determine the scaling factor.
+    :ivar float statisticalSecurity: Statistical security of CKKS in NOISE_FLOODING_DECRYPT mode. This is the bound on the probability of success that any adversary can have. Specifically, they a probability of success of at most 2^(-statisticalSecurity).
+    :ivar float numAdversarialQueries: This is the number of adversarial queries a user is expecting for their application, which we use to ensure security of CKKS in NOISE_FLOODING_DECRYPT mode.
+    :ivar int thresholdNumOfParties: This is the number of parties in a threshold application, which is used for bound on the joint secret key
+    :ivar int firstModSize: firstModSize and scalingModSize are used to calculate ciphertext modulus. The ciphertext modulus should be seen as: Q = q_0 * q_1 * ... * q_n * q' where q_0 is first prime, and it's number of bits is firstModSize other q_i have same number of bits and is equal to scalingModSize the prime q' is not explicitly given, but it is used internally in CKKS and BGV schemes (in *EXT scaling methods)
+    :ivar int scalingModSize: firstModSize and scalingModSize are used to calculate ciphertext modulus. The ciphertext modulus should be seen as: Q = q_0 * q_1 * ... * q_n * q' where q_0 is first prime, and it's number of bits is firstModSize other q_i have same number of bits and is equal to scalingModSize the prime q' is not explicitly given, but it is used internally in CKKS and BGV schemes (in *EXT scaling methods)
+    :ivar int numLargeDigits: see KeySwitchTechnique - number of digits in HYBRID key switching
+    :ivar int multiplicativeDepth: multiplicative depth
+    :ivar SecurityLevel securityLevel: security level: We use the values from the security standard  at http://homomorphicencryption.org/wp-content/uploads/2018/11/HomomorphicEncryptionStandardv1.1.pdf For given ring dimension and security level we have upper bound of possible highest modulus (Q for BV or P*Q for HYBRID)
+    :ivar int ringDim: ring dimension N of the scheme : the ring is Z_Q[x] / (X^N+1)
+    :ivar int evalAddCount: number of additions (used for setting noise in BGV and BFV)
+    :ivar int keySwitchCount: number of key switching operations (used for setting noise in BGV and BFV)
+    :ivar int multiHopModSize: size of moduli used for PRE in the provable HRA setting
+    :ivar EncryptionTechnique encryptionTechnique: STANDARD or EXTENDED mode for BFV encryption
+    :ivar MultiplicationTechnique multiplicationTechnique: multiplication method in BFV: BEHZ, HPS, etc.
+)doc";
+
+#endif // CRYPTOPARAMS_DOCSTRINGS_H

+ 84 - 0
include/docstrings/plaintext_docs.h

@@ -0,0 +1,84 @@
+#ifndef PLAINTEXT_DOCSTRINGS_H
+#define PLAINTEXT_DOCSTRINGS_H
+
+const char* ptx_GetScalingFactor_docs = R"doc(
+    Get the scaling factor of the plaintext for CKKS-based plaintexts.
+
+    Returns
+    -------
+        float: The scaling factor of the plaintext.
+)doc";
+
+const char* ptx_SetScalingFactor_docs = R"doc(
+    Set the scaling factor of the plaintext for CKKS-based plaintexts.
+
+    Parameters
+    ----------
+        sf (float): The scaling factor to set.
+)doc";
+
+const char* ptx_GetLength_docs = R"doc(
+    Get method to return the length of the plaintext.
+
+    Returns
+    -------
+        int: The length of the plaintext in terms of the number of bits.
+)doc";
+
+const char* ptx_GetSchemeID_docs = R"doc(
+    Get the encryption technique of the plaintext for BFV-based plaintexts.
+
+    Returns
+    -------
+        SCHEME: The scheme ID of the plaintext.
+)doc";
+
+const char* ptx_SetLength_docs = R"doc(
+    resize the plaintext; only works for plaintexts that support a resizable vector (coefpacked)
+
+    Parameters
+    ----------
+        newSize (int): -
+)doc";
+
+const char* ptx_IsEncoded_docs = R"doc(
+    Check if the plaintext is encoded.
+
+    Returns
+    -------
+        bool: True if the plaintext is encoded, False otherwise.
+)doc";
+
+const char* ptx_GetLogPrecision_docs = R"doc(
+    Get the log of the plaintext precision.
+
+    Returns
+    -------
+        float: The log of the plaintext precision.
+)doc";
+
+const char* ptx_Encode_docs = R"doc(
+    Encode the plaintext into a polynomial
+
+    Returns
+    -------
+        None
+)doc";
+
+const char* ptx_Decode_docs = R"doc(
+    Decode the polynomial into a plaintext
+
+    Returns
+    -------
+        None
+)doc";
+
+const char* ptx_GetCKKSPackedValue_docs = R"doc(
+    Get the packed value of the plaintext for CKKS-based plaintexts.
+
+    Returns
+    -------
+        List[complex]: The packed value of the plaintext.
+)doc";
+
+#endif // PLAINTEXT_DOCSTRINGS_H

+ 33 - 0
include/pke/cryptocontext_wrapper.h

@@ -0,0 +1,33 @@
+#ifndef OPENFHE_CRYPTOCONTEXT_BINDINGS_H
+#define OPENFHE_CRYPTOCONTEXT_BINDINGS_H
+
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+#include <vector>
+#include <algorithm>
+#include <complex>
+#include "openfhe.h"
+#include "bindings.h"
+
+namespace py = pybind11;
+using namespace lbcrypto;
+using ParmType = typename DCRTPoly::Params;
+
+Ciphertext<DCRTPoly> EvalFastRotationPrecomputeWrapper(CryptoContext<DCRTPoly>& self,
+                                                        ConstCiphertext<DCRTPoly> ciphertext);
+
+Ciphertext<DCRTPoly> EvalFastRotationWrapper(CryptoContext<DCRTPoly>& self,
+                                            ConstCiphertext<DCRTPoly> ciphertext,
+                                              const usint index,
+                                              const usint m,
+                                              ConstCiphertext<DCRTPoly> digits);
+Ciphertext<DCRTPoly> EvalFastRotationExtWrapper(CryptoContext<DCRTPoly>& self,ConstCiphertext<DCRTPoly> ciphertext, const usint index, ConstCiphertext<DCRTPoly> digits, bool addFirst);
+
+Plaintext DecryptWrapper(CryptoContext<DCRTPoly>& self,
+ConstCiphertext<DCRTPoly> ciphertext,const PrivateKey<DCRTPoly> privateKey);
+Plaintext DecryptWrapper(CryptoContext<DCRTPoly>& self,
+const PrivateKey<DCRTPoly> privateKey,ConstCiphertext<DCRTPoly> ciphertext);
+
+const std::map<usint, EvalKey<DCRTPoly>> EvalAutomorphismKeyGenWrapper(CryptoContext<DCRTPoly>& self,const PrivateKey<DCRTPoly> privateKey,const std::vector<usint> &indexList);
+const std::map<usint, EvalKey<DCRTPoly>> EvalAutomorphismKeyGenWrapper_PublicKey(CryptoContext<DCRTPoly>& self,const PublicKey<DCRTPoly> publicKey, const PrivateKey<DCRTPoly> privateKey, const std::vector<usint> &indexList);
+#endif // OPENFHE_CRYPTOCONTEXT_BINDINGS_H

+ 10 - 0
include/pke/serialization.h

@@ -0,0 +1,10 @@
+#ifndef OPENFHE_SERIALIZATION_BINDINGS_H
+#define OPENFHE_SERIALIZATION_BINDINGS_H
+
+#include <pybind11/pybind11.h>
+using namespace lbcrypto;
+
+template <typename ST>
+bool SerializeEvalMultKeyWrapper(const std::string& filename, const ST& sertype, std::string id);
+
+#endif // OPENFHE_SERIALIZATION_BINDINGS_H

+ 820 - 0
src/bindings.cpp

@@ -0,0 +1,820 @@
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+#include <pybind11/stl_bind.h>
+#include <pybind11/complex.h>
+#include <pybind11/functional.h>
+#include <pybind11/operators.h>
+#include <pybind11/iostream.h>
+#include <iostream>
+#include <map>
+#include "openfhe.h"
+#include "key/key-ser.h"
+#include "bindings.h"
+#include "cryptocontext_wrapper.h"
+#include "binfhe_bindings.h"
+#include "cryptocontext_docs.h"
+#include "plaintext_docs.h"
+#include "ciphertext_docs.h"
+
+using namespace lbcrypto;
+namespace py = pybind11;
+PYBIND11_MAKE_OPAQUE(std::map<usint, EvalKey<DCRTPoly>>);
+
+template <typename T>
+void bind_parameters(py::module &m,const std::string name)
+{
+    py::class_<CCParams<T>>(m, name.c_str())
+        .def(py::init<>())
+        // getters
+        .def("GetPlaintextModulus", &CCParams<T>::GetPlaintextModulus)
+        .def("GetScheme", &CCParams<T>::GetScheme)
+        .def("GetDigitSize", &CCParams<T>::GetDigitSize)
+        .def("GetStandardDeviation", &CCParams<T>::GetStandardDeviation)
+        .def("GetSecretKeyDist", &CCParams<T>::GetSecretKeyDist)
+        .def("GetMaxRelinSkDeg", &CCParams<T>::GetMaxRelinSkDeg)
+        .def("GetPREMode", &CCParams<T>::GetPREMode)
+        .def("GetMultipartyMode", &CCParams<T>::GetMultipartyMode)
+        .def("GetExecutionMode", &CCParams<T>::GetExecutionMode)
+        .def("GetDecryptionNoiseMode", &CCParams<T>::GetDecryptionNoiseMode)
+        .def("GetNoiseEstimate", &CCParams<T>::GetNoiseEstimate)
+        .def("GetDesiredPrecision", &CCParams<T>::GetDesiredPrecision)
+        .def("GetStatisticalSecurity", &CCParams<T>::GetStatisticalSecurity)
+        .def("GetNumAdversarialQueries", &CCParams<T>::GetNumAdversarialQueries)
+        //.def("GetThresholdNumOfParties", &CCParams<T>::GetThresholdNumOfParties)
+        .def("GetKeySwitchTechnique", &CCParams<T>::GetKeySwitchTechnique)
+        .def("GetScalingTechnique", &CCParams<T>::GetScalingTechnique)
+        .def("GetBatchSize", &CCParams<T>::GetBatchSize)
+        .def("GetFirstModSize", &CCParams<T>::GetFirstModSize)
+        .def("GetNumLargeDigits", &CCParams<T>::GetNumLargeDigits)
+        .def("GetMultiplicativeDepth", &CCParams<T>::GetMultiplicativeDepth)
+        .def("GetScalingModSize", &CCParams<T>::GetScalingModSize)
+        .def("GetSecurityLevel", &CCParams<T>::GetSecurityLevel)
+        .def("GetRingDim", &CCParams<T>::GetRingDim)
+        .def("GetEvalAddCount", &CCParams<T>::GetEvalAddCount)
+        .def("GetKeySwitchCount", &CCParams<T>::GetKeySwitchCount)
+        .def("GetEncryptionTechnique", &CCParams<T>::GetEncryptionTechnique)
+        .def("GetMultiplicationTechnique", &CCParams<T>::GetMultiplicationTechnique)
+        .def("GetMultiHopModSize", &CCParams<T>::GetMultiHopModSize)
+        // setters
+        .def("SetPlaintextModulus", &CCParams<T>::SetPlaintextModulus)
+        .def("SetDigitSize", &CCParams<T>::SetDigitSize)
+        .def("SetStandardDeviation", &CCParams<T>::SetStandardDeviation)
+        .def("SetSecretKeyDist", &CCParams<T>::SetSecretKeyDist)
+        .def("SetMaxRelinSkDeg", &CCParams<T>::SetMaxRelinSkDeg)
+        .def("SetPREMode", &CCParams<T>::SetPREMode)
+        .def("SetMultipartyMode", &CCParams<T>::SetMultipartyMode)
+        .def("SetExecutionMode", &CCParams<T>::SetExecutionMode)
+        .def("SetDecryptionNoiseMode", &CCParams<T>::SetDecryptionNoiseMode)
+        .def("SetNoiseEstimate", &CCParams<T>::SetNoiseEstimate)
+        .def("SetDesiredPrecision", &CCParams<T>::SetDesiredPrecision)
+        .def("SetStatisticalSecurity", &CCParams<T>::SetStatisticalSecurity)
+        .def("SetNumAdversarialQueries", &CCParams<T>::SetNumAdversarialQueries)
+        //.def("SetThresholdNumOfParties", &CCParams<T>::SetThresholdNumOfParties)
+        .def("SetKeySwitchTechnique", &CCParams<T>::SetKeySwitchTechnique)
+        .def("SetScalingTechnique", &CCParams<T>::SetScalingTechnique)
+        .def("SetBatchSize", &CCParams<T>::SetBatchSize)
+        .def("SetFirstModSize", &CCParams<T>::SetFirstModSize)
+        .def("SetNumLargeDigits", &CCParams<T>::SetNumLargeDigits)
+        .def("SetMultiplicativeDepth", &CCParams<T>::SetMultiplicativeDepth)
+        .def("SetScalingModSize", &CCParams<T>::SetScalingModSize)
+        .def("SetSecurityLevel", &CCParams<T>::SetSecurityLevel)
+        .def("SetRingDim", &CCParams<T>::SetRingDim)
+        .def("SetEvalAddCount", &CCParams<T>::SetEvalAddCount)
+        .def("SetKeySwitchCount", &CCParams<T>::SetKeySwitchCount)
+        .def("SetEncryptionTechnique", &CCParams<T>::SetEncryptionTechnique)
+        .def("SetMultiplicationTechnique", &CCParams<T>::SetMultiplicationTechnique)
+        .def("SetMultiHopModSize", &CCParams<T>::SetMultiHopModSize)
+        .def("__str__",[](const CCParams<T> &params) {
+            std::stringstream stream;
+            stream << params;
+            return stream.str();
+        });
+
+        //
+
+}
+
+void bind_crypto_context(py::module &m)
+{
+    py::class_<CryptoContextImpl<DCRTPoly>, std::shared_ptr<CryptoContextImpl<DCRTPoly>>>(m, "CryptoContext")
+        .def(py::init<>())
+        .def("GetKeyGenLevel", &CryptoContextImpl<DCRTPoly>::GetKeyGenLevel, cc_GetKeyGenLevel_docs)
+        .def("SetKeyGenLevel", &CryptoContextImpl<DCRTPoly>::SetKeyGenLevel, cc_SetKeyGenLevel_docs,
+             py::arg("level"))
+        //.def("GetScheme",&CryptoContextImpl<DCRTPoly>::GetScheme)
+        //.def("GetCryptoParameters", &CryptoContextImpl<DCRTPoly>::GetCryptoParameters)
+        .def("GetRingDimension", &CryptoContextImpl<DCRTPoly>::GetRingDimension, cc_GetRingDimension_docs)
+        .def("Enable", static_cast<void (CryptoContextImpl<DCRTPoly>::*)(PKESchemeFeature)>(&CryptoContextImpl<DCRTPoly>::Enable), cc_Enable_docs,
+             py::arg("feature"))
+        .def("KeyGen", &CryptoContextImpl<DCRTPoly>::KeyGen, cc_KeyGen_docs)
+        .def("EvalMultKeyGen", &CryptoContextImpl<DCRTPoly>::EvalMultKeyGen,
+             cc_EvalMultKeyGen_docs,
+             py::arg("privateKey"))
+        .def("EvalMultKeysGen", &CryptoContextImpl<DCRTPoly>::EvalMultKeysGen,
+             cc_EvalMultKeysGen_docs,
+             py::arg("privateKey"))
+        .def("EvalRotateKeyGen", &CryptoContextImpl<DCRTPoly>::EvalRotateKeyGen,
+             cc_EvalRotateKeyGen_docs,
+             py::arg("privateKey"),
+             py::arg("indexList"),
+             py::arg("publicKey") = nullptr)
+        .def("MakeStringPlaintext", &CryptoContextImpl<DCRTPoly>::MakeStringPlaintext,
+             cc_MakeStringPlaintext_docs,
+             py::arg("str"))
+        .def("MakePackedPlaintext", &CryptoContextImpl<DCRTPoly>::MakePackedPlaintext,
+             cc_MakePackedPlaintext_docs,
+             py::arg("value"),
+             py::arg("depth") = 1,
+             py::arg("level") = 0)
+        .def("MakeCoefPackedPlaintext", &CryptoContextImpl<DCRTPoly>::MakeCoefPackedPlaintext,
+            cc_MakeCoefPackedPlaintext_docs,
+            py::arg("value"),
+            py::arg("depth") = 1,
+            py::arg("level") = 0)
+        // TODO (Oliveira): allow user to specify different params values
+        .def("MakeCKKSPackedPlaintext", static_cast<Plaintext (CryptoContextImpl<DCRTPoly>::*)(const std::vector<std::complex<double>> &, size_t, uint32_t, const std::shared_ptr<ParmType>, usint) const>(&CryptoContextImpl<DCRTPoly>::MakeCKKSPackedPlaintext), cc_MakeCKKSPackedPlaintextComplex_docs,
+             py::arg("value"),
+             py::arg("depth") = static_cast<size_t>(1),
+             py::arg("level") = static_cast<uint32_t>(0),
+             py::arg("params") = py::none(),
+             py::arg("slots") = 0)
+        .def("MakeCKKSPackedPlaintext", static_cast<Plaintext (CryptoContextImpl<DCRTPoly>::*)(const std::vector<double> &, size_t, uint32_t, const std::shared_ptr<ParmType>, usint) const>(&CryptoContextImpl<DCRTPoly>::MakeCKKSPackedPlaintext), cc_MakeCKKSPlaintextReal_docs,
+             py::arg("value"),
+             py::arg("depth") = static_cast<size_t>(1),
+             py::arg("level") = static_cast<uint32_t>(0),
+             py::arg("params") = py::none(),
+             py::arg("slots") = 0)
+        .def("EvalRotate", &CryptoContextImpl<DCRTPoly>::EvalRotate,
+            cc_EvalRotate_docs,
+            py::arg("ciphertext"),
+            py::arg("index"))
+        .def("EvalFastRotationPrecompute", &EvalFastRotationPrecomputeWrapper,
+            cc_EvalFastRotationPreCompute_docs,
+            py::arg("ciphertext"))
+        .def("EvalFastRotation", &EvalFastRotationWrapper,
+            cc_EvalFastRotation_docs,
+            py::arg("ciphertext"),
+            py::arg("index"),
+            py::arg("m"),
+            py::arg("digits"))
+        .def("EvalFastRotationExt", &EvalFastRotationExtWrapper, 
+            cc_EvalFastRotationExt_docs,
+            py::arg("ciphertext"),
+            py::arg("index"),
+            py::arg("digits"),
+            py::arg("addFirst"))
+        .def("EvalAtIndexKeyGen", &CryptoContextImpl<DCRTPoly>::EvalAtIndexKeyGen,
+            cc_EvalAtIndexKeyGen_docs,
+            py::arg("privateKey"),
+            py::arg("indexList"),
+            py::arg("publicKey") = nullptr)
+        .def("EvalAtIndex", &CryptoContextImpl<DCRTPoly>::EvalAtIndex,
+            cc_EvalAtIndex_docs,
+            py::arg("ciphertext"),
+            py::arg("index"))
+        .def("Encrypt", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(const PublicKey<DCRTPoly>, Plaintext) const>
+            (&CryptoContextImpl<DCRTPoly>::Encrypt),
+            cc_Encrypt_docs,
+            py::arg("publicKey"),
+            py::arg("plaintext"))
+        .def("Decrypt", static_cast<Plaintext (*)(CryptoContext<DCRTPoly> &, const PrivateKey<DCRTPoly>, ConstCiphertext<DCRTPoly>)>
+            (&DecryptWrapper), cc_Decrypt_docs,
+            py::arg("privateKey"),
+            py::arg("ciphertext"))
+        .def("Decrypt", static_cast<Plaintext (*)(CryptoContext<DCRTPoly> &, ConstCiphertext<DCRTPoly>, const PrivateKey<DCRTPoly>)>
+            (&DecryptWrapper), cc_Decrypt_docs,
+            py::arg("ciphertext"),
+            py::arg("privateKey"))
+        .def("EvalAdd", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(ConstCiphertext<DCRTPoly>, ConstCiphertext<DCRTPoly>) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalAdd), 
+            cc_EvalAdd_docs,
+            py::arg("ciphertext1"),
+            py::arg("ciphertext2"))
+        .def("EvalAdd", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(ConstCiphertext<DCRTPoly>, double) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalAdd), 
+            cc_EvalAddfloat_docs,
+            py::arg("ciphertext"),
+            py::arg("scalar"))
+        .def("EvalAddInPlace", static_cast<void (CryptoContextImpl<DCRTPoly>::*)(Ciphertext<DCRTPoly> &, ConstCiphertext<DCRTPoly>) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalAddInPlace),
+            cc_EvalAddInPlace_docs,
+            py::arg("ciphertext1"),
+            py::arg("ciphertext2"))
+        .def("EvalAddInPlace", static_cast<void (CryptoContextImpl<DCRTPoly>::*)(Ciphertext<DCRTPoly> &, ConstPlaintext) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalAddInPlace),
+            cc_EvalAddInPlacePlaintext_docs,
+            py::arg("ciphertext"),
+            py::arg("plaintext"))
+        .def("EvalAddInPlace", static_cast<void (CryptoContextImpl<DCRTPoly>::*)(ConstPlaintext, Ciphertext<DCRTPoly> &) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalAddInPlace),
+            "",
+            py::arg("plaintext"),
+            py::arg("ciphertext"))
+        .def("EvalAddMutable", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(Ciphertext<DCRTPoly> &, Ciphertext<DCRTPoly> &) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalAddMutable),
+            cc_EvalAddMutable_docs,
+            py::arg("ct1"),
+            py::arg("ct2"))
+        .def("EvalAddMutable", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(Ciphertext<DCRTPoly> &, Plaintext) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalAddMutable),
+            cc_EvalAddMutablePlaintext_docs,
+            py::arg("ciphertext"),
+            py::arg("plaintext"))
+        .def("EvalAddMutable", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(Plaintext, Ciphertext<DCRTPoly> &) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalAddMutable),
+            "",
+            py::arg("plaintext"),
+            py::arg("ciphertext"))
+        .def("EvalAddMutableInPlace", &CryptoContextImpl<DCRTPoly>::EvalAddMutableInPlace,
+            cc_EvalAddMutableInPlace_docs,
+            py::arg("ciphertext1"),
+            py::arg("ciphertext2"))
+        .def("EvalSub", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(ConstCiphertext<DCRTPoly>, ConstCiphertext<DCRTPoly>) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalSub),
+            cc_EvalSub_docs,
+            py::arg("ct1"),
+            py::arg("ct2"))
+        .def("EvalSub", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(ConstCiphertext<DCRTPoly>, double) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalSub),
+            cc_EvalSubfloat_docs,
+            py::arg("ciphertext"),
+            py::arg("constant"))
+        .def("EvalSub", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(double, ConstCiphertext<DCRTPoly>) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalSub),
+            "",
+            py::arg("constant"),
+            py::arg("ciphertext"))
+        .def("EvalSub", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(ConstCiphertext<DCRTPoly>, ConstPlaintext) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalSub),
+            cc_EvalSubPlaintext_docs,
+            py::arg("ciphertext"),
+            py::arg("plaintext"))
+        .def("EvalSub", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(ConstPlaintext, ConstCiphertext<DCRTPoly>) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalSub),
+            "",
+            py::arg("plaintext"),
+            py::arg("ciphertext"))
+        .def("EvalSubInPlace", static_cast<void (CryptoContextImpl<DCRTPoly>::*)(Ciphertext<DCRTPoly> &, ConstCiphertext<DCRTPoly>) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalSubInPlace),
+            cc_EvalSubInPlace_docs,
+            py::arg("ct1"),
+            py::arg("ct2"))
+        .def("EvalSubInPlace", static_cast<void (CryptoContextImpl<DCRTPoly>::*)(Ciphertext<DCRTPoly> &, double) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalSubInPlace),
+            cc_EvalSubInPlacefloat_docs,
+            py::arg("ciphertext"),
+            py::arg("constant"))
+        .def("EvalSubInPlace", static_cast<void (CryptoContextImpl<DCRTPoly>::*)(double, Ciphertext<DCRTPoly> &) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalSubInPlace),
+            "",
+            py::arg("constant"),
+            py::arg("ciphertext"))
+        .def("EvalSubMutable", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(Ciphertext<DCRTPoly> &, Ciphertext<DCRTPoly> &) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalSubMutable),
+            cc_EvalSubMutable_docs,
+            py::arg("ct1"),
+            py::arg("ct2"))
+        .def("EvalSubMutable", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(Ciphertext<DCRTPoly> &, Plaintext) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalSubMutable),
+            cc_EvalSubMutablePlaintext_docs,
+            py::arg("ciphertext"),
+            py::arg("plaintext"))
+        .def("EvalSubMutable", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(Plaintext, Ciphertext<DCRTPoly> &) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalSubMutable),
+            "",
+            py::arg("plaintext"),
+            py::arg("ciphertext"))
+        .def("EvalSubMutableInPlace", &CryptoContextImpl<DCRTPoly>::EvalSubMutableInPlace,
+            cc_EvalSubMutableInPlace_docs,
+            py::arg("ciphertext1"),
+            py::arg("ciphertext2"))
+        .def("EvalMult", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(ConstCiphertext<DCRTPoly>, ConstCiphertext<DCRTPoly>) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalMult),
+            cc_EvalMult_docs,
+            py::arg("ct1"),
+            py::arg("ct2"))
+        .def("EvalMult", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(ConstCiphertext<DCRTPoly>, double) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalMult),
+            cc_EvalMultfloat_docs,
+            py::arg("ciphertext"),
+            py::arg("constant"))
+        .def("EvalMult", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(ConstCiphertext<DCRTPoly>, ConstPlaintext) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalMult),
+            cc_EvalMultPlaintext_docs,
+            py::arg("ciphertext"),
+            py::arg("plaintext"))
+        .def("EvalMult", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(ConstPlaintext, ConstCiphertext<DCRTPoly>) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalMult),
+            "",
+            py::arg("plaintext"),
+            py::arg("ciphertext"))
+        .def("EvalMult", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(double, ConstCiphertext<DCRTPoly>) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalMult),
+            "",
+            py::arg("constant"),
+            py::arg("ciphertext"))
+        .def("EvalMultMutable", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(Ciphertext<DCRTPoly> &, Ciphertext<DCRTPoly> &) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalMultMutable),
+            cc_EvalMultMutable_docs,
+            py::arg("ct1"),
+            py::arg("ct2"))
+        .def("EvalMultMutable", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(Ciphertext<DCRTPoly> &, Plaintext) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalMultMutable),
+            cc_EvalMultMutablePlaintext_docs,
+            py::arg("ciphertext"),
+            py::arg("plaintext"))
+        .def("EvalMultMutable", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(Plaintext, Ciphertext<DCRTPoly> &) const>
+            (&CryptoContextImpl<DCRTPoly>::EvalMultMutable),
+            "",
+            py::arg("plaintext"),
+            py::arg("ciphertext"))
+        .def("EvalMultMutableInPlace", &CryptoContextImpl<DCRTPoly>::EvalMultMutableInPlace,
+            cc_EvalMultMutableInPlace_docs,
+            py::arg("ct1"),
+            py::arg("ct2"))
+        .def("EvalSquare", &CryptoContextImpl<DCRTPoly>::EvalSquare,
+            cc_EvalSquare_docs,
+            py::arg("ct"))
+        .def("EvalSquareMutable", &CryptoContextImpl<DCRTPoly>::EvalSquareMutable,
+            cc_EvalSquareMutable_docs,
+            py::arg("ct"))
+        .def("EvalSquareInPlace", &CryptoContextImpl<DCRTPoly>::EvalSquareInPlace,
+            cc_EvalSquareInPlace_docs,
+            py::arg("ct"))
+        .def("EvalMultNoRelin", &CryptoContextImpl<DCRTPoly>::EvalMultNoRelin,
+            cc_EvalMultNoRelin_docs,
+            py::arg("ct1"),
+            py::arg("ct2"))
+        .def("Relinearize", &CryptoContextImpl<DCRTPoly>::Relinearize,
+            cc_Relinearize_docs,
+            py::arg("ciphertext"))
+        .def("RelinearizeInPlace", &CryptoContextImpl<DCRTPoly>::RelinearizeInPlace,
+            cc_RelinearizeInPlace_docs,
+            py::arg("ciphertext"))
+        .def("EvalMultAndRelinearize", &CryptoContextImpl<DCRTPoly>::EvalMultAndRelinearize,
+            cc_EvalMultAndRelinearize_docs,
+            py::arg("ct1"),
+            py::arg("ct2"))
+        .def("EvalNegate", &CryptoContextImpl<DCRTPoly>::EvalNegate,
+            cc_EvalNegate_docs,
+            py::arg("ct"))
+        .def("EvalNegateInPlace", &CryptoContextImpl<DCRTPoly>::EvalNegateInPlace,
+            cc_EvalNegateInPlace_docs,
+            py::arg("ct"))
+        .def("EvalLogistic", &CryptoContextImpl<DCRTPoly>::EvalLogistic,
+            cc_EvalLogistic_docs,
+            py::arg("ciphertext"),
+            py::arg("a"),
+            py::arg("b"),
+            py::arg("degree"))
+        .def("EvalChebyshevSeries", &CryptoContextImpl<DCRTPoly>::EvalChebyshevSeries,
+            cc_EvalChebyshevSeries_docs,
+            py::arg("ciphertext"),
+            py::arg("coefficients"),
+            py::arg("a"),
+            py::arg("b"))
+        .def("EvalChebyshevSeriesLinear", &CryptoContextImpl<DCRTPoly>::EvalChebyshevSeriesLinear,
+            cc_EvalChebyshevSeriesLinear_docs,
+            py::arg("ciphertext"),
+            py::arg("coefficients"),
+            py::arg("a"),
+            py::arg("b"))
+        .def("EvalChebyshevSeriesPS", &CryptoContextImpl<DCRTPoly>::EvalChebyshevSeriesPS,
+            cc_EvalChebyshevSeriesPS_docs,
+            py::arg("ciphertext"),
+            py::arg("coefficients"),
+            py::arg("a"),
+            py::arg("b"))
+        .def("EvalChebyshevFunction", &CryptoContextImpl<DCRTPoly>::EvalChebyshevFunction,
+            cc_EvalChebyshevFunction_docs,
+             py::arg("func"),
+             py::arg("ciphertext"),
+             py::arg("a"),
+             py::arg("b"),
+             py::arg("degree"))
+        .def("EvalSin", &CryptoContextImpl<DCRTPoly>::EvalSin,
+             cc_EvalSin_docs,
+             py::arg("ciphertext"),
+             py::arg("a"),
+             py::arg("b"),
+             py::arg("degree"))
+        .def("EvalCos", &CryptoContextImpl<DCRTPoly>::EvalCos,
+             cc_EvalCos_docs,
+             py::arg("ciphertext"),
+             py::arg("a"),
+             py::arg("b"),
+             py::arg("degree"))
+        .def("EvalDivide", &CryptoContextImpl<DCRTPoly>::EvalDivide,
+             cc_EvalDivide_docs,
+             py::arg("ciphertext"),
+             py::arg("a"),
+             py::arg("b"),
+             py::arg("degree"))
+        .def("EvalSumKeyGen", &CryptoContextImpl<DCRTPoly>::EvalSumKeyGen,
+             cc_EvalSumKeyGen_docs,
+             py::arg("privateKey"),
+             py::arg("publicKey") = py::none())
+        //TODO (Oliveira, R.): Solve pointer handling bug when dealing with EvalKeyMap object for the next functions 
+        .def("EvalSumRowsKeyGen", &CryptoContextImpl<DCRTPoly>::EvalSumRowsKeyGen,
+             cc_EvalSumRowsKeyGen_docs,
+             py::arg("privateKey"),
+             py::arg("publicKey") = py::none(),
+             py::arg("rowSize") = 0,
+             py::arg("subringDim") = 0)
+        .def("EvalSumColsKeyGen", &CryptoContextImpl<DCRTPoly>::EvalSumColsKeyGen,
+             cc_EvalSumColsKeyGen_docs,
+             py::arg("privateKey"),
+             py::arg("publicKey") = py::none())
+        .def("EvalSumRows", &CryptoContextImpl<DCRTPoly>::EvalSumRows,
+             cc_EvalSumRows_docs,
+             py::arg("ciphertext"),
+             py::arg("rowSize"),
+             py::arg("evalSumKeyMap"),
+             py::arg("subringDim") = 0)
+        .def("EvalSumCols", &CryptoContextImpl<DCRTPoly>::EvalSumCols,
+             cc_EvalSumCols_docs,
+             py::arg("ciphertext"),
+             py::arg("rowSize"),
+             py::arg("evalSumKeyMap"))
+        .def("EvalInnerProduct", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(ConstCiphertext<DCRTPoly>, ConstCiphertext<DCRTPoly>, usint) const>(&CryptoContextImpl<DCRTPoly>::EvalInnerProduct),
+             cc_EvalInnerProduct_docs,
+             py::arg("ciphertext1"),
+             py::arg("ciphertext2"),
+             py::arg("batchSize"))
+        .def("EvalInnerProduct", static_cast<Ciphertext<DCRTPoly> (CryptoContextImpl<DCRTPoly>::*)(ConstCiphertext<DCRTPoly>, ConstPlaintext, usint) const>(&CryptoContextImpl<DCRTPoly>::EvalInnerProduct),
+             cc_EvalInnerProductPlaintext_docs,
+             py::arg("ciphertext"),
+             py::arg("plaintext"),
+             py::arg("batchSize"))
+        .def("EvalMerge", &CryptoContextImpl<DCRTPoly>::EvalMerge,
+             cc_EvalMerge_docs,
+             py::arg("ciphertextVec"))
+        .def("EvalPoly", &CryptoContextImpl<DCRTPoly>::EvalPoly,
+             cc_EvalPoly_docs,
+             py::arg("ciphertext"),
+             py::arg("coefficients"))
+        .def("EvalPolyLinear", &CryptoContextImpl<DCRTPoly>::EvalPolyLinear,
+             cc_EvalPolyLinear_docs,
+             py::arg("ciphertext"),
+             py::arg("coefficients"))
+        .def("EvalPolyPS", &CryptoContextImpl<DCRTPoly>::EvalPolyPS,
+             cc_EvalPolyPS_docs,
+             py::arg("ciphertext"),
+             py::arg("coefficients"))
+        .def("Rescale", &CryptoContextImpl<DCRTPoly>::Rescale,
+             cc_Rescale_docs,
+             py::arg("ciphertext"))
+        .def("EvalBootstrapSetup", &CryptoContextImpl<DCRTPoly>::EvalBootstrapSetup,
+             cc_EvalBootstrapSetup_docs,
+             py::arg("levelBudget") = std::vector<uint32_t>({5, 4}),
+             py::arg("dim1") = std::vector<uint32_t>({0, 0}),
+             py::arg("slots") = 0,
+             py::arg("correctionFactor") = 0)
+        .def("EvalBootstrapKeyGen", &CryptoContextImpl<DCRTPoly>::EvalBootstrapKeyGen,
+             cc_EvalBootstrapKeyGen_docs,
+             py::arg("privateKey"),
+             py::arg("slots"))
+        .def("EvalBootstrap", &CryptoContextImpl<DCRTPoly>::EvalBootstrap,
+             cc_EvalBootstrap_docs,
+             py::arg("ciphertext"),
+             py::arg("numIterations") = 1,
+             py::arg("precision") = 0)
+        //TODO (Oliveira, R.): Solve pointer handling bug when returning EvalKeyMap objects for the next functions
+        .def("EvalAutomorphismKeyGen", &EvalAutomorphismKeyGenWrapper, 
+            cc_EvalAutomorphismKeyGen_docs,
+            py::arg("privateKey"),
+            py::arg("indexList"),
+            py::return_value_policy::reference_internal)
+        .def("EvalAutomorphismKeyGen", &EvalAutomorphismKeyGenWrapper_PublicKey, 
+            cc_EvalAutomorphismKeyGenPublic_docs,
+            py::arg("publicKey"),
+            py::arg("privateKey"),
+            py::arg("indexList"),
+            py::return_value_policy::reference_internal)
+        .def("FindAutomorphismIndex", &CryptoContextImpl<DCRTPoly>::FindAutomorphismIndex,
+            cc_FindAutomorphismIndex_docs,
+            py::arg("idx"))
+        .def("FindAutomorphismIndices", &CryptoContextImpl<DCRTPoly>::FindAutomorphismIndices,
+            cc_FindAutomorphismIndices_docs,
+            py::arg("idxList"))
+        .def_static(
+            "ClearEvalMultKeys", []()
+            { CryptoContextImpl<DCRTPoly>::ClearEvalMultKeys(); },
+            cc_ClearEvalMultKeys_docs)
+        .def_static(
+            "ClearEvalAutomorphismKeys", []()
+            { CryptoContextImpl<DCRTPoly>::ClearEvalAutomorphismKeys(); },
+            cc_ClearEvalAutomorphismKeys_docs)
+        .def_static(
+            "SerializeEvalMultKey", [](const std::string &filename, const SerType::SERBINARY &sertype, std::string id = "")
+            {
+                std::ofstream outfile(filename,std::ios::out | std::ios::binary);
+                bool res;
+                res = CryptoContextImpl<DCRTPoly>::SerializeEvalMultKey<SerType::SERBINARY>(outfile, sertype, id);
+                outfile.close();
+                return res; },
+            cc_SerializeEvalMultKey_docs,
+            py::arg("filename"), py::arg("sertype"), py::arg("id") = "")
+        .def_static(
+            "SerializeEvalAutomorphismKey", [](const std::string &filename, const SerType::SERBINARY &sertype, std::string id = "")
+            {
+                std::ofstream outfile(filename,std::ios::out | std::ios::binary);
+                bool res;
+                res = CryptoContextImpl<DCRTPoly>::SerializeEvalAutomorphismKey<SerType::SERBINARY>(outfile, sertype, id);
+                outfile.close();
+                return res; },
+            cc_SerializeEvalAutomorphismKey_docs,
+            py::arg("filename"), py::arg("sertype"), py::arg("id") = "")
+        .def_static("DeserializeEvalMultKey", [](std::shared_ptr<CryptoContextImpl<DCRTPoly>> &self, const std::string &filename, const SerType::SERBINARY &sertype)
+                    {
+                        std::ifstream emkeys(filename, std::ios::in | std::ios::binary);
+                         if (!emkeys.is_open()) {
+                            std::cerr << "I cannot read serialization from " << filename << std::endl;
+                         }
+                        bool res;
+                        res = self->DeserializeEvalMultKey<SerType::SERBINARY>(emkeys, sertype);
+                        return res; },
+                        cc_DeserializeEvalMultKey_docs,
+                        py::arg("self"),
+                        py::arg("filename"), py::arg("sertype"))
+        .def_static("DeserializeEvalAutomorphismKey", [](std::shared_ptr<CryptoContextImpl<DCRTPoly>> &self, const std::string &filename, const SerType::SERBINARY &sertype)
+                    {
+                        std::ifstream erkeys(filename, std::ios::in | std::ios::binary);
+                         if (!erkeys.is_open()) {
+                            std::cerr << "I cannot read serialization from " << filename << std::endl;
+                         }
+                        bool res;
+                        res = self->DeserializeEvalAutomorphismKey<SerType::SERBINARY>(erkeys, sertype);
+                        return res; },
+                        cc_DeserializeEvalAutomorphismKey_docs,
+                        py::arg("self"),
+                        py::arg("filename"), py::arg("sertype"));
+
+    // Generator Functions
+    m.def("GenCryptoContext", &GenCryptoContext<CryptoContextBFVRNS>,
+        py::arg("params"));
+    m.def("GenCryptoContext", &GenCryptoContext<CryptoContextBGVRNS>,
+        py::arg("params"));
+    m.def("GenCryptoContext", &GenCryptoContext<CryptoContextCKKSRNS>,
+        py::arg("params"));
+    m.def("ReleaseAllContexts", &CryptoContextFactory<DCRTPoly>::ReleaseAllContexts);
+}
+
+int get_native_int(){
+    #if NATIVEINT == 128 && !defined(__EMSCRIPTEN__)
+        return 128;
+    #else
+        return 64;    
+    #endif
+}
+
+void bind_enums_and_constants(py::module &m)
+{
+    /* ---- PKE enums ---- */ 
+    // Scheme Types
+    py::enum_<SCHEME>(m, "SCHEME")
+        .value("INVALID_SCHEME", SCHEME::INVALID_SCHEME)
+        .value("CKKSRNS_SCHEME", SCHEME::CKKSRNS_SCHEME)
+        .value("BFVRNS_SCHEME", SCHEME::BFVRNS_SCHEME)
+        .value("BGVRNS_SCHEME", SCHEME::BGVRNS_SCHEME);
+    m.attr("INVALID_SCHEME") = py::cast(SCHEME::INVALID_SCHEME);
+    m.attr("CKKSRNS_SCHEME") = py::cast(SCHEME::CKKSRNS_SCHEME);
+    m.attr("BFVRNS_SCHEME") = py::cast(SCHEME::BFVRNS_SCHEME);
+    m.attr("BGVRNS_SCHEME") = py::cast(SCHEME::BGVRNS_SCHEME);
+
+    // PKE Features
+    py::enum_<PKESchemeFeature>(m, "PKESchemeFeature")
+        .value("PKE", PKESchemeFeature::PKE)
+        .value("KEYSWITCH", PKESchemeFeature::KEYSWITCH)
+        .value("PRE", PKESchemeFeature::PRE)
+        .value("LEVELEDSHE", PKESchemeFeature::LEVELEDSHE)
+        .value("ADVANCEDSHE", PKESchemeFeature::ADVANCEDSHE)
+        .value("MULTIPARTY", PKESchemeFeature::MULTIPARTY)
+        .value("FHE", PKESchemeFeature::FHE);
+    m.attr("PKE") = py::cast(PKESchemeFeature::PKE);
+    m.attr("KEYSWITCH") = py::cast(PKESchemeFeature::KEYSWITCH);
+    m.attr("PRE") = py::cast(PKESchemeFeature::PRE);
+    m.attr("LEVELEDSHE") = py::cast(PKESchemeFeature::LEVELEDSHE);
+    m.attr("ADVANCEDSHE") = py::cast(PKESchemeFeature::ADVANCEDSHE);
+    m.attr("MULTIPARTY") = py::cast(PKESchemeFeature::MULTIPARTY);
+    m.attr("FHE") = py::cast(PKESchemeFeature::FHE);
+
+    // Serialization Types
+    py::class_<SerType::SERJSON>(m, "SERJSON");
+    py::class_<SerType::SERBINARY>(m, "SERBINARY");
+    m.attr("JSON") = py::cast(SerType::JSON);
+    m.attr("BINARY") = py::cast(SerType::BINARY);
+
+    // Scaling Techniques
+    py::enum_<ScalingTechnique>(m, "ScalingTechnique")
+       .value("FIXEDMANUAL", ScalingTechnique::FIXEDMANUAL)
+       .value("FIXEDAUTO", ScalingTechnique::FIXEDAUTO)
+       .value("FLEXIBLEAUTO", ScalingTechnique::FLEXIBLEAUTO)
+       .value("FLEXIBLEAUTOEXT", ScalingTechnique::FLEXIBLEAUTOEXT)
+       .value("NORESCALE", ScalingTechnique::NORESCALE)
+       .value("INVALID_RS_TECHNIQUE", ScalingTechnique::INVALID_RS_TECHNIQUE);
+    m.attr("FIXEDMANUAL") = py::cast(ScalingTechnique::FIXEDMANUAL);
+    m.attr("FIXEDAUTO") = py::cast(ScalingTechnique::FIXEDAUTO);
+    m.attr("FLEXIBLEAUTO") = py::cast(ScalingTechnique::FLEXIBLEAUTO);
+    m.attr("FLEXIBLEAUTOEXT") = py::cast(ScalingTechnique::FLEXIBLEAUTOEXT);
+    m.attr("NORESCALE") = py::cast(ScalingTechnique::NORESCALE);
+    m.attr("INVALID_RS_TECHNIQUE") = py::cast(ScalingTechnique::INVALID_RS_TECHNIQUE);
+
+    // Key Switching Techniques
+    py::enum_<KeySwitchTechnique>(m, "KeySwitchTechnique")
+        .value("INVALID_KS_TECH", KeySwitchTechnique::INVALID_KS_TECH)
+        .value("BV", KeySwitchTechnique::BV)
+        .value("HYBRID", KeySwitchTechnique::HYBRID);
+    m.attr("INVALID_KS_TECH") = py::cast(KeySwitchTechnique::INVALID_KS_TECH);
+    m.attr("BV") = py::cast(KeySwitchTechnique::BV);
+    m.attr("HYBRID") = py::cast(KeySwitchTechnique::HYBRID);
+
+    // Secret Key Dist
+    py::enum_<SecretKeyDist>(m, "SecretKeyDist")
+        .value("GAUSSIAN", SecretKeyDist::GAUSSIAN)
+        .value("UNIFORM_TERNARY", SecretKeyDist::UNIFORM_TERNARY)
+        .value("SPARSE_TERNARY", SecretKeyDist::SPARSE_TERNARY);
+    m.attr("GAUSSIAN") = py::cast(SecretKeyDist::GAUSSIAN);
+    m.attr("UNIFORM_TERNARY") = py::cast(SecretKeyDist::UNIFORM_TERNARY);
+    m.attr("SPARSE_TERNARY") = py::cast(SecretKeyDist::SPARSE_TERNARY);
+
+    // ProxyReEncryptionMode
+    py::enum_<ProxyReEncryptionMode>(m, "ProxyReEncryptionMode")
+        .value("NOT_SET", ProxyReEncryptionMode::NOT_SET)
+        .value("INDCPA", ProxyReEncryptionMode::INDCPA)
+        .value("FIXED_NOISE_HRA", ProxyReEncryptionMode::FIXED_NOISE_HRA)
+        .value("NOISE_FLOODING_HRA", ProxyReEncryptionMode::NOISE_FLOODING_HRA)
+        .value("DIVIDE_AND_ROUND_HRA", ProxyReEncryptionMode::DIVIDE_AND_ROUND_HRA);
+    m.attr("NOT_SET") = py::cast(ProxyReEncryptionMode::NOT_SET);
+    m.attr("INDCPA") = py::cast(ProxyReEncryptionMode::INDCPA);
+    m.attr("FIXED_NOISE_HRA") = py::cast(ProxyReEncryptionMode::FIXED_NOISE_HRA);
+    m.attr("NOISE_FLOODING_HRA") = py::cast(ProxyReEncryptionMode::NOISE_FLOODING_HRA);
+    m.attr("DIVIDE_AND_ROUND_HRA") = py::cast(ProxyReEncryptionMode::DIVIDE_AND_ROUND_HRA);
+    
+    // MultipartyMode
+    py::enum_<MultipartyMode>(m, "MultipartyMode")
+        .value("INVALID_MULTIPARTY_MODE", MultipartyMode::INVALID_MULTIPARTY_MODE)
+        .value("FIXED_NOISE_MULTIPARTY", MultipartyMode::FIXED_NOISE_MULTIPARTY)
+        .value("NOISE_FLOODING_MULTIPARTY", MultipartyMode::NOISE_FLOODING_MULTIPARTY);
+    m.attr("INVALID_MULTIPARTY_MODE") = py::cast(MultipartyMode::INVALID_MULTIPARTY_MODE);
+    m.attr("FIXED_NOISE_MULTIPARTY") = py::cast(MultipartyMode::FIXED_NOISE_MULTIPARTY);
+    m.attr("NOISE_FLOODING_MULTIPARTY") = py::cast(MultipartyMode::NOISE_FLOODING_MULTIPARTY);
+
+    // ExecutionMode
+    py::enum_<ExecutionMode>(m, "ExecutionMode")
+        .value("EXEC_EVALUATION", ExecutionMode::EXEC_EVALUATION)
+        .value("EXEC_NOISE_ESTIMATION", ExecutionMode::EXEC_NOISE_ESTIMATION);
+    m.attr("EXEC_EVALUATION") = py::cast(ExecutionMode::EXEC_EVALUATION);
+    m.attr("EXEC_NOISE_ESTIMATION") = py::cast(ExecutionMode::EXEC_NOISE_ESTIMATION);
+
+    // DecryptionNoiseMode
+    py::enum_<DecryptionNoiseMode>(m, "DecryptionNoiseMode")
+        .value("FIXED_NOISE_DECRYPT", DecryptionNoiseMode::FIXED_NOISE_DECRYPT)
+        .value("NOISE_FLOODING_DECRYPT", DecryptionNoiseMode::NOISE_FLOODING_DECRYPT);
+    m.attr("FIXED_NOISE_DECRYPT") = py::cast(DecryptionNoiseMode::FIXED_NOISE_DECRYPT);
+    m.attr("NOISE_FLOODING_DECRYPT") = py::cast(DecryptionNoiseMode::NOISE_FLOODING_DECRYPT);
+
+    // EncryptionTechnique
+    py::enum_<EncryptionTechnique>(m, "EncryptionTechnique")
+        .value("STANDARD", EncryptionTechnique::STANDARD)
+        .value("EXTENDED", EncryptionTechnique::EXTENDED);
+    m.attr("STANDARD") = py::cast(EncryptionTechnique::STANDARD);
+    m.attr("EXTENDED") = py::cast(EncryptionTechnique::EXTENDED);
+
+    // MultiplicationTechnique
+    py::enum_<MultiplicationTechnique>(m, "MultiplicationTechnique")
+        .value("BEHZ", MultiplicationTechnique::BEHZ)
+        .value("HPS", MultiplicationTechnique::HPS)
+        .value("HPSPOVERQ", MultiplicationTechnique::HPSPOVERQ)
+        .value("HPSPOVERQLEVELED", MultiplicationTechnique::HPSPOVERQLEVELED);
+    m.attr("BEHZ") = py::cast(MultiplicationTechnique::BEHZ);
+    m.attr("HPS") = py::cast(MultiplicationTechnique::HPS);
+    m.attr("HPSPOVERQ") = py::cast(MultiplicationTechnique::HPSPOVERQ);
+    m.attr("HPSPOVERQLEVELED") = py::cast(MultiplicationTechnique::HPSPOVERQLEVELED);
+
+    /* ---- CORE enums ---- */ 
+    // Security Level
+    py::enum_<SecurityLevel>(m,"SecurityLevel")
+        .value("HEStd_128_classic", SecurityLevel::HEStd_128_classic)
+        .value("HEStd_192_classic", SecurityLevel::HEStd_192_classic)
+        .value("HEStd_256_classic", SecurityLevel::HEStd_256_classic)
+        .value("HEStd_NotSet", SecurityLevel::HEStd_NotSet);
+    m.attr("HEStd_128_classic") = py::cast(SecurityLevel::HEStd_128_classic);
+    m.attr("HEStd_192_classic") = py::cast(SecurityLevel::HEStd_192_classic);
+    m.attr("HEStd_256_classic") = py::cast(SecurityLevel::HEStd_256_classic);
+    m.attr("HEStd_NotSet") = py::cast(SecurityLevel::HEStd_NotSet);
+    
+    //Parameters Type
+    /*TODO (Oliveira): If we expose Poly's and ParmType, this block will go somewhere else */
+    using ParmType = typename DCRTPoly::Params;
+    py::class_<ParmType, std::shared_ptr<ParmType>>(m, "ParmType");
+
+    //NATIVEINT function
+    m.def("get_native_int", &get_native_int);
+  
+    // EvalKeyMap
+    py::bind_map<std::map<usint, EvalKey<DCRTPoly>>>(m, "EvalKeyMap");
+}
+
+void bind_keys(py::module &m)
+{
+    py::class_<PublicKeyImpl<DCRTPoly>, std::shared_ptr<PublicKeyImpl<DCRTPoly>>>(m, "PublicKey")
+        .def(py::init<>());
+    py::class_<PrivateKeyImpl<DCRTPoly>, std::shared_ptr<PrivateKeyImpl<DCRTPoly>>>(m, "PrivateKey");
+    py::class_<KeyPair<DCRTPoly>>(m, "KeyPair")
+        .def_readwrite("publicKey", &KeyPair<DCRTPoly>::publicKey)
+        .def_readwrite("secretKey", &KeyPair<DCRTPoly>::secretKey);
+    py::class_<EvalKeyImpl<DCRTPoly>, std::shared_ptr<EvalKeyImpl<DCRTPoly>>>(m, "EvalKey")
+        .def(py::init<>());
+}
+
+void bind_encodings(py::module &m)
+{
+    py::class_<PlaintextImpl, std::shared_ptr<PlaintextImpl>>(m, "Plaintext")
+        .def("GetScalingFactor", &PlaintextImpl::GetScalingFactor,
+            ptx_GetScalingFactor_docs)
+        .def("SetScalingFactor", &PlaintextImpl::SetScalingFactor,
+            ptx_SetScalingFactor_docs,
+            py::arg("sf"))
+        .def("GetLength", &PlaintextImpl::GetLength,
+            ptx_GetLength_docs)
+        .def("GetSchemeID", &PlaintextImpl::GetSchemeID,
+            ptx_GetSchemeID_docs)
+        .def("SetLength", &PlaintextImpl::SetLength,
+            ptx_SetLength_docs,
+            py::arg("newSize"))
+        .def("IsEncoded", &PlaintextImpl::IsEncoded,
+            ptx_IsEncoded_docs)
+        .def("GetLogPrecision", &PlaintextImpl::GetLogPrecision,
+            ptx_GetLogPrecision_docs)
+        .def("Encode", &PlaintextImpl::Encode,
+            ptx_Encode_docs)
+        .def("Decode", &PlaintextImpl::Decode,
+            ptx_Decode_docs)
+        .def("GetCKKSPackedValue", &PlaintextImpl::GetCKKSPackedValue,
+            ptx_GetCKKSPackedValue_docs)
+        .def("__repr__", [](const PlaintextImpl &p)
+             {
+        std::stringstream ss;
+        ss << "<Plaintext Object: ";
+        p.PrintValue(ss);
+        ss << ">";
+        return ss.str(); })
+        .def("__str__", [](const PlaintextImpl &p)
+             {
+        std::stringstream ss;
+        p.PrintValue(ss);
+        return ss.str(); });
+}
+
+void bind_ciphertext(py::module &m)
+{
+    py::class_<CiphertextImpl<DCRTPoly>, std::shared_ptr<CiphertextImpl<DCRTPoly>>>(m, "Ciphertext")
+        .def(py::init<>())
+        .def("__add__", [](const Ciphertext<DCRTPoly> &a, const Ciphertext<DCRTPoly> &b)
+             {return a + b; },py::is_operator(),pybind11::keep_alive<0, 1>())
+       // .def(py::self + py::self);
+    // .def("GetDepth", &CiphertextImpl<DCRTPoly>::GetDepth)
+    // .def("SetDepth", &CiphertextImpl<DCRTPoly>::SetDepth)
+     .def("GetLevel", &CiphertextImpl<DCRTPoly>::GetLevel,
+        ctx_GetLevel_docs)
+     .def("SetLevel", &CiphertextImpl<DCRTPoly>::SetLevel,
+        ctx_SetLevel_docs,
+        py::arg("level"));
+    // .def("GetHopLevel", &CiphertextImpl<DCRTPoly>::GetHopLevel)
+    // .def("SetHopLevel", &CiphertextImpl<DCRTPoly>::SetHopLevel)
+    // .def("GetScalingFactor", &CiphertextImpl<DCRTPoly>::GetScalingFactor)
+    // .def("SetScalingFactor", &CiphertextImpl<DCRTPoly>::SetScalingFactor)
+    // .def("GetSlots", &CiphertextImpl<DCRTPoly>::GetSlots)
+    // .def("SetSlots", &CiphertextImpl<DCRTPoly>::SetSlots);
+}
+
+void bind_schemes(py::module &m){
+    /*Bind schemes specific functionalities like bootstrapping functions and multiparty*/
+    py::class_<FHECKKSRNS>(m, "FHECKKSRNS")
+        .def(py::init<>())
+        //.def_static("GetBootstrapDepth", &FHECKKSRNS::GetBootstrapDepth)
+        .def_static("GetBootstrapDepth", static_cast<uint32_t (*)(uint32_t, const std::vector<uint32_t>&, SecretKeyDist)>(&FHECKKSRNS::GetBootstrapDepth));                               
+    
+}
+
+PYBIND11_MODULE(openfhe, m)
+{
+    m.doc() = "Open-Source Fully Homomorphic Encryption Library";
+    // pke library
+    bind_enums_and_constants(m);
+    bind_parameters<CryptoContextBFVRNS>(m,"CCParamsBFVRNS");
+    bind_parameters<CryptoContextBGVRNS>(m,"CCParamsBGVRNS");
+    bind_parameters<CryptoContextCKKSRNS>(m,"CCParamsCKKSRNS");
+    bind_encodings(m);
+    bind_ciphertext(m);
+    bind_keys(m);
+    bind_crypto_context(m);
+    bind_serialization(m);
+    bind_schemes(m);
+    // binfhe library
+    bind_binfhe_enums(m);
+    bind_binfhe_context(m);
+    bind_binfhe_keys(m);
+    bind_binfhe_ciphertext(m);
+}

+ 25 - 0
src/binfhe/binfhecontext_wrapper.cpp

@@ -0,0 +1,25 @@
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+#include <openfhe.h>
+#include "binfhecontext_wrapper.h"
+
+using namespace lbcrypto;
+namespace py = pybind11;
+
+LWECiphertext binfhe_EncryptWrapper(BinFHEContext &self, ConstLWEPrivateKey sk, const LWEPlaintext &m, BINFHE_OUTPUT output,
+                                    LWEPlaintextModulus p, uint64_t mod)
+{
+    NativeInteger mod_native_int = NativeInteger(mod);
+    return self.Encrypt(sk, m, output, p, mod_native_int);
+}
+
+LWEPlaintext binfhe_DecryptWrapper(BinFHEContext &self,
+                                   ConstLWEPrivateKey sk,
+                                   ConstLWECiphertext ct,
+                                   LWEPlaintextModulus p)
+{
+
+    LWEPlaintext result;
+    self.Decrypt(sk, ct, &result, p);
+    return result;
+}

+ 58 - 0
src/binfhe/examples/boolean-ap.py

@@ -0,0 +1,58 @@
+from openfhe import *
+
+## Sample Program: Step 1: Set CryptoContext
+
+cc = BinFHEContext()
+
+"""
+STD128 is the security level of 128 bits of security based on LWE Estimator
+and HE standard. Other common options are TOY, MEDIUM, STD192, and STD256.
+MEDIUM corresponds to the level of more than 100 bits for both quantum and
+classical computer attacks. The second argument is the bootstrapping method
+(AP or GINX). The default method is GINX. Here we explicitly set AP. GINX
+typically provides better performance: the bootstrapping key is much
+smaller in GINX (by 20x) while the runtime is roughly the same.
+"""
+cc.GenerateBinFHEContext(STD128,AP)
+
+## Sample Program: Step 2: Key Generation
+
+# Generate the secret key
+sk = cc.KeyGen()
+
+print("Generating the bootstrapping keys...\n")
+
+# Generate the bootstrapping keys (refresh and switching keys)
+cc.BTKeyGen(sk)
+
+print("Completed the key generation.\n")
+# Sample Program: Step 3: Encryption
+"""
+Encrypt two ciphertexts representing Boolean True (1).
+By default, freshly encrypted ciphertexts are bootstrapped.
+If you wish to get a fresh encryption without bootstrapping, write
+ct1 = cc.Encrypt(sk, 1, FRESH)
+"""
+ct1 = cc.Encrypt(sk, 1)
+ct2 = cc.Encrypt(sk, 1)
+
+# Sample Program: Step 4: Evaluation
+
+# Compute (1 AND 1) = 1; Other binary gate options are OR, NAND, and NOR
+ctAND1 = cc.EvalBinGate(AND, ct1, ct2)
+
+# Compute (NOT 1) = 0
+ct2Not = cc.EvalNOT(ct2)
+
+# Compute (1 AND (NOT 1)) = 0
+ctAND2 = cc.EvalBinGate(AND, ct2Not, ct1)
+
+# Compute OR of the result in ctAND1 and ctAND2
+ctResult = cc.EvalBinGate(OR, ctAND1, ctAND2)
+
+# Sample Program: Step 5: Decryption
+
+result = cc.Decrypt(sk, ctResult)
+
+print(f"Result of encrypted computation of (1 AND 1) OR (1 AND (NOT 1)) = {result}")
+

+ 197 - 0
src/binfhe/examples/boolean-truth-tables.py

@@ -0,0 +1,197 @@
+from openfhe import *
+
+# Sample Program: Step 1: Set CryptoContext
+cc = BinFHEContext()
+
+print("Generate cryptocontext\n")
+
+"""
+STD128 is the security level of 128 bits of security based on LWE Estimator
+and HE standard. Other common options are TOY, MEDIUM, STD192, and STD256. MEDIUM 
+corresponds to the level of more than 100 bits for both quantum and
+classical computer attacks
+"""
+
+cc.GenerateBinFHEContext(STD128)
+print("Finished generating cryptocontext\n")
+
+# Sample Program: Step 2: Key Generation
+
+# Generate the secret key
+sk = cc.KeyGen()
+
+print("Generating the bootstrapping keys...\n")
+
+# Generate the bootstrapping keys (refresh and switching keys)
+cc.BTKeyGen(sk)
+
+print("Completed the key generation.\n\n")
+
+# Sample Program: Step 3: Encryption
+
+# Encrypt two ciphertexts representing Boolean True (1).
+ct10 = cc.Encrypt(sk, 1)
+ct11 = cc.Encrypt(sk, 1)
+# Encrypt two ciphertexts representing Boolean False (0).
+ct00 = cc.Encrypt(sk, 0)
+ct01 = cc.Encrypt(sk, 0)
+
+# Sample Program: Step 4: Evaluation of NAND gates
+
+ctNAND1 = cc.EvalBinGate(NAND, ct10, ct11)
+ctNAND2 = cc.EvalBinGate(NAND, ct10, ct01)
+ctNAND3 = cc.EvalBinGate(NAND, ct00, ct01)
+ctNAND4 = cc.EvalBinGate(NAND, ct00, ct11)
+
+result = cc.Decrypt(sk, ctNAND1)
+print(f"1 NAND 1 = {result}")
+
+result = cc.Decrypt(sk, ctNAND2)
+print(f"1 NAND 0 = {result}")
+
+result = cc.Decrypt(sk, ctNAND3)
+print(f"0 NAND 0 = {result}")
+
+result = cc.Decrypt(sk, ctNAND4)
+print(f"0 NAND 1 = {result}")
+
+# Sample Program: Step 5: Evaluation of AND gates
+
+ctAND1 = cc.EvalBinGate(AND, ct10, ct11)
+ctAND2 = cc.EvalBinGate(AND, ct10, ct01)
+ctAND3 = cc.EvalBinGate(AND, ct00, ct01)
+ctAND4 = cc.EvalBinGate(AND, ct00, ct11)
+
+result = cc.Decrypt(sk, ctAND1)
+print(f"1 AND 1 = {result}")
+
+result = cc.Decrypt(sk, ctAND2)
+print(f"1 AND 0 = {result}")
+
+result = cc.Decrypt(sk, ctAND3)
+print(f"0 AND 0 = {result}")
+
+result = cc.Decrypt(sk, ctAND4)
+print(f"0 AND 1 = {result}")
+
+# Sample Program: Step 6: Evaluation of OR gates
+
+ctOR1 = cc.EvalBinGate(OR, ct10, ct11)
+ctOR2 = cc.EvalBinGate(OR, ct10, ct01)
+ctOR3 = cc.EvalBinGate(OR, ct00, ct01)
+ctOR4 = cc.EvalBinGate(OR, ct00, ct11)
+
+result = cc.Decrypt(sk, ctOR1)
+print(f"1 OR 1 = {result}")
+
+result = cc.Decrypt(sk, ctOR2)
+print(f"1 OR 0 = {result}")
+
+result = cc.Decrypt(sk, ctOR3)
+print(f"0 OR 0 = {result}")
+
+result = cc.Decrypt(sk, ctOR4)
+print(f"0 OR 1 = {result}")
+
+# Sample Program: Step 7: Evaluation of NOR gates
+
+ctNOR1 = cc.EvalBinGate(NOR, ct10, ct11)
+ctNOR2 = cc.EvalBinGate(NOR, ct10, ct01)
+ctNOR3 = cc.EvalBinGate(NOR, ct00, ct01)
+ctNOR4 = cc.EvalBinGate(NOR, ct00, ct11)
+
+result = cc.Decrypt(sk, ctNOR1)
+print(f"1 NOR 1 = {result}")
+
+result = cc.Decrypt(sk, ctNOR2)
+print(f"1 NOR 0 = {result}")
+
+result = cc.Decrypt(sk, ctNOR3)
+print(f"0 NOR 0 = {result}")
+
+result = cc.Decrypt(sk, ctNOR4)
+print(f"0 NOR 1 = {result}")
+
+# Sample Program: Step 8: Evaluation of XOR gates
+
+ctXOR1 = cc.EvalBinGate(XOR, ct10, ct11)
+ctXOR2 = cc.EvalBinGate(XOR, ct10, ct01)
+ctXOR3 = cc.EvalBinGate(XOR, ct00, ct01)
+ctXOR4 = cc.EvalBinGate(XOR, ct00, ct11)
+
+result = cc.Decrypt(sk, ctXOR1)
+print(f"1 XOR 1 = {result}")
+
+result = cc.Decrypt(sk, ctXOR2)
+print(f"1 XOR 0 = {result}")
+
+result = cc.Decrypt(sk, ctXOR3)
+print(f"0 XOR 0 = {result}")
+
+result = cc.Decrypt(sk, ctXOR4)
+print(f"0 XOR 1 = {result}")
+
+# Sample Program: Step 9: Evaluation of XNOR gates
+
+ctXNOR1 = cc.EvalBinGate(XNOR, ct10, ct11)
+ctXNOR2 = cc.EvalBinGate(XNOR, ct10, ct01)
+ctXNOR3 = cc.EvalBinGate(XNOR, ct00, ct01)
+ctXNOR4 = cc.EvalBinGate(XNOR, ct00, ct11)
+
+result = cc.Decrypt(sk, ctXNOR1)
+print(f"1 XNOR 1 = {result}")
+
+result = cc.Decrypt(sk, ctXNOR2)
+print(f"1 XNOR 0 = {result}")
+
+result = cc.Decrypt(sk, ctXNOR3)
+print(f"0 XNOR 0 = {result}")
+
+result = cc.Decrypt(sk, ctXNOR4)
+print(f"0 XNOR 1 = {result}")
+
+# Sample Program: Step 90: Evaluation of NOR gates
+# using XOR_FAT (1 boostrap but the probability of failure is higher)
+
+ctNOR1_FAST = cc.EvalBinGate(XOR_FAST, ct10, ct11)
+ctNOR2_FAST = cc.EvalBinGate(XOR_FAST, ct10, ct01)
+ctNOR3_FAST = cc.EvalBinGate(XOR_FAST, ct00, ct01)
+ctNOR4_FAST = cc.EvalBinGate(XOR_FAST, ct00, ct11)
+
+result = cc.Decrypt(sk, ctNOR1_FAST)
+print(f"1 XOR_FAST 1 = {result}")
+
+result = cc.Decrypt(sk, ctNOR2_FAST)
+print(f"1 XOR_FAST 0 = {result}")
+
+result = cc.Decrypt(sk, ctNOR3_FAST)
+print(f"0 XOR_FAST 0 = {result}")
+
+result = cc.Decrypt(sk, ctNOR4_FAST)
+print(f"0 XOR_FAST 1 = {result}")
+
+# Sample Program: Step 10: Evaluation of XNOR gates
+# using XNOR_FAT (1 boostrap but the probability of failure is higher)
+
+ctXNOR1_FAST = cc.EvalBinGate(XNOR_FAST, ct10, ct11)
+ctXNOR2_FAST = cc.EvalBinGate(XNOR_FAST, ct10, ct01)
+ctXNOR3_FAST = cc.EvalBinGate(XNOR_FAST, ct00, ct01)
+ctXNOR4_FAST = cc.EvalBinGate(XNOR_FAST, ct00, ct11)
+
+result = cc.Decrypt(sk, ctXNOR1_FAST)
+print(f"1 XNOR_FAST 1 = {result}")
+
+result = cc.Decrypt(sk, ctXNOR2_FAST)
+print(f"1 XNOR_FAST 0 = {result}")
+
+result = cc.Decrypt(sk, ctXNOR3_FAST)
+print(f"0 XNOR_FAST 0 = {result}")
+
+result = cc.Decrypt(sk, ctXNOR4_FAST)
+print(f"0 XNOR_FAST 1 = {result}")
+
+
+
+
+
+

+ 54 - 0
src/binfhe/examples/boolean.py

@@ -0,0 +1,54 @@
+from openfhe import *
+
+## Sample Program: Step 1: Set CryptoContext
+
+cc = BinFHEContext()
+
+"""
+STD128 is the security level of 128 bits of security based on LWE Estimator
+and HE standard. Other common options are TOY, MEDIUM, STD192, and STD256.
+MEDIUM corresponds to the level of more than 100 bits for both quantum and
+classical computer attacks
+"""
+cc.GenerateBinFHEContext(STD128,GINX)
+
+## Sample Program: Step 2: Key Generation
+
+# Generate the secret key
+sk = cc.KeyGen()
+
+print("Generating the bootstrapping keys...\n")
+
+# Generate the bootstrapping keys (refresh and switching keys)
+cc.BTKeyGen(sk)
+
+# Sample Program: Step 3: Encryption
+"""
+Encrypt two ciphertexts representing Boolean True (1).
+By default, freshly encrypted ciphertexts are bootstrapped.
+If you wish to get a fresh encryption without bootstrapping, write
+ct1 = cc.Encrypt(sk, 1, FRESH)
+"""
+ct1 = cc.Encrypt(sk, 1)
+ct2 = cc.Encrypt(sk, 1)
+
+# Sample Program: Step 4: Evaluation
+
+# Compute (1 AND 1) = 1; Other binary gate options are OR, NAND, and NOR
+ctAND1 = cc.EvalBinGate(AND, ct1, ct2)
+
+# Compute (NOT 1) = 0
+ct2Not = cc.EvalNOT(ct2)
+
+# Compute (1 AND (NOT 1)) = 0
+ctAND2 = cc.EvalBinGate(AND, ct2Not, ct1)
+
+# Compute OR of the result in ctAND1 and ctAND2
+ctResult = cc.EvalBinGate(OR, ctAND1, ctAND2)
+
+# Sample Program: Step 5: Decryption
+
+result = cc.Decrypt(sk, ctResult)
+
+print(f"Result of encrypted computation of (1 AND 1) OR (1 AND (NOT 1)) = {result}")
+

+ 137 - 0
src/binfhe_bindings.cpp

@@ -0,0 +1,137 @@
+#include <pybind11/pybind11.h>
+#include <pybind11/operators.h>
+#include <iostream>
+#include "openfhe.h"
+#include "binfhe_bindings.h"
+#include "binfhecontext.h"
+#include "binfhecontext_wrapper.h"
+#include "binfhecontext_docs.h"
+
+using namespace lbcrypto;
+namespace py = pybind11;
+
+void bind_binfhe_enums(py::module &m)
+{
+    py::enum_<BINFHE_PARAMSET>(m, "BINFHE_PARAMSET")
+        .value("TOY", BINFHE_PARAMSET::TOY)
+        .value("MEDIUM", BINFHE_PARAMSET::MEDIUM)
+        .value("STD128_AP", BINFHE_PARAMSET::STD128_AP)
+        .value("STD128_APOPT", BINFHE_PARAMSET::STD128_APOPT)
+        .value("STD128", BINFHE_PARAMSET::STD128)
+        .value("STD128_OPT", BINFHE_PARAMSET::STD128_OPT)
+        .value("STD192", BINFHE_PARAMSET::STD192)
+        .value("STD192_OPT", BINFHE_PARAMSET::STD192_OPT)
+        .value("STD256", BINFHE_PARAMSET::STD256)
+        .value("STD256_OPT", BINFHE_PARAMSET::STD256_OPT)
+        .value("STD128Q", BINFHE_PARAMSET::STD128Q)
+        .value("STD128Q_OPT", BINFHE_PARAMSET::STD128Q_OPT)
+        .value("STD192Q", BINFHE_PARAMSET::STD192Q)
+        .value("STD192Q_OPT", BINFHE_PARAMSET::STD192Q_OPT)
+        .value("STD256Q", BINFHE_PARAMSET::STD256Q)
+        .value("STD256Q_OPT", BINFHE_PARAMSET::STD256Q_OPT)
+        .value("SIGNED_MOD_TEST", BINFHE_PARAMSET::SIGNED_MOD_TEST);
+    m.attr("TOY") = py::cast(BINFHE_PARAMSET::TOY);
+    m.attr("MEDIUM") = py::cast(BINFHE_PARAMSET::MEDIUM);
+    m.attr("STD128_AP") = py::cast(BINFHE_PARAMSET::STD128_AP);
+    m.attr("STD128_APOPT") = py::cast(BINFHE_PARAMSET::STD128_APOPT);
+    m.attr("STD128") = py::cast(BINFHE_PARAMSET::STD128);
+    m.attr("STD128_OPT") = py::cast(BINFHE_PARAMSET::STD128_OPT);
+    m.attr("STD192") = py::cast(BINFHE_PARAMSET::STD192);
+    m.attr("STD192_OPT") = py::cast(BINFHE_PARAMSET::STD192_OPT);
+    m.attr("STD256") = py::cast(BINFHE_PARAMSET::STD256);
+    m.attr("STD256_OPT") = py::cast(BINFHE_PARAMSET::STD256_OPT);
+    m.attr("STD128Q") = py::cast(BINFHE_PARAMSET::STD128Q);
+    m.attr("STD128Q_OPT") = py::cast(BINFHE_PARAMSET::STD128Q_OPT);
+    m.attr("STD192Q") = py::cast(BINFHE_PARAMSET::STD192Q);
+    m.attr("STD192Q_OPT") = py::cast(BINFHE_PARAMSET::STD192Q_OPT);
+    m.attr("STD256Q") = py::cast(BINFHE_PARAMSET::STD256Q);
+    m.attr("STD256Q_OPT") = py::cast(BINFHE_PARAMSET::STD256Q_OPT);
+    m.attr("SIGNED_MOD_TEST") = py::cast(BINFHE_PARAMSET::SIGNED_MOD_TEST);
+
+    py::enum_<BINFHE_METHOD>(m, "BINFHE_METHOD")
+        .value("INVALID_METHOD", BINFHE_METHOD::INVALID_METHOD)
+        .value("AP", BINFHE_METHOD::AP)
+        .value("GINX", BINFHE_METHOD::GINX);
+    m.attr("INVALID_METHOD") = py::cast(BINFHE_METHOD::INVALID_METHOD);
+    m.attr("GINX") = py::cast(BINFHE_METHOD::GINX);
+    m.attr("AP") = py::cast(BINFHE_METHOD::AP);
+
+    py::enum_<BINFHE_OUTPUT>(m, "BINFHE_OUTPUT")
+        .value("INVALID_OUTPUT", BINFHE_OUTPUT::INVALID_OUTPUT)
+        .value("FRESH", BINFHE_OUTPUT::FRESH)
+        .value("BOOTSTRAPPED", BINFHE_OUTPUT::BOOTSTRAPPED);
+    m.attr("INVALID_OUTPUT") = py::cast(BINFHE_OUTPUT::INVALID_OUTPUT);
+    m.attr("FRESH") = py::cast(BINFHE_OUTPUT::FRESH);
+    m.attr("BOOTSTRAPPED") = py::cast(BINFHE_OUTPUT::BOOTSTRAPPED);
+
+    py::enum_<BINGATE>(m, "BINGATE")
+        .value("OR", BINGATE::OR)
+        .value("AND", BINGATE::AND)
+        .value("NOR", BINGATE::NOR)
+        .value("NAND", BINGATE::NAND)
+        .value("XOR_FAST", BINGATE::XOR_FAST)
+        .value("XNOR_FAST", BINGATE::XNOR_FAST)
+        .value("XOR", BINGATE::XOR)
+        .value("XNOR", BINGATE::XNOR);
+    m.attr("OR") = py::cast(BINGATE::OR);
+    m.attr("AND") = py::cast(BINGATE::AND);
+    m.attr("NOR") = py::cast(BINGATE::NOR);
+    m.attr("NAND") = py::cast(BINGATE::NAND);
+    m.attr("XOR_FAST") = py::cast(BINGATE::XOR_FAST);
+    m.attr("XNOR_FAST") = py::cast(BINGATE::XNOR_FAST);
+    m.attr("XOR") = py::cast(BINGATE::XOR);
+    m.attr("XNOR") = py::cast(BINGATE::XNOR);
+
+}
+
+void bind_binfhe_keys(py::module &m)
+{
+    py::class_<LWEPrivateKeyImpl, std::shared_ptr<LWEPrivateKeyImpl>>(m, "LWEPrivateKey")
+        .def(py::init<>())
+        .def("GetLength", &LWEPrivateKeyImpl::GetLength)
+        .def(py::self == py::self)
+        .def(py::self != py::self);
+}
+void bind_binfhe_ciphertext(py::module &m)
+{
+    py::class_<LWECiphertextImpl, std::shared_ptr<LWECiphertextImpl>>(m, "LWECiphertext")
+        .def(py::init<>())
+        .def("GetLength", &LWECiphertextImpl::GetLength)
+        .def(py::self == py::self)
+        .def(py::self != py::self);
+}
+
+void bind_binfhe_context(py::module &m)
+{
+    py::class_<BinFHEContext>(m, "BinFHEContext")
+        .def(py::init<>())
+        .def("GenerateBinFHEContext", static_cast<void (BinFHEContext::*)(BINFHE_PARAMSET, BINFHE_METHOD)>(&BinFHEContext::GenerateBinFHEContext),
+             binfhe_GenerateBinFHEContext_parset_docs,
+             py::arg("set"), 
+             py::arg("method") = GINX)
+        .def("KeyGen", &BinFHEContext::KeyGen,
+            binfhe_KeyGen_docs)
+        .def("BTKeyGen", &BinFHEContext::BTKeyGen,
+            binfhe_BTKeyGen_docs)
+        .def("Encrypt", &binfhe_EncryptWrapper,
+             binfhe_Encrypt_docs,
+             py::arg("sk"),
+             py::arg("m"),
+             py::arg("output") = BOOTSTRAPPED,
+             py::arg("p") = 4, 
+             py::arg("mod") = 0)
+        .def("Decrypt",&binfhe_DecryptWrapper,
+             binfhe_Decrypt_docs,
+             py::arg("sk"),
+             py::arg("ct"),
+             py::arg("p") = 4)
+        .def("EvalBinGate",&BinFHEContext::EvalBinGate,
+             binfhe_EvalBinGate_docs,
+             py::arg("gate"),
+             py::arg("ct1"),
+             py::arg("ct2"))
+        .def("EvalNOT",&BinFHEContext::EvalNOT,
+             binfhe_EvalNOT_docs,
+             py::arg("ct"));
+            
+}

+ 51 - 0
src/pke/cryptocontext_wrapper.cpp

@@ -0,0 +1,51 @@
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+#include <openfhe.h>
+#include <vector>
+#include <algorithm>
+#include <complex> 
+#include "cryptocontext_wrapper.h"
+
+using namespace lbcrypto;
+namespace py = pybind11;
+
+Ciphertext<DCRTPoly> EvalFastRotationPrecomputeWrapper(CryptoContext<DCRTPoly> &self,ConstCiphertext<DCRTPoly> ciphertext) {
+    std::shared_ptr<std::vector<DCRTPoly>> precomp = self->EvalFastRotationPrecompute(ciphertext);
+    std::vector<DCRTPoly> elements = *(precomp.get());
+    CiphertextImpl<DCRTPoly> cipherdigits = CiphertextImpl<DCRTPoly>(self);
+    std::shared_ptr<CiphertextImpl<DCRTPoly>> cipherdigitsPtr = std::make_shared<CiphertextImpl<DCRTPoly>>(cipherdigits);
+    cipherdigitsPtr->SetElements(elements);
+    return cipherdigitsPtr;
+}
+Ciphertext<DCRTPoly> EvalFastRotationWrapper(CryptoContext<DCRTPoly>& self,ConstCiphertext<DCRTPoly> ciphertext, const usint index, const usint m,ConstCiphertext<DCRTPoly> digits) {
+    
+        std::vector<DCRTPoly> digitsElements = digits->GetElements();
+        std::shared_ptr<std::vector<DCRTPoly>> digitsElementsPtr = std::make_shared<std::vector<DCRTPoly>>(digitsElements);
+        return self->EvalFastRotation(ciphertext, index, m, digitsElementsPtr);
+    }
+
+Ciphertext<DCRTPoly> EvalFastRotationExtWrapper(CryptoContext<DCRTPoly>& self,ConstCiphertext<DCRTPoly> ciphertext, const usint index, ConstCiphertext<DCRTPoly> digits, bool addFirst) {
+    std::vector<DCRTPoly> digitsElements = digits->GetElements();
+    std::shared_ptr<std::vector<DCRTPoly>> digitsElementsPtr = std::make_shared<std::vector<DCRTPoly>>(digitsElements);
+    return self->EvalFastRotationExt(ciphertext, index, digitsElementsPtr, addFirst);
+}
+
+
+Plaintext DecryptWrapper(CryptoContext<DCRTPoly>& self,ConstCiphertext<DCRTPoly> ciphertext,const PrivateKey<DCRTPoly> privateKey){
+    Plaintext plaintextDecResult;
+    self->Decrypt(privateKey, ciphertext,&plaintextDecResult);
+    return plaintextDecResult;
+}
+Plaintext DecryptWrapper(CryptoContext<DCRTPoly>& self,const PrivateKey<DCRTPoly> privateKey,ConstCiphertext<DCRTPoly> ciphertext){
+    Plaintext plaintextDecResult;
+    self->Decrypt(privateKey, ciphertext,&plaintextDecResult);
+    return plaintextDecResult;
+}
+
+const std::map<usint, EvalKey<DCRTPoly>> EvalAutomorphismKeyGenWrapper(CryptoContext<DCRTPoly>& self,const PrivateKey<DCRTPoly> privateKey,const std::vector<usint> &indexList){
+    return *(self->EvalAutomorphismKeyGen(privateKey, indexList));
+}
+
+const std::map<usint, EvalKey<DCRTPoly>> EvalAutomorphismKeyGenWrapper_PublicKey(CryptoContext<DCRTPoly>& self,const PublicKey<DCRTPoly> publicKey, const PrivateKey<DCRTPoly> privateKey, const std::vector<usint> &indexList){
+    return *(self->EvalAutomorphismKeyGen(publicKey, privateKey, indexList));
+};

+ 390 - 0
src/pke/examples/advanced-real-numbers-128.py

@@ -0,0 +1,390 @@
+from openfhe import *
+import time # to enable TIC-TOC timing measurements
+ 
+def automatic_rescale_demo(scal_tech):
+    if(scal_tech == ScalingTechnique.FLEXIBLEAUTO):
+        print("\n\n\n ===== FlexibleAutoDemo =============\n") 
+    else:
+         print("\n\n\n ===== FixedAutoDemo =============\n")
+
+    batch_size = 8
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(6)
+    parameters.SetScalingModSize(90)
+    parameters.SetScalingTechnique(scal_tech)
+    parameters.SetBatchSize(batch_size)
+
+    cc = GenCryptoContext(parameters)
+
+    print(f"CKKS scheme is using ring dimension {cc.GetRingDimension()}\n")
+
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+
+    keys = cc.KeyGen()
+    cc.EvalMultKeyGen(keys.secretKey)
+
+    # Input
+    x = [1.0, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07]
+    ptxt = cc.MakeCKKSPackedPlaintext(x)
+
+    print(f"Input x: {ptxt}")
+
+    c = cc.Encrypt(keys.publicKey,ptxt)
+
+    # Computing f(x) = x^18 + x^9 + d
+    #
+    # In the following we compute f(x) with a computation
+    # that has a multiplicative depth of 5.
+    #
+    # The result is correct, even though there is no call to
+    # the Rescale() operation.
+
+    c2 = cc.EvalMult(c, c)                        # x^2
+    c4 = cc.EvalMult(c2, c2)                      # x^4
+    c8 = cc.EvalMult(c4, c4)                      # x^8
+    c16 = cc.EvalMult(c8, c8)                     # x^16
+    c9 = cc.EvalMult(c8, c)                       # x^9
+    c18 = cc.EvalMult(c16, c2)                    # x^18
+    c_res1 = cc.EvalAdd(cc.EvalAdd(c18, c9), 1.0)  # Final result 1
+    c_res2 = cc.EvalSub(cc.EvalAdd(c18,c9), 1.0)   # Final result 2
+    c_res3 = cc.EvalMult(cc.EvalAdd(c18,c9), 0.5)  # Final result 3
+
+    result1 = cc.Decrypt(c_res1,keys.secretKey)
+    result.SetLength(batch_size)
+    print("x^18 + x^9 + 1 = ", result1)
+    
+    result2 = cc.Decrypt(c_res2,keys.secretKey)
+    result.SetLength(batch_size)
+    print("x^18 + x^9 - 1 = ", result2)
+
+    result3 = cc.Decrypt(c_res3,keys.secretKey)
+    result.SetLength(batch_size)
+    print("0.5 * (x^18 + x^9) = ", result3)
+
+
+def manual_rescale_demo(scal_tech):
+    print("\n\n\n ===== FixedManualDemo =============\n")
+    
+    batch_size = 8
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(5)
+    parameters.SetScalingModSize(90)
+    parameters.SetBatchSize(batch_size)
+
+    cc = GenCryptoContext(parameters)
+
+    print(f"CKKS scheme is using ring dimension {cc.GetRingDimension()}\n")
+    
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+
+    keys = cc.KeyGen()
+    cc.EvalMultKeyGen(keys.secretKey)
+
+    # Input
+    x = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7]
+    ptxt = cc.MakeCKKSPackedPlaintext(x)
+
+    print(f"Input x: {ptxt}")
+
+    c = cc.Encrypt(keys.publicKey,ptxt)
+
+    # Computing f(x) = x^18 + x^9 + 1
+    #
+    # Compare the following with the corresponding code
+    # for FIXEDAUTO. Here we need to track the depth of ciphertexts
+    # and call Rescale whenever needed. In this instance it's still
+    # not hard to do so, but this can be quite tedious in other
+    # complicated computations. (e.g. in bootstrapping)
+    #
+    #
+
+    # x^2
+    c2_depth2 = cc.EvalMult(c, c)
+    c2_depth1 = cc.Rescale(c2_depth2)
+    # x^4
+    c4_depth2 = cc.EvalMult(c2_depth1, c2_depth1)
+    c4_depth1 = cc.Rescale(c4_depth2)
+    # x^8
+    c8_depth2 = cc.EvalMult(c4_depth1, c4_depth1)
+    c8_depth1 = cc.Rescale(c8_depth2)
+    # x^16
+    c16_depth2 = cc.EvalMult(c8_depth1, c8_depth1)
+    c16_depth1 = cc.Rescale(c16_depth2)
+    # x^9
+    c9_depth2 = cc.EvalMult(c8_depth1, c)
+    # x^18
+    c18_depth2 = cc.EvalMult(c16_depth1, c2_depth1)
+    # Final result
+    c_res_depth2 = cc.EvalAdd(cc.EvalAdd(c18_depth2, c9_depth2), 1.0)
+    c_res_depth1 = cc.Rescale(c_res_depth2)
+
+    result = cc.Decrypt(c_res_depth1,keys.secretKey)
+    result.SetLength(batch_size)
+    print("x^18 + x^9 + 1 = ", result)
+
+def hybrid_key_switching_demo1():
+    
+    print("\n\n\n ===== hybrid_key_switching_demo1 ============= \n")
+    dnum = 2
+    batch_size = 8
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(5)
+    parameters.SetScalingModSize(90)
+    parameters.SetBatchSize(batch_size)
+    parameters.SetScalingTechnique(ScalingTechnique.FIXEDAUTO)
+    parameters.SetNumLargeDigits(dnum)
+
+    cc = GenCryptoContext(parameters)
+
+    print(f"CKKS scheme is using ring dimension {cc.GetRingDimension()}\n")
+
+    print(f"- Using HYBRID key switching with {dnum} digits\n")
+
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+
+    keys = cc.KeyGen()
+    cc.EvalRotateKeyGen(keys.secretKey,[1,-2])
+
+    # Input
+    x = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7]
+    ptxt = cc.MakeCKKSPackedPlaintext(x)
+
+    print(f"Input x: {ptxt}")
+
+    c = cc.Encrypt(keys.publicKey,ptxt)
+
+    t = time.time()
+    c_rot1 = cc.EvalRotate(c,1)
+    c_rot2 = cc.EvalRotate(c_rot1,-2)
+    time2digits = time.time() - t
+
+    result = cc.Decrypt(c_rot2,keys.secretKey)
+    result.SetLength(batch_size)
+    print(f"x rotate by -1 = {result}")
+    print(f" - 2 rotations with HYBRID (2 digits) took {time2digits*1000} ms")
+
+
+def hybrid_key_switching_demo2():
+    print("\n\n\n ===== hybrid_key_switching_demo2 =============\n")
+    dnum = 3
+    batch_size = 8
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(5)
+    parameters.SetScalingModSize(90)
+    parameters.SetBatchSize(batch_size)
+    parameters.SetScalingTechnique(ScalingTechnique.FIXEDAUTO)
+    parameters.SetNumLargeDigits(dnum)
+
+    cc = GenCryptoContext(parameters)
+
+    # Compare the ring dimension in this demo to the one in the previous
+    print(f"CKKS scheme is using ring dimension {cc.GetRingDimension()}\n")
+
+    print(f"- Using HYBRID key switching with {dnum} digits\n")
+
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+
+    keys = cc.KeyGen()
+    cc.EvalRotateKeyGen(keys.secretKey,[1,-2])
+
+    # Input
+    x = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7]
+    ptxt = cc.MakeCKKSPackedPlaintext(x)
+
+    print(f"Input x: {ptxt}")
+
+    c = cc.Encrypt(keys.publicKey,ptxt)
+
+    t = time.time()
+    c_rot1 = cc.EvalRotate(c,1)
+    c_rot2 = cc.EvalRotate(c_rot1,-2)
+    time3digits = time.time() - t
+    # The runtime here is smaller than the previous demo
+
+    result = cc.Decrypt(c_rot2,keys.secretKey)
+    result.SetLength(batch_size)
+    print(f"x rotate by -1 = {result}")
+    print(f" - 2 rotations with HYBRID (3 digits) took {time3digits*1000} ms")
+
+def fast_rotation_demo1():
+    print("\n\n\n ===== fast_rotation_demo1 =============\n")
+    batch_size = 8
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(1)
+    parameters.SetScalingModSize(90)
+    parameters.SetBatchSize(batch_size)
+
+    cc = GenCryptoContext(parameters)
+
+    N = cc.GetRingDimension()
+    print(f"CKKS scheme is using ring dimension {N}\n")
+
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+
+    keys = cc.KeyGen()
+    cc.EvalRotateKeyGen(keys.secretKey,[1,2,3,4,5,6,7])
+
+    # Input
+    x = [0, 0, 0, 0, 0, 0, 0, 1]
+    ptxt = cc.MakeCKKSPackedPlaintext(x)
+
+    print(f"Input x: {ptxt}")
+
+    c = cc.Encrypt(keys.publicKey,ptxt)
+
+    # First, we perform 7 regular (non-hoisted) rotations
+    # and measure the runtime
+    t = time.time()
+    c_rot1 = cc.EvalRotate(c,1)
+    c_rot2 = cc.EvalRotate(c,2)
+    c_rot3 = cc.EvalRotate(c,3)
+    c_rot4 = cc.EvalRotate(c,4)
+    c_rot5 = cc.EvalRotate(c,5)
+    c_rot6 = cc.EvalRotate(c,6)
+    c_rot7 = cc.EvalRotate(c,7)
+    time_no_hoisting = time.time() - t
+
+    c_res_no_hoist = c + c_rot1 + c_rot2 + c_rot3 + c_rot4 + c_rot5 + c_rot6 + c_rot7
+
+    # M is the cyclotomic order and we need it to call EvalFastRotation
+    M = 2*N
+
+    # Then, we perform 7 rotations with hoisting.
+    t = time.time()
+    c_precomp = cc.EvalFastRotationPrecompute(c)
+    c_rot1 = cc.EvalFastRotation(c, 1, M, c_precomp)
+    c_rot2 = cc.EvalFastRotation(c, 2, M, c_precomp)
+    c_rot3 = cc.EvalFastRotation(c, 3, M, c_precomp)
+    c_rot4 = cc.EvalFastRotation(c, 4, M, c_precomp)
+    c_rot5 = cc.EvalFastRotation(c, 5, M, c_precomp)
+    c_rot6 = cc.EvalFastRotation(c, 6, M, c_precomp)
+    c_rot7 = cc.EvalFastRotation(c, 7, M, c_precomp)
+    time_hoisting = time.time() - t
+    # The time with hoisting should be faster than without hoisting.
+
+    c_res_hoist = c + c_rot1 + c_rot2 + c_rot3 + c_rot4 + c_rot5 + c_rot6 + c_rot7
+    
+    result = cc.Decrypt(c_res_no_hoist,keys.secretKey)
+    result.SetLength(batch_size)
+    print(f"Result without hoisting: {result}")
+    print(f" - 7 rotations without hoisting took {time_no_hoisting*1000} ms")
+
+    
+    result = cc.Decrypt(c_res_hoist,keys.secretKey)
+    result.SetLength(batch_size)
+    print(f"Result with hoisting: {result}")
+    print(f" - 7 rotations with hoisting took {time_hoisting*1000} ms")
+
+
+
+
+def fast_rotation_demo2():
+    print("\n\n\n ===== fast_rotation_demo2 =============\n")
+
+    batch_size = 8
+
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(1)
+    parameters.SetScalingModSize(90)
+    parameters.SetBatchSize(batch_size)
+    parameters.SetScalingTechnique(ScalingTechnique.FIXEDAUTO)
+    parameters.SetKeySwitchTechnique(KeySwitchTechnique.BV)
+
+    digit_size = 10
+    first_mod_size = 100
+    parameters.SetFirstModSize(first_mod_size)
+    parameters.SetDigitSize(digit_size)
+
+    cc = GenCryptoContext(parameters)
+
+    N = cc.GetRingDimension()
+    print(f"CKKS scheme is using ring dimension {N}\n")
+
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+
+    keys = cc.KeyGen()
+    cc.EvalRotateKeyGen(keys.secretKey,[1,2,3,4,5,6,7])
+
+    # Input
+    x = [0, 0, 0, 0, 0, 0, 0, 1]
+    ptxt = cc.MakeCKKSPackedPlaintext(x)
+
+    print(f"Input x: {ptxt}")
+
+    c = cc.Encrypt(keys.publicKey,ptxt)
+
+    # First, we perform 7 regular (non-hoisted) rotations
+    # and measure the runtime
+    t = time.time()
+    c_rot1 = cc.EvalRotate(c,1)
+    c_rot2 = cc.EvalRotate(c,2)
+    c_rot3 = cc.EvalRotate(c,3)
+    c_rot4 = cc.EvalRotate(c,4)
+    c_rot5 = cc.EvalRotate(c,5)
+    c_rot6 = cc.EvalRotate(c,6)
+    c_rot7 = cc.EvalRotate(c,7)
+    time_no_hoisting = time.time() - t
+
+    c_res_no_hoist = c + c_rot1 + c_rot2 + c_rot3 + c_rot4 + c_rot5 + c_rot6 + c_rot7
+
+    # M is the cyclotomic order and we need it to call EvalFastRotation
+    M = 2*N
+
+    # Then, we perform 7 rotations with hoisting.
+    t = time.time()
+    c_precomp = cc.EvalFastRotationPrecompute(c)
+    c_rot1 = cc.EvalFastRotation(c, 1, M, c_precomp)
+    c_rot2 = cc.EvalFastRotation(c, 2, M, c_precomp)
+    c_rot3 = cc.EvalFastRotation(c, 3, M, c_precomp)
+    c_rot4 = cc.EvalFastRotation(c, 4, M, c_precomp)
+    c_rot5 = cc.EvalFastRotation(c, 5, M, c_precomp)
+    c_rot6 = cc.EvalFastRotation(c, 6, M, c_precomp)
+    c_rot7 = cc.EvalFastRotation(c, 7, M, c_precomp)
+    time_hoisting = time.time() - t
+    # The time with hoisting should be faster than without hoisting.
+    # Also, the benefits from hoisting should be more pronounced in this
+    # case because we're using BV. Of course, we also observe less
+    # accurate results than when using HYBRID, because of using
+    # digitSize = 10 (Users can decrease digitSize to see the accuracy
+    # increase, and performance decrease).
+
+    c_res_hoist = c + c_rot1 + c_rot2 + c_rot3 + c_rot4 + c_rot5 + c_rot6 + c_rot7
+
+    result = cc.Decrypt(c_res_no_hoist,keys.secretKey)
+    result.SetLength(batch_size)
+    print(f"Result without hoisting: {result}")
+    print(f" - 7 rotations without hoisting took {time_no_hoisting*1000} ms")
+
+    result = cc.Decrypt(c_res_hoist,keys.secretKey)
+    result.SetLength(batch_size)
+    print(f"Result with hoisting: {result}")
+    print(f" - 7 rotations with hoisting took {time_hoisting*1000} ms")
+
+
+def main():
+    if get_native_int() == 128:
+        automatic_rescale_demo(ScalingTechnique.FIXEDAUTO)
+        # Note that FLEXIBLEAUTO is not supported for 128-bit CKKS
+        manual_rescale_demo(ScalingTechnique.FIXEDMANUAL)
+        hybrid_key_switching_demo1()
+        hybrid_key_switching_demo2()
+        fast_rotation_demo1()
+        fast_rotation_demo2()
+    else:
+        print("This demo only runs for 128-bit CKKS.\nIf you want to test it please reinstall the OpenFHE C++ with the flag -DNATIVE_INT=128, then reinstall OpenFHE-Python.")
+
+if __name__ == "__main__":
+    main()
+

+ 374 - 0
src/pke/examples/advanced-real-numbers.py

@@ -0,0 +1,374 @@
+from openfhe import *
+import time # to enable TIC-TOC timing measurements
+
+def automatic_rescale_demo(scal_tech):
+    if(scal_tech == ScalingTechnique.FLEXIBLEAUTO):
+        print("\n\n\n ===== FlexibleAutoDemo =============\n") 
+    else:
+         print("\n\n\n ===== FixedAutoDemo =============\n")
+
+    batch_size = 8
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(5)
+    parameters.SetScalingModSize(50)
+    parameters.SetScalingTechnique(scal_tech)
+    parameters.SetBatchSize(batch_size)
+
+    cc = GenCryptoContext(parameters)
+
+    print(f"CKKS scheme is using ring dimension {cc.GetRingDimension()}\n")
+
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+
+    keys = cc.KeyGen()
+    cc.EvalMultKeyGen(keys.secretKey)
+
+    # Input
+    x = [1.0, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07]
+    ptxt = cc.MakeCKKSPackedPlaintext(x)
+
+    print(f"Input x: {ptxt}")
+
+    c = cc.Encrypt(keys.publicKey,ptxt)
+
+    # Computing f(x) = x^18 + x^9 + 1
+    #
+    # In the following we compute f(x) with a computation
+    # that has a multiplicative depth of 5.
+    #
+    # The result is correct, even though there is no call to
+    # the Rescale() operation.
+
+    c2 = cc.EvalMult(c, c)                       # x^2
+    c4 = cc.EvalMult(c2, c2)                     # x^4
+    c8 = cc.EvalMult(c4, c4)                     # x^8
+    c16 = cc.EvalMult(c8, c8)                    # x^16
+    c9 = cc.EvalMult(c8, c)                      # x^9
+    c18 = cc.EvalMult(c16, c2)                   # x^18
+    cRes = cc.EvalAdd(cc.EvalAdd(c18, c9), 1.0)  # Final result
+
+    result = cc.Decrypt(cRes,keys.secretKey)
+    print("x^18 + x^9 + 1 = ", result)
+    result.SetLength(batch_size)
+    print(f"Result: {result}")
+
+def manual_rescale_demo(scal_tech):
+    print("\n\n\n ===== FixedManualDemo =============\n")
+    
+    batch_size = 8
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(5)
+    parameters.SetScalingModSize(50)
+    parameters.SetBatchSize(batch_size)
+
+    cc = GenCryptoContext(parameters)
+
+    print(f"CKKS scheme is using ring dimension {cc.GetRingDimension()}\n")
+    
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+
+    keys = cc.KeyGen()
+    cc.EvalMultKeyGen(keys.secretKey)
+
+    # Input
+    x = [1.0, 1.01, 1.02, 1.03, 1.04, 1.05, 1.06, 1.07]
+    ptxt = cc.MakeCKKSPackedPlaintext(x)
+
+    print(f"Input x: {ptxt}")
+
+    c = cc.Encrypt(keys.publicKey,ptxt)
+
+    # Computing f(x) = x^18 + x^9 + 1
+    #
+    # Compare the following with the corresponding code
+    # for FLEXIBLEAUTO. Here we need to track the depth of ciphertexts
+    # and call Rescale whenever needed. In this instance it's still
+    # not hard to do so, but this can be quite tedious in other
+    # complicated computations. (e.g. in bootstrapping)
+    #
+    #
+
+    # x^2
+    c2_depth2 = cc.EvalMult(c, c)
+    c2_depth1 = cc.Rescale(c2_depth2)
+    # x^4
+    c4_depth2 = cc.EvalMult(c2_depth1, c2_depth1)
+    c4_depth1 = cc.Rescale(c4_depth2)
+    # x^8
+    c8_depth2 = cc.EvalMult(c4_depth1, c4_depth1)
+    c8_depth1 = cc.Rescale(c8_depth2)
+    # x^16
+    c16_depth2 = cc.EvalMult(c8_depth1, c8_depth1)
+    c16_depth1 = cc.Rescale(c16_depth2)
+    # x^9
+    c9_depth2 = cc.EvalMult(c8_depth1, c)
+    # x^18
+    c18_depth2 = cc.EvalMult(c16_depth1, c2_depth1)
+    # Final result
+    cRes_depth2 = cc.EvalAdd(cc.EvalAdd(c18_depth2, c9_depth2), 1.0)
+    cRes_depth1 = cc.Rescale(cRes_depth2)
+
+    result = cc.Decrypt(cRes_depth1,keys.secretKey)
+    result.SetLength(batch_size)
+    print("x^18 + x^9 + 1 = ", result)
+
+def hybrid_key_switching_demo1():
+    print("\n\n\n ===== hybrid_key_switching_demo1 =============\n")
+    dnum = 2
+    batch_size = 8
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(5)
+    parameters.SetScalingModSize(50)
+    parameters.SetBatchSize(batch_size)
+    parameters.SetScalingTechnique(ScalingTechnique.FLEXIBLEAUTO)
+    parameters.SetNumLargeDigits(dnum)
+
+    cc = GenCryptoContext(parameters)
+
+    print(f"CKKS scheme is using ring dimension {cc.GetRingDimension()}\n")
+
+    print(f"- Using HYBRID key switching with {dnum} digits\n")
+
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+
+    keys = cc.KeyGen()
+    cc.EvalRotateKeyGen(keys.secretKey,[1,-2])
+
+    # Input
+    x = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7]
+    ptxt = cc.MakeCKKSPackedPlaintext(x)
+
+    print(f"Input x: {ptxt}")
+
+    c = cc.Encrypt(keys.publicKey,ptxt)
+
+    t = time.time()
+    c_rot1 = cc.EvalRotate(c,1)
+    c_rot2 = cc.EvalRotate(c_rot1,-2)
+    time2digits = time.time() - t
+
+    result = cc.Decrypt(c_rot2,keys.secretKey)
+    result.SetLength(batch_size)
+    print(f"x rotate by -1 = {result}")
+    print(f" - 2 rotations with HYBRID (2 digits) took {time2digits*1000} ms")
+
+
+def hybrid_key_switching_demo2():
+    print("\n\n\n ===== hybrid_key_switching_demo2 =============\n")
+    dnum = 3
+    batch_size = 8
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(5)
+    parameters.SetScalingModSize(50)
+    parameters.SetBatchSize(batch_size)
+    parameters.SetScalingTechnique(ScalingTechnique.FLEXIBLEAUTO)
+    parameters.SetNumLargeDigits(dnum)
+
+    cc = GenCryptoContext(parameters)
+
+    # Compare the ring dimension in this demo to the one in the previous
+    print(f"CKKS scheme is using ring dimension {cc.GetRingDimension()}\n")
+
+    print(f"- Using HYBRID key switching with {dnum} digits\n")
+
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+
+    keys = cc.KeyGen()
+    cc.EvalRotateKeyGen(keys.secretKey,[1,-2])
+
+    # Input
+    x = [1.0, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7]
+    ptxt = cc.MakeCKKSPackedPlaintext(x)
+
+    print(f"Input x: {ptxt}")
+
+    c = cc.Encrypt(keys.publicKey,ptxt)
+
+    t = time.time()
+    c_rot1 = cc.EvalRotate(c,1)
+    c_rot2 = cc.EvalRotate(c_rot1,-2)
+    time3digits = time.time() - t
+    # The runtime here is smaller than the previous demo
+
+    result = cc.Decrypt(c_rot2,keys.secretKey)
+    result.SetLength(batch_size)
+    print(f"x rotate by -1 = {result}")
+    print(f" - 2 rotations with HYBRID (3 digits) took {time3digits*1000} ms")
+
+def fast_rotation_demo1():
+    print("\n\n\n ===== fast_rotation_demo1 =============\n")
+    batch_size = 8
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(5)
+    parameters.SetScalingModSize(50)
+    parameters.SetBatchSize(batch_size)
+
+    cc = GenCryptoContext(parameters)
+
+    N = cc.GetRingDimension()
+    print(f"CKKS scheme is using ring dimension {N}\n")
+
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+
+    keys = cc.KeyGen()
+    cc.EvalRotateKeyGen(keys.secretKey,[1,2,3,4,5,6,7])
+
+    # Input
+    x = [0, 0, 0, 0, 0, 0, 0, 1]
+    ptxt = cc.MakeCKKSPackedPlaintext(x)
+
+    print(f"Input x: {ptxt}")
+
+    c = cc.Encrypt(keys.publicKey,ptxt)
+
+    # First, we perform 7 regular (non-hoisted) rotations
+    # and measure the runtime
+    t = time.time()
+    c_rot1 = cc.EvalRotate(c,1)
+    c_rot2 = cc.EvalRotate(c,2)
+    c_rot3 = cc.EvalRotate(c,3)
+    c_rot4 = cc.EvalRotate(c,4)
+    c_rot5 = cc.EvalRotate(c,5)
+    c_rot6 = cc.EvalRotate(c,6)
+    c_rot7 = cc.EvalRotate(c,7)
+    time_no_hoisting = time.time() - t
+
+    c_res_no_hoist = c + c_rot1 + c_rot2 + c_rot3 + c_rot4 + c_rot5 + c_rot6 + c_rot7
+
+    # M is the cyclotomic order and we need it to call EvalFastRotation
+    M = 2*N
+
+    # Then, we perform 7 rotations with hoisting.
+    t = time.time()
+    c_precomp = cc.EvalFastRotationPrecompute(c)
+    c_rot1 = cc.EvalFastRotation(c,1,M,c_precomp)
+    c_rot2 = cc.EvalFastRotation(c,2,M,c_precomp)
+    c_rot3 = cc.EvalFastRotation(c,3,M,c_precomp)
+    c_rot4 = cc.EvalFastRotation(c,4,M,c_precomp)
+    c_rot5 = cc.EvalFastRotation(c,5,M,c_precomp)
+    c_rot6 = cc.EvalFastRotation(c,6,M,c_precomp)
+    c_rot7 = cc.EvalFastRotation(c,7,M,c_precomp)
+    time_hoisting = time.time() - t
+    # The time with hoisting should be faster than without hoisting.
+
+    c_res_hoist = c + c_rot1 + c_rot2 + c_rot3 + c_rot4 + c_rot5 + c_rot6 + c_rot7
+    
+    result = cc.Decrypt(c_res_no_hoist,keys.secretKey)
+    result.SetLength(batch_size)
+    print(f"Result without hoisting: {result}")
+    print(f" - 7 rotations without hoisting took {time_no_hoisting*1000} ms")
+
+    
+    result = cc.Decrypt(c_res_hoist,keys.secretKey)
+    result.SetLength(batch_size)
+    print(f"Result with hoisting: {result}")
+    print(f" - 7 rotations with hoisting took {time_hoisting*1000} ms")
+
+
+
+
+def fast_rotation_demo2():
+    print("\n\n\n ===== fast_rotation_demo2 =============\n")
+
+    digit_size = 3
+    batch_size = 8
+
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(1)
+    parameters.SetScalingModSize(50)
+    parameters.SetBatchSize(batch_size)
+    parameters.SetScalingTechnique(ScalingTechnique.FLEXIBLEAUTO)
+    parameters.SetKeySwitchTechnique(KeySwitchTechnique.BV)
+    parameters.SetFirstModSize(60)
+    parameters.SetDigitSize(digit_size)
+
+    cc = GenCryptoContext(parameters)
+
+    N = cc.GetRingDimension()
+    print(f"CKKS scheme is using ring dimension {N}\n")
+
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+
+    keys = cc.KeyGen()
+    cc.EvalRotateKeyGen(keys.secretKey,[1,2,3,4,5,6,7])
+
+    # Input
+    x = [0, 0, 0, 0, 0, 0, 0, 1]
+    ptxt = cc.MakeCKKSPackedPlaintext(x)
+
+    print(f"Input x: {ptxt}")
+
+    c = cc.Encrypt(keys.publicKey,ptxt)
+
+    # First, we perform 7 regular (non-hoisted) rotations
+    # and measure the runtime
+    t = time.time()
+    c_rot1 = cc.EvalRotate(c,1)
+    c_rot2 = cc.EvalRotate(c,2)
+    c_rot3 = cc.EvalRotate(c,3)
+    c_rot4 = cc.EvalRotate(c,4)
+    c_rot5 = cc.EvalRotate(c,5)
+    c_rot6 = cc.EvalRotate(c,6)
+    c_rot7 = cc.EvalRotate(c,7)
+    time_no_hoisting = time.time() - t
+
+    c_res_no_hoist = c + c_rot1 + c_rot2 + c_rot3 + c_rot4 + c_rot5 + c_rot6 + c_rot7
+
+    # M is the cyclotomic order and we need it to call EvalFastRotation
+    M = 2*N
+
+    # Then, we perform 7 rotations with hoisting.
+    t = time.time()
+    c_precomp = cc.EvalFastRotationPrecompute(c)
+    c_rot1 = cc.EvalFastRotation(c,1,M,c_precomp)
+    c_rot2 = cc.EvalFastRotation(c,2,M,c_precomp)
+    c_rot3 = cc.EvalFastRotation(c,3,M,c_precomp)
+    c_rot4 = cc.EvalFastRotation(c,4,M,c_precomp)
+    c_rot5 = cc.EvalFastRotation(c,5,M,c_precomp)
+    c_rot6 = cc.EvalFastRotation(c,6,M,c_precomp)
+    c_rot7 = cc.EvalFastRotation(c,7,M,c_precomp)
+    time_hoisting = time.time() - t
+    # The time with hoisting should be faster than without hoisting.
+    # Also, the benefits from hoisting should be more pronounced in this
+    # case because we're using BV. Of course, we also observe less
+    # accurate results than when using HYBRID, because of using
+    # digitSize = 10 (Users can decrease digitSize to see the accuracy
+    # increase, and performance decrease).
+
+    c_res_hoist = c + c_rot1 + c_rot2 + c_rot3 + c_rot4 + c_rot5 + c_rot6 + c_rot7
+
+    result = cc.Decrypt(c_res_no_hoist,keys.secretKey)
+    result.SetLength(batch_size)
+    print(f"Result without hoisting: {result}")
+    print(f" - 7 rotations without hoisting took {time_no_hoisting*1000} ms")
+
+    result = cc.Decrypt(c_res_no_hoist,keys.secretKey)
+    result.SetLength(batch_size)
+    print(f"Result with hoisting: {result}")
+    print(f" - 7 rotations with hoisting took {time_hoisting*1000} ms")
+
+
+def main():
+    automatic_rescale_demo(ScalingTechnique.FLEXIBLEAUTO)
+    automatic_rescale_demo(ScalingTechnique.FIXEDAUTO)
+    manual_rescale_demo(ScalingTechnique.FIXEDMANUAL)
+    hybrid_key_switching_demo1()
+    hybrid_key_switching_demo2()
+    fast_rotation_demo1()
+    fast_rotation_demo2()
+
+if __name__ == "__main__":
+    main()
+

+ 98 - 0
src/pke/examples/function-evaluation.py

@@ -0,0 +1,98 @@
+from openfhe import *
+import math
+
+def main():
+    eval_logistic_example()
+    eval_function_example()
+
+def eval_logistic_example():
+    print("--------------------------------- EVAL LOGISTIC FUNCTION ---------------------------------\n")
+    parameters = CCParamsCKKSRNS()
+    parameters.SetSecurityLevel(SecurityLevel.HEStd_NotSet)
+    parameters.SetRingDim(1 << 10)
+
+    scaling_mod_size = 59
+    first_mod_size = 60
+
+    parameters.SetScalingModSize(scaling_mod_size)
+    parameters.SetFirstModSize(first_mod_size)
+
+    poly_degree = 16
+    mult_depth = 6
+
+    parameters.SetMultiplicativeDepth(mult_depth)
+    cc = GenCryptoContext(parameters)
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+    cc.Enable(PKESchemeFeature.ADVANCEDSHE)
+
+    key_pair = cc.KeyGen()
+    cc.EvalMultKeyGen(key_pair.secretKey)
+
+    input = [-4, -3, -2, -1, 0, 1, 2, 3, 4]
+    encoded_length = len(input)
+    plaintext = cc.MakeCKKSPackedPlaintext(input)
+    ciphertext = cc.Encrypt(key_pair.publicKey, plaintext)
+
+    lower_bound = -4
+    upper_bound = 4
+    result = cc.EvalLogistic(ciphertext, lower_bound, upper_bound, poly_degree)
+
+    plaintext_dec = cc.Decrypt(result, key_pair.secretKey)
+    plaintext_dec.SetLength(encoded_length)
+
+    expected_output = [0.0179885, 0.0474289, 0.119205, 0.268936, 0.5, 0.731064, 0.880795, 0.952571, 0.982011]
+    print(f"Expected output\n\t {expected_output}\n")
+
+    final_result = plaintext_dec.GetCKKSPackedValue()
+    print(f"Actual output\n\t {final_result}\n")
+
+def eval_function_example():
+    print("--------------------------------- EVAL SQUARE ROOT FUNCTION ---------------------------------\n")
+    parameters = CCParamsCKKSRNS()
+    parameters.SetSecurityLevel(SecurityLevel.HEStd_NotSet)
+    parameters.SetRingDim(1 << 10)
+
+    if get_native_int() == 128:
+        scaling_mod_size = 78
+        first_mod_size = 89
+    else:
+        scaling_mod_size = 50
+        first_mod_size = 60
+
+    parameters.SetScalingModSize(scaling_mod_size)
+    parameters.SetFirstModSize(first_mod_size)
+
+    poly_degree = 50
+    mult_depth = 7
+
+    parameters.SetMultiplicativeDepth(mult_depth)
+    cc = GenCryptoContext(parameters)
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+    cc.Enable(PKESchemeFeature.ADVANCEDSHE)
+
+    key_pair = cc.KeyGen()
+    cc.EvalMultKeyGen(key_pair.secretKey)
+
+    input = [1, 2, 3, 4, 5, 6, 7, 8, 9]
+    encoded_length = len(input)
+    plaintext = cc.MakeCKKSPackedPlaintext(input)
+    ciphertext = cc.Encrypt(key_pair.publicKey, plaintext)
+
+    lower_bound = 0
+    upper_bound = 10
+    result = cc.EvalChebyshevFunction(math.sqrt,ciphertext, lower_bound, upper_bound, poly_degree)
+
+    plaintext_dec = cc.Decrypt(result, key_pair.secretKey)
+    plaintext_dec.SetLength(encoded_length)
+
+    expected_output = [1, 1.414213, 1.732050, 2, 2.236067, 2.449489, 2.645751, 2.828427, 3]
+    print(f"Expected output\n\t {expected_output}\n")
+
+    final_result = plaintext_dec.GetCKKSPackedValue()
+    print(f"Actual output\n\t {final_result}\n")
+if __name__ == "__main__":
+    main()

+ 113 - 0
src/pke/examples/iterative-ckks-bootstrapping.py

@@ -0,0 +1,113 @@
+from openfhe import *
+import math
+import random
+
+def main():
+    iterative_bootstrap_example()
+
+def calculate_approximation_error(result,expected_result):
+    if len(result) != len(expected_result):
+        raise Exception("Cannot compare vectors with different numbers of elements")
+    # using the infinity norm
+    # error is abs of the difference of real parts
+    max_error = max([abs(el1.real - el2.real) for (el1, el2) in zip(result, expected_result)])
+    # return absolute value of log base2 of the error
+    return abs(math.log(max_error,2))
+def iterative_bootstrap_example():
+    # Step 1: Set CryptoContext
+    parameters = CCParamsCKKSRNS()
+    secret_key_dist = SecretKeyDist.UNIFORM_TERNARY
+    parameters.SetSecretKeyDist(secret_key_dist)
+    parameters.SetSecurityLevel(SecurityLevel.HEStd_NotSet)
+    parameters.SetRingDim(1 << 12)
+
+    rescale_tech = ScalingTechnique.FLEXIBLEAUTO
+    dcrt_bits = 59
+    first_mod = 60
+
+    parameters.SetScalingModSize(dcrt_bits)
+    parameters.SetScalingTechnique(rescale_tech)
+    parameters.SetFirstModSize(first_mod)
+
+    # Here, we specify the number of iterations to run bootstrapping. 
+    # Note that we currently only support 1 or 2 iterations.
+    # Two iterations should give us approximately double the precision of one iteration.
+    num_iterations = 2
+
+    level_budget = [3, 3]
+    # Each extra iteration on top of 1 requires an extra level to be consumed.
+    approx_bootstrapp_depth = 8 + (num_iterations - 1)
+    bsgs_dim = [0,0]
+
+    levels_used_before_bootstrap = 10
+    depth = levels_used_before_bootstrap + FHECKKSRNS.GetBootstrapDepth(approx_bootstrapp_depth, level_budget, secret_key_dist)
+    parameters.SetMultiplicativeDepth(depth)
+
+    # Generate crypto context
+    cryptocontext = GenCryptoContext(parameters)
+
+    # Enable features that you wish to use. Note, we must enable FHE to use bootstrapping.
+
+    cryptocontext.Enable(PKESchemeFeature.PKE)
+    cryptocontext.Enable(PKESchemeFeature.KEYSWITCH)
+    cryptocontext.Enable(PKESchemeFeature.LEVELEDSHE)
+    cryptocontext.Enable(PKESchemeFeature.ADVANCEDSHE)
+    cryptocontext.Enable(PKESchemeFeature.FHE)
+
+    ring_dim = cryptocontext.GetRingDimension()
+    print(f"CKKS is using ring dimension {ring_dim}\n\n")
+
+    # Step 2: Precomputations for bootstrapping
+    # We use a sparse packing
+    num_slots = 8
+    cryptocontext.EvalBootstrapSetup(level_budget, bsgs_dim, num_slots)
+
+    # Step 3: Key generation
+    key_pair = cryptocontext.KeyGen()
+    cryptocontext.EvalMultKeyGen(key_pair.secretKey)
+    # Generate bootstrapping keys.
+    cryptocontext.EvalBootstrapKeyGen(key_pair.secretKey, num_slots)
+
+    # Step 4: Encoding and encryption of inputs
+    # Generate random input
+    x = [random.uniform(0, 1) for i in range(num_slots)]
+
+    """ Encoding as plaintexts
+        We specify the number of slots as num_slots to achieve a performance improvement.
+        We use the other default values of depth 1, levels 0, and no params.
+        Alternatively, you can also set batch size as a parameter in the CryptoContext as follows:
+        parameters.SetBatchSize(num_slots);
+        Here, we assume all ciphertexts in the cryptoContext will have num_slots slots.
+        We start with a depleted ciphertext that has used up all of its levels."""
+    ptxt = cryptocontext.MakeCKKSPackedPlaintext(x, 1, depth -1,None,num_slots)
+
+    # Encrypt the encoded vectors
+    ciph = cryptocontext.Encrypt(key_pair.publicKey, ptxt)
+
+    # Step 5: Measure the precision of a single bootstrapping operation.
+    ciphertext_after = cryptocontext.EvalBootstrap(ciph)
+
+    result = cryptocontext.Decrypt(ciphertext_after,key_pair.secretKey)
+    result.SetLength(num_slots)
+    precision = calculate_approximation_error(result.GetCKKSPackedValue(),ptxt.GetCKKSPackedValue())
+    print(f"Bootstrapping precision after 1 iteration: {precision} bits\n")
+
+    # Set the precision equal to empirically measured value after many test runs.
+    precision = 17
+    print(f"Precision input to algorithm: {precision}\n")
+
+    # Step 6: Run bootstrapping with multiple iterations
+    ciphertext_two_iterations = cryptocontext.EvalBootstrap(ciph,num_iterations,precision)
+
+    result_two_iterations = cryptocontext.Decrypt(ciphertext_two_iterations,key_pair.secretKey)
+    result_two_iterations.SetLength(num_slots)
+    actual_result = result_two_iterations.GetCKKSPackedValue()
+
+    print(f"Output after two interations of bootstrapping: {actual_result}\n")
+    precision_multiple_iterations = calculate_approximation_error(actual_result,ptxt.GetCKKSPackedValue())
+
+    print(f"Bootstrapping precision after 2 iterations: {precision_multiple_iterations} bits\n")
+    print(f"Number of levels remaining after 2 bootstrappings: {depth - ciphertext_two_iterations.GetLevel()}\n")
+
+if __name__ == "__main__":
+    main()

+ 70 - 0
src/pke/examples/polynomial-evaluation.py

@@ -0,0 +1,70 @@
+from openfhe import *
+import time
+
+def main():
+
+    print("\n======EXAMPLE FOR EVALPOLY========\n")
+    parameters = CCParamsCKKSRNS()
+    parameters.SetMultiplicativeDepth(6)
+    parameters.SetScalingModSize(50)
+
+    cc = GenCryptoContext(parameters)
+    cc.Enable(PKESchemeFeature.PKE)
+    cc.Enable(PKESchemeFeature.KEYSWITCH)
+    cc.Enable(PKESchemeFeature.LEVELEDSHE)
+    cc.Enable(PKESchemeFeature.ADVANCEDSHE)
+
+    input = [complex(a,0) for a in [0.5, 0.7, 0.9, 0.95, 0.93]]
+    # input = [0.5, 0.7, 0.9, 0.95, 0.93]
+    encoded_length = len(input)
+    coefficients1 = [0.15, 0.75, 0, 1.25, 0, 0, 1, 0, 1, 2, 0, 1, 0, 0, 0, 0, 1]
+    coefficients2 = [1, 2, 3, 4, 5, -1, -2, -3, -4, -5,
+                    0.1, 0.2, 0.3, 0.4, 0.5, -0.1, -0.2, -0.3, -0.4, -0.5,
+                    0.1, 0.2, 0.3, 0.4, 0.5, -0.1, -0.2, -0.3, -0.4, -0.5]
+    plaintext1 = cc.MakeCKKSPackedPlaintext(input)
+
+    key_pair = cc.KeyGen()
+    
+    print("Generating evaluation key for homomorphic multiplication...")
+    cc.EvalMultKeyGen(key_pair.secretKey)
+    print("Completed.\n")
+
+    ciphertext1 = cc.Encrypt(key_pair.publicKey, plaintext1)
+
+    t = time.time()
+    result = cc.EvalPoly(ciphertext1, coefficients1)
+    time_eval_poly1 = time.time() - t
+
+    t = time.time()
+    result2 = cc.EvalPoly(ciphertext1, coefficients2)
+    time_eval_poly2 = time.time() - t
+
+    plaintext_dec = cc.Decrypt(result, key_pair.secretKey)
+
+    plaintext_dec.SetLength(encoded_length)
+
+    plaintext_dec2 = cc.Decrypt(result2, key_pair.secretKey)
+
+    plaintext_dec2.SetLength(encoded_length)
+
+    print("\n Original Plaintext #1: \n")
+    print(plaintext1)
+
+    print(f"\n Result of evaluating a polynomial with coefficients {coefficients1}: \n")
+    print(plaintext_dec)
+
+    print("\n Expected result: (0.70519107, 1.38285078, 3.97211180, "
+                 "5.60215665, 4.86357575) \n") 
+
+    print(f"\n Evaluation time: {time_eval_poly1*1000} ms \n")
+
+    print(f"\n Result of evaluating a polynomial with coefficients {coefficients2}: \n")
+    print(plaintext_dec2)  
+
+    print("\n Expected result: (3.4515092326, 5.3752765397, 4.8993108833, "
+                 "3.2495023573, 4.0485229982) \n")
+
+    print(f"\n Evaluation time: {time_eval_poly2*1000} ms \n")
+
+if __name__ == '__main__':
+    main() 

+ 72 - 0
src/pke/examples/simple-ckks-bootstrapping.py

@@ -0,0 +1,72 @@
+from openfhe import *
+
+def main():
+    simple_bootstrap_example()
+
+def simple_bootstrap_example():
+    parameters = CCParamsCKKSRNS()
+
+    secret_key_dist = SecretKeyDist.UNIFORM_TERNARY
+    parameters.SetSecretKeyDist(secret_key_dist)
+
+    parameters.SetSecurityLevel(SecurityLevel.HEStd_NotSet)
+    parameters.SetRingDim(1<<12)
+
+    rescale_tech = ScalingTechnique.FLEXIBLEAUTO
+    dcrt_bits = 59
+    first_mod = 60
+    
+    parameters.SetScalingModSize(dcrt_bits)
+    parameters.SetScalingTechnique(rescale_tech)
+    parameters.SetFirstModSize(first_mod)
+
+    level_budget = [4, 4]
+    approx_bootstrapp_depth = 8
+
+    levels_used_before_bootstrap = 10
+
+    depth = levels_used_before_bootstrap + FHECKKSRNS.GetBootstrapDepth(approx_bootstrapp_depth, level_budget, secret_key_dist)
+
+    parameters.SetMultiplicativeDepth(depth)
+
+    cryptocontext = GenCryptoContext(parameters)
+    cryptocontext.Enable(PKESchemeFeature.PKE)
+    cryptocontext.Enable(PKESchemeFeature.KEYSWITCH)
+    cryptocontext.Enable(PKESchemeFeature.LEVELEDSHE)
+    cryptocontext.Enable(PKESchemeFeature.ADVANCEDSHE)
+    cryptocontext.Enable(PKESchemeFeature.FHE)
+
+    ring_dim = cryptocontext.GetRingDimension()
+    # This is the mazimum number of slots that can be used full packing.
+
+    num_slots = int(ring_dim / 2)
+    print(f"CKKS is using ring dimension {ring_dim}")
+
+    cryptocontext.EvalBootstrapSetup(level_budget)
+
+    key_pair = cryptocontext.KeyGen()
+    cryptocontext.EvalMultKeyGen(key_pair.secretKey)
+    cryptocontext.EvalBootstrapKeyGen(key_pair.secretKey, num_slots)
+
+    x = [0.25, 0.5, 0.75, 1.0, 2.0, 3.0, 4.0, 5.0]
+    encoded_length = len(x)
+
+    ptxt = cryptocontext.MakeCKKSPackedPlaintext(x)
+    ptxt.SetLength(encoded_length)
+
+    print(f"Input: {x}")
+
+    ciph = cryptocontext.Encrypt(key_pair.publicKey, ptxt)
+
+    print(f"Initial number of levels remaining: {ciph.GetLevel()}")
+
+    ciphertext_after = cryptocontext.EvalBootstrap(ciph)
+
+    print(f"Number of levels remaining after bootstrapping: {ciphertext_after.GetLevel()}")
+
+    result = cryptocontext.Decrypt(ciphertext_after,key_pair.secretKey)
+    result.SetLength(encoded_length)
+    print(f"Output after bootstrapping: {result}")
+
+if __name__ == '__main__':
+    main()

+ 94 - 0
src/pke/examples/simple-integers-bgvrns.py

@@ -0,0 +1,94 @@
+# Initial Settings
+from openfhe import *
+# import openfhe.PKESchemeFeature as Feature
+
+
+# Sample Program: Step 1: Set CryptoContext
+parameters = CCParamsBGVRNS()
+parameters.SetPlaintextModulus(65537)
+parameters.SetMultiplicativeDepth(2)
+
+crypto_context = GenCryptoContext(parameters)
+# Enable features that you wish to use
+crypto_context.Enable(PKESchemeFeature.PKE)
+crypto_context.Enable(PKESchemeFeature.KEYSWITCH)
+crypto_context.Enable(PKESchemeFeature.LEVELEDSHE)
+
+# Sample Program: Step 2: Key Generation
+
+# Generate a public/private key pair
+key_pair = crypto_context.KeyGen()
+
+# Generate the relinearization key
+crypto_context.EvalMultKeyGen(key_pair.secretKey)
+
+# Generate the rotation evaluation keys
+crypto_context.EvalRotateKeyGen(key_pair.secretKey, [1, 2, -1, -2])
+
+# Sample Program: Step 3: Encryption
+
+# First plaintext vector is encoded
+vector_of_ints1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
+plaintext1 = crypto_context.MakePackedPlaintext(vector_of_ints1)
+
+# Second plaintext vector is encoded
+vector_of_ints2 = [3, 2, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12]
+plaintext2 = crypto_context.MakePackedPlaintext(vector_of_ints2)
+
+# Third plaintext vector is encoded
+vector_of_ints3 = [1, 2, 5, 2, 5, 6, 7, 8, 9, 10, 11, 12]
+plaintext3 = crypto_context.MakePackedPlaintext(vector_of_ints3)
+
+# The encoded vectors are encrypted
+ciphertext1 = crypto_context.Encrypt(key_pair.publicKey, plaintext1)
+ciphertext2 = crypto_context.Encrypt(key_pair.publicKey, plaintext2)
+ciphertext3 = crypto_context.Encrypt(key_pair.publicKey, plaintext3)
+
+#  Sample Program: Step 4: Evaluation
+
+# Homomorphic additions
+ciphertext_add12 = crypto_context.EvalAdd(ciphertext1, ciphertext2)
+ciphertext_add_result = crypto_context.EvalAdd(ciphertext_add12, ciphertext3)
+
+# Homomorphic Multiplication
+ciphertext_mult12 = crypto_context.EvalMult(ciphertext1, ciphertext2)
+ciphertext_mult_result = crypto_context.EvalMult(ciphertext_mult12, ciphertext3)
+
+# Homomorphic Rotations
+ciphertext_rot1 = crypto_context.EvalRotate(ciphertext1, 1)
+ciphertext_rot2 = crypto_context.EvalRotate(ciphertext1, 2)
+ciphertext_rot3 = crypto_context.EvalRotate(ciphertext1, -1)
+ciphertext_rot4 = crypto_context.EvalRotate(ciphertext1, -2)
+
+# Sample Program: Step 5: Decryption
+
+# Decrypt the result of additions
+plaintext_add_result = crypto_context.Decrypt(ciphertext_add_result,key_pair.secretKey)
+
+# Decrypt the result of multiplications
+plaintext_mult_result = crypto_context.Decrypt(ciphertext_mult_result,key_pair.secretKey)
+
+# Decrypt the result of rotations
+plaintextRot1 = crypto_context.Decrypt(ciphertext_rot1,key_pair.secretKey)
+plaintextRot2 = crypto_context.Decrypt(ciphertext_rot2,key_pair.secretKey)
+plaintextRot3 = crypto_context.Decrypt(ciphertext_rot3,key_pair.secretKey)
+plaintextRot4 = crypto_context.Decrypt(ciphertext_rot4,key_pair.secretKey)
+
+
+plaintextRot1.SetLength(len(vector_of_ints1))
+plaintextRot2.SetLength(len(vector_of_ints1))
+plaintextRot3.SetLength(len(vector_of_ints1))
+plaintextRot4.SetLength(len(vector_of_ints1))
+
+print("Plaintext #1: " + str(plaintext1))
+print("Plaintext #2: " + str(plaintext2))
+print("Plaintext #3: " + str(plaintext3))
+
+# Output Results
+print("\nResults of homomorphic computations")
+print("#1 + #2 + #3 = " + str(plaintext_add_result))
+print("#1 * #2 * #3 = " + str(plaintext_mult_result))
+print("Left rotation of #1 by 1 = " + str(plaintextRot1))
+print("Left rotation of #1 by 2 = " + str(plaintextRot2))
+print("Right rotation of #1 by 1 = " + str(plaintextRot3))
+print("Right rotation of #1 by 2 = " + str(plaintextRot4))

+ 168 - 0
src/pke/examples/simple-integers-serial-bgvrns.py

@@ -0,0 +1,168 @@
+# Initial Settings
+from openfhe import *
+# import openfhe.PKESchemeFeature as Feature
+
+datafolder = 'demoData'
+
+print("This program requres the subdirectory `" + datafolder + "' to exist, otherwise you will get an error writing serializations.")
+
+# Sample Program: Step 1: Set CryptoContext
+parameters = CCParamsBGVRNS()
+parameters.SetPlaintextModulus(65537)
+parameters.SetMultiplicativeDepth(2)
+
+cryptoContext = GenCryptoContext(parameters)
+# Enable features that you wish to use
+cryptoContext.Enable(PKESchemeFeature.PKE)
+cryptoContext.Enable(PKESchemeFeature.KEYSWITCH)
+cryptoContext.Enable(PKESchemeFeature.LEVELEDSHE)
+
+# Serialize cryptocontext
+if not SerializeToFile(datafolder + "/cryptocontext.txt", cryptoContext, BINARY):
+   raise Exception("Error writing serialization of the crypto context to cryptocontext.txt")
+print("The cryptocontext has been serialized.")
+
+# Sample Program: Step 2: Key Generation
+
+# Generate a public/private key pair
+keypair = cryptoContext.KeyGen()
+print("The keypair has been generated.")
+
+# Serialize the public key
+if not SerializeToFile(datafolder + "/key-public.txt", keypair.publicKey, BINARY):
+   raise Exception("Error writing serialization of the public key to key-public.txt")
+print("The public key has been serialized.")
+
+# Serialize the secret key
+if not SerializeToFile(datafolder + "/key-secret.txt", keypair.secretKey, BINARY):
+   raise Exception("Error writing serialization of the secret key to key-secret.txt")
+print("The secret key has been serialized.")
+
+# Generate the relinearization key
+cryptoContext.EvalMultKeyGen(keypair.secretKey)
+print("The relinearization key has been generated.")
+
+# Serialize the relinearization key
+if not cryptoContext.SerializeEvalMultKey(datafolder + "/key-eval-mult.txt",BINARY):
+   raise Exception("Error writing serialization of the eval mult keys to \"key-eval-mult.txt\"")
+print("The relinearization key has been serialized.")
+
+# Generate the rotation evaluation keys
+cryptoContext.EvalRotateKeyGen(keypair.secretKey, [1, 2, -1, -2])
+print("The rotation evaluation keys have been generated.")
+
+# Serialize the rotation evaluation keys
+if not cryptoContext.SerializeEvalAutomorphismKey(datafolder + "/key-eval-rot.txt",BINARY):
+   raise Exception("Error writing serialization of the eval rotate keys to \"key-eval-rot.txt\"")
+print("The rotation evaluation keys have been serialized.")
+
+# Sample Program: Step 3: Encryption
+
+# First plaintext vector is encoded
+vectorOfInts1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
+plaintext1 = cryptoContext.MakePackedPlaintext(vectorOfInts1)
+
+# Second plaintext vector is encoded
+vectorOfInts2 = [3, 2, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12]
+plaintext2 = cryptoContext.MakePackedPlaintext(vectorOfInts2)
+
+# Third plaintext vector is encoded
+vectorOfInts3 = [1, 2, 5, 2, 5, 6, 7, 8, 9, 10, 11, 12]
+plaintext3 = cryptoContext.MakePackedPlaintext(vectorOfInts3)
+
+
+# The encoded vectors are encrypted
+ciphertext1 = cryptoContext.Encrypt(keypair.publicKey, plaintext1)
+ciphertext2 = cryptoContext.Encrypt(keypair.publicKey, plaintext2)
+ciphertext3 = cryptoContext.Encrypt(keypair.publicKey, plaintext3)
+print("The plaintexts have been encrypted.")
+
+if not SerializeToFile(datafolder + "/ciphertext1.txt", ciphertext1, BINARY):
+   raise Exception("Error writing serialization of ciphertext 1 to ciphertext1.txt")
+print("The first ciphertext has been serialized.")
+
+if not SerializeToFile(datafolder + "/ciphertext2.txt", ciphertext2, BINARY):
+   raise Exception("Error writing serialization of ciphertext2 to ciphertext2.txt")
+print("The second ciphertext has been serialized.")
+
+if not SerializeToFile(datafolder + "/ciphertext3.txt", ciphertext3, BINARY):   
+   raise Exception("Error writing serialization of ciphertext3 to ciphertext3.txt")
+print("The third ciphertext has been serialized.")
+
+# Sample Program: Step 4: Evaluation
+
+# OpenFHE maintains an internal map of CryptoContext objects which are
+# indexed by a tag and the tag is applied to both the CryptoContext and some
+# of the keys. When deserializing a context, OpenFHE checks for the tag and
+# if it finds it in the CryptoContext map, it will return the stored version.
+# Hence, we need to clear the context and clear the keys.
+cryptoContext.ClearEvalMultKeys()
+cryptoContext.ClearEvalAutomorphismKeys()
+ReleaseAllContexts()
+
+# Deserialize the crypto context
+cc = CryptoContext()
+
+if not DeserializeFromFile(datafolder + "/cryptocontext.txt", cc, BINARY):
+   raise Exception("Error reading serialization of the crypto context from cryptocontext.txt")
+print("The cryptocontext has been deserialized.")
+
+# Deserialize the public key
+pk = PublicKey()
+
+if not DeserializeFromFile(datafolder + "/key-public.txt", pk, BINARY):
+   raise Exception("Error reading serialization of the public key from key-public.txt")
+
+print("The public key has been deserialized.")
+
+# if cryptoContext.DeserializeEvalMultKey(datafolder + "/key-eval-mult.txt",BINARY):
+#    raise Exception("Could not deserialize the eval mult key file")
+
+# print("The relinearization key has been deserialized.")
+
+# if cryptoContext.DeserializeEvalAutomorphismKey(datafolder + "/key-eval-rot.txt",BINARY):
+#    raise Exception("Could not deserialize the eval rotation key file")
+
+# print("Deserialized the eval rotation keys.")
+
+# Deserialize the ciphertexts
+
+ct1 = Ciphertext()
+ct2 = Ciphertext()
+ct3 = Ciphertext()
+
+if not DeserializeFromFile(datafolder + "/ciphertext1.txt", ct1, BINARY):
+    raise Exception("Could not read the ciphertext")
+
+print("The first ciphertext has been deserialized.")
+
+if not DeserializeFromFile(datafolder + "/ciphertext2.txt", ct2, BINARY):
+    raise Exception("Could not read the ciphertext")
+
+print("The second ciphertext has been deserialized.")
+
+if not DeserializeFromFile(datafolder + "/ciphertext3.txt", ct3, BINARY):   
+    raise Exception("Could not read the ciphertext")
+
+print("The third ciphertext has been deserialized.")
+
+# Homomorphic addition
+
+ciphertextAdd12 = cc.EvalAdd(ct1, ct2)
+ciphertextAddResult = cc.EvalAdd(ciphertextAdd12, ct3)
+
+# Homomorphic multiplication
+ciphertextMult12 = cc.EvalMult(ct1, ct2)
+ciphertextMultResult = cc.EvalMult(ciphertextMult12, ct3)
+
+# Homomorphic rotation
+ciphertextRot1 = cc.EvalRotate(ct1, 1)
+ciphertextRot2 = cc.EvalRotate(ct2, 2)
+ciphertextRot3 = cc.EvalRotate(ct3, -1)
+ciphertextRot4 = cc.EvalRotate(ct3, -2)
+
+
+
+
+
+

+ 168 - 0
src/pke/examples/simple-integers-serial.py

@@ -0,0 +1,168 @@
+# Initial Settings
+from openfhe import *
+# import openfhe.PKESchemeFeature as Feature
+
+datafolder = 'demoData'
+
+print("This program requres the subdirectory `" + datafolder + "' to exist, otherwise you will get an error writing serializations.")
+
+# Sample Program: Step 1: Set CryptoContext
+parameters = CCParamsBFVRNS()
+parameters.SetPlaintextModulus(65537)
+parameters.SetMultiplicativeDepth(2)
+
+cryptoContext = GenCryptoContext(parameters)
+# Enable features that you wish to use
+cryptoContext.Enable(PKESchemeFeature.PKE)
+cryptoContext.Enable(PKESchemeFeature.KEYSWITCH)
+cryptoContext.Enable(PKESchemeFeature.LEVELEDSHE)
+
+# Serialize cryptocontext
+if not SerializeToFile(datafolder + "/cryptocontext.txt", cryptoContext, BINARY):
+   raise Exception("Error writing serialization of the crypto context to cryptocontext.txt")
+print("The cryptocontext has been serialized.")
+
+# Sample Program: Step 2: Key Generation
+
+# Generate a public/private key pair
+keypair = cryptoContext.KeyGen()
+print("The keypair has been generated.")
+
+# Serialize the public key
+if not SerializeToFile(datafolder + "/key-public.txt", keypair.publicKey, BINARY):
+   raise Exception("Error writing serialization of the public key to key-public.txt")
+print("The public key has been serialized.")
+
+# Serialize the secret key
+if not SerializeToFile(datafolder + "/key-secret.txt", keypair.secretKey, BINARY):
+   raise Exception("Error writing serialization of the secret key to key-secret.txt")
+print("The secret key has been serialized.")
+
+# Generate the relinearization key
+cryptoContext.EvalMultKeyGen(keypair.secretKey)
+print("The relinearization key has been generated.")
+
+# Serialize the relinearization key
+if not cryptoContext.SerializeEvalMultKey(datafolder + "/key-eval-mult.txt",BINARY):
+   raise Exception("Error writing serialization of the eval mult keys to \"key-eval-mult.txt\"")
+print("The relinearization key has been serialized.")
+
+# Generate the rotation evaluation keys
+cryptoContext.EvalRotateKeyGen(keypair.secretKey, [1, 2, -1, -2])
+print("The rotation evaluation keys have been generated.")
+
+# Serialize the rotation evaluation keys
+if not cryptoContext.SerializeEvalAutomorphismKey(datafolder + "/key-eval-rot.txt",BINARY):
+   raise Exception("Error writing serialization of the eval rotate keys to \"key-eval-rot.txt\"")
+print("The rotation evaluation keys have been serialized.")
+
+# Sample Program: Step 3: Encryption
+
+# First plaintext vector is encoded
+vectorOfInts1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
+plaintext1 = cryptoContext.MakePackedPlaintext(vectorOfInts1)
+
+# Second plaintext vector is encoded
+vectorOfInts2 = [3, 2, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12]
+plaintext2 = cryptoContext.MakePackedPlaintext(vectorOfInts2)
+
+# Third plaintext vector is encoded
+vectorOfInts3 = [1, 2, 5, 2, 5, 6, 7, 8, 9, 10, 11, 12]
+plaintext3 = cryptoContext.MakePackedPlaintext(vectorOfInts3)
+
+
+# The encoded vectors are encrypted
+ciphertext1 = cryptoContext.Encrypt(keypair.publicKey, plaintext1)
+ciphertext2 = cryptoContext.Encrypt(keypair.publicKey, plaintext2)
+ciphertext3 = cryptoContext.Encrypt(keypair.publicKey, plaintext3)
+print("The plaintexts have been encrypted.")
+
+if not SerializeToFile(datafolder + "/ciphertext1.txt", ciphertext1, BINARY):
+   raise Exception("Error writing serialization of ciphertext 1 to ciphertext1.txt")
+print("The first ciphertext has been serialized.")
+
+if not SerializeToFile(datafolder + "/ciphertext2.txt", ciphertext2, BINARY):
+   raise Exception("Error writing serialization of ciphertext2 to ciphertext2.txt")
+print("The second ciphertext has been serialized.")
+
+if not SerializeToFile(datafolder + "/ciphertext3.txt", ciphertext3, BINARY):   
+   raise Exception("Error writing serialization of ciphertext3 to ciphertext3.txt")
+print("The third ciphertext has been serialized.")
+
+# Sample Program: Step 4: Evaluation
+
+# OpenFHE maintains an internal map of CryptoContext objects which are
+# indexed by a tag and the tag is applied to both the CryptoContext and some
+# of the keys. When deserializing a context, OpenFHE checks for the tag and
+# if it finds it in the CryptoContext map, it will return the stored version.
+# Hence, we need to clear the context and clear the keys.
+cryptoContext.ClearEvalMultKeys()
+cryptoContext.ClearEvalAutomorphismKeys()
+ReleaseAllContexts()
+
+# Deserialize the crypto context
+cc = CryptoContext()
+
+if not DeserializeFromFile(datafolder + "/cryptocontext.txt", cc, BINARY):
+   raise Exception("Error reading serialization of the crypto context from cryptocontext.txt")
+print("The cryptocontext has been deserialized.")
+
+# Deserialize the public key
+pk = PublicKey()
+
+if not DeserializeFromFile(datafolder + "/key-public.txt", pk, BINARY):
+   raise Exception("Error reading serialization of the public key from key-public.txt")
+
+print("The public key has been deserialized.")
+
+if cryptoContext.DeserializeEvalMultKey(datafolder + "/key-eval-mult.txt",BINARY):
+   raise Exception("Could not deserialize the eval mult key file")
+
+print("The relinearization key has been deserialized.")
+
+if cryptoContext.DeserializeEvalAutomorphismKey(datafolder + "/key-eval-rot.txt",BINARY):
+   raise Exception("Could not deserialize the eval rotation key file")
+
+print("Deserialized the eval rotation keys.")
+
+# Deserialize the ciphertexts
+
+ct1 = Ciphertext()
+ct2 = Ciphertext()
+ct3 = Ciphertext()
+
+if not DeserializeFromFile(datafolder + "/ciphertext1.txt", ct1, BINARY):
+    raise Exception("Could not read the ciphertext")
+
+print("The first ciphertext has been deserialized.")
+
+if not DeserializeFromFile(datafolder + "/ciphertext2.txt", ct2, BINARY):
+    raise Exception("Could not read the ciphertext")
+
+print("The second ciphertext has been deserialized.")
+
+if not DeserializeFromFile(datafolder + "/ciphertext3.txt", ct3, BINARY):   
+    raise Exception("Could not read the ciphertext")
+
+print("The third ciphertext has been deserialized.")
+
+# Homomorphic addition
+
+ciphertextAdd12 = cc.EvalAdd(ct1, ct2)
+ciphertextAddResult = cc.EvalAdd(ciphertextAdd12, ct3)
+
+# Homomorphic multiplication
+ciphertextMult12 = cc.EvalMult(ct1, ct2)
+ciphertextMultResult = cc.EvalMult(ciphertextMult12, ct3)
+
+# Homomorphic rotation
+ciphertextRot1 = cc.EvalRotate(ct1, 1)
+ciphertextRot2 = cc.EvalRotate(ct2, 2)
+ciphertextRot3 = cc.EvalRotate(ct3, -1)
+ciphertextRot4 = cc.EvalRotate(ct3, -2)
+
+
+
+
+
+

+ 94 - 0
src/pke/examples/simple-integers.py

@@ -0,0 +1,94 @@
+# Initial Settings
+from openfhe import *
+# import openfhe.PKESchemeFeature as Feature
+
+
+# Sample Program: Step 1: Set CryptoContext
+parameters = CCParamsBFVRNS()
+parameters.SetPlaintextModulus(65537)
+parameters.SetMultiplicativeDepth(2)
+
+crypto_context = GenCryptoContext(parameters)
+# Enable features that you wish to use
+crypto_context.Enable(PKESchemeFeature.PKE)
+crypto_context.Enable(PKESchemeFeature.KEYSWITCH)
+crypto_context.Enable(PKESchemeFeature.LEVELEDSHE)
+
+# Sample Program: Step 2: Key Generation
+
+# Generate a public/private key pair
+key_pair = crypto_context.KeyGen()
+
+# Generate the relinearization key
+crypto_context.EvalMultKeyGen(key_pair.secretKey)
+
+# Generate the rotation evaluation keys
+crypto_context.EvalRotateKeyGen(key_pair.secretKey, [1, 2, -1, -2])
+
+# Sample Program: Step 3: Encryption
+
+# First plaintext vector is encoded
+vector_of_ints1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
+plaintext1 = crypto_context.MakePackedPlaintext(vector_of_ints1)
+
+# Second plaintext vector is encoded
+vector_of_ints2 = [3, 2, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12]
+plaintext2 = crypto_context.MakePackedPlaintext(vector_of_ints2)
+
+# Third plaintext vector is encoded
+vector_of_ints3 = [1, 2, 5, 2, 5, 6, 7, 8, 9, 10, 11, 12]
+plaintext3 = crypto_context.MakePackedPlaintext(vector_of_ints3)
+
+# The encoded vectors are encrypted
+ciphertext1 = crypto_context.Encrypt(key_pair.publicKey, plaintext1)
+ciphertext2 = crypto_context.Encrypt(key_pair.publicKey, plaintext2)
+ciphertext3 = crypto_context.Encrypt(key_pair.publicKey, plaintext3)
+
+#  Sample Program: Step 4: Evaluation
+
+# Homomorphic additions
+ciphertext_add12 = crypto_context.EvalAdd(ciphertext1, ciphertext2)
+ciphertext_add_result = crypto_context.EvalAdd(ciphertext_add12, ciphertext3)
+
+# Homomorphic Multiplication
+ciphertext_mult12 = crypto_context.EvalMult(ciphertext1, ciphertext2)
+ciphertext_mult_result = crypto_context.EvalMult(ciphertext_mult12, ciphertext3)
+
+# Homomorphic Rotations
+ciphertext_rot1 = crypto_context.EvalRotate(ciphertext1, 1)
+ciphertext_rot2 = crypto_context.EvalRotate(ciphertext1, 2)
+ciphertext_rot3 = crypto_context.EvalRotate(ciphertext1, -1)
+ciphertext_rot4 = crypto_context.EvalRotate(ciphertext1, -2)
+
+# Sample Program: Step 5: Decryption
+
+# Decrypt the result of additions
+plaintext_add_result = crypto_context.Decrypt(ciphertext_add_result,key_pair.secretKey)
+
+# Decrypt the result of multiplications
+plaintext_mult_result = crypto_context.Decrypt(ciphertext_mult_result,key_pair.secretKey)
+
+# Decrypt the result of rotations
+plaintextRot1 = crypto_context.Decrypt(ciphertext_rot1,key_pair.secretKey)
+plaintextRot2 = crypto_context.Decrypt(ciphertext_rot2,key_pair.secretKey)
+plaintextRot3 = crypto_context.Decrypt(ciphertext_rot3,key_pair.secretKey)
+plaintextRot4 = crypto_context.Decrypt(ciphertext_rot4,key_pair.secretKey)
+
+
+plaintextRot1.SetLength(len(vector_of_ints1))
+plaintextRot2.SetLength(len(vector_of_ints1))
+plaintextRot3.SetLength(len(vector_of_ints1))
+plaintextRot4.SetLength(len(vector_of_ints1))
+
+print("Plaintext #1: " + str(plaintext1))
+print("Plaintext #2: " + str(plaintext2))
+print("Plaintext #3: " + str(plaintext3))
+
+# Output Results
+print("\nResults of homomorphic computations")
+print("#1 + #2 + #3 = " + str(plaintext_add_result))
+print("#1 * #2 * #3 = " + str(plaintext_mult_result))
+print("Left rotation of #1 by 1 = " + str(plaintextRot1))
+print("Left rotation of #1 by 2 = " + str(plaintextRot2))
+print("Right rotation of #1 by 1 = " + str(plaintextRot3))
+print("Right rotation of #1 by 2 = " + str(plaintextRot4))

+ 89 - 0
src/pke/examples/simple-real-numbers.py

@@ -0,0 +1,89 @@
+from openfhe import *
+
+mult_depth = 1
+scale_mod_size = 50
+batch_size = 8
+
+parameters = CCParamsCKKSRNS()
+parameters.SetMultiplicativeDepth(mult_depth)
+parameters.SetScalingModSize(scale_mod_size)
+parameters.SetBatchSize(batch_size)
+
+cc = GenCryptoContext(parameters)
+cc.Enable(PKESchemeFeature.PKE)
+cc.Enable(PKESchemeFeature.KEYSWITCH)
+cc.Enable(PKESchemeFeature.LEVELEDSHE)
+
+print("The CKKS scheme is using ring dimension: " + str(cc.GetRingDimension()))
+
+keys = cc.KeyGen()
+cc.EvalMultKeyGen(keys.secretKey)
+cc.EvalRotateKeyGen(keys.secretKey, [1, -2])
+
+x1 = [0.25, 0.5, 0.75, 1.0, 2.0, 3.0, 4.0, 5.0]
+x2 = [5.0, 4.0, 3.0, 2.0, 1.0, 0.75, 0.5, 0.25]
+
+ptx1 = cc.MakeCKKSPackedPlaintext(x1)
+ptx2 = cc.MakeCKKSPackedPlaintext(x2)
+
+print("Input x1: " + str(ptx1))
+print("Input x2: " + str(ptx2))
+
+# Encrypt the encoded vectors
+c1 = cc.Encrypt(keys.publicKey, ptx1)
+c2 = cc.Encrypt(keys.publicKey, ptx2)
+
+# Step 4: Evaluation
+# Homomorphic additions
+c_add = cc.EvalAdd(c1, c2)
+# Homomorphic subtraction
+c_sub = cc.EvalSub(c1, c2)
+# Homomorphic scalar multiplication
+c_scalar = cc.EvalMult(c1,4)
+# Homomorphic multiplication
+c_mult = cc.EvalMult(c1, c2)
+# Homomorphic rotations
+c_rot1 = cc.EvalRotate(c1, 1)
+c_rot2 = cc.EvalRotate(c1, -2)
+
+# Step 5: Decryption and output
+# Decrypt the result of additions
+ptAdd = cc.Decrypt(c_add,keys.secretKey)
+
+# We set the precision to 8 decimal digits for a nicer output.
+# If you want to see the error/noise introduced by CKKS, bump it up
+# to 15 and it should become visible.
+
+precision = 8
+print("Results of homomorphic computations:")
+result = cc.Decrypt(c1, keys.secretKey)
+result.SetLength(batch_size)
+print("x1 = " + str(result))
+print("Estimated precision in bits: " + str(result.GetLogPrecision()))
+
+# Decrypt the result of scalar multiplication
+result = cc.Decrypt(c_scalar,keys.secretKey)
+result.SetLength(batch_size)
+print("4 * x1 = " + str(result))
+
+# Decrypt the result of multiplication
+result = cc.Decrypt(c_mult,keys.secretKey)
+result.SetLength(batch_size)
+print("x1 * x2 = " + str(result))
+
+# Decrypt the result of rotations
+result = cc.Decrypt(c_rot1,keys.secretKey)
+result.SetLength(batch_size)
+print("In rotations, very small outputs (~10^-10 here) correspond to 0's:")
+print("x1 rotated by 1 = " + str(result))
+
+result = cc.Decrypt(c_rot2,keys.secretKey)
+result.SetLength(batch_size)
+print("x1 rotated by -2 = " + str(result))
+
+
+
+
+
+
+

+ 46 - 0
src/pke/serialization.cpp

@@ -0,0 +1,46 @@
+#include <pybind11/pybind11.h>
+#include <pybind11/stl.h>
+#include "openfhe.h"
+#include "scheme/bfvrns/bfvrns-ser.h"
+#include "scheme/bgvrns/bgvrns-ser.h"
+#include "cryptocontext-ser.h"
+#include "bindings.h"
+#include "serialization.h"
+
+using namespace lbcrypto;
+namespace py = pybind11;
+
+template <typename ST>
+bool SerializeEvalMultKeyWrapper(const std::string& filename, const ST& sertype, std::string id)
+{
+    std::ofstream outfile(filename, std::ios::out | std::ios::binary);
+    bool res;
+    res = CryptoContextImpl<DCRTPoly>::SerializeEvalMultKey<ST>(outfile, sertype, id);
+    outfile.close();
+    return res;
+}
+
+void bind_serialization(pybind11::module &m) {
+    // Json Serialization
+    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const CryptoContext<DCRTPoly>&, const SerType::SERJSON&)>(&Serial::SerializeToFile<DCRTPoly>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("DeserializeFromFile", static_cast<bool (*)(const std::string&, CryptoContext<DCRTPoly>& ,const SerType::SERJSON&)>(&Serial::DeserializeFromFile<DCRTPoly>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const PublicKey<DCRTPoly>&, const SerType::SERJSON&)>(&Serial::SerializeToFile<PublicKey<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("DeserializeFromFile", static_cast<bool (*)(const std::string&, PublicKey<DCRTPoly>& ,const SerType::SERJSON&)>(&Serial::DeserializeFromFile<PublicKey<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const PrivateKey<DCRTPoly>&, const SerType::SERJSON&)>(&Serial::SerializeToFile<PrivateKey<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("DeserializeFromFile", static_cast<bool (*)(const std::string&, PrivateKey<DCRTPoly>& ,const SerType::SERJSON&)>(&Serial::DeserializeFromFile<PrivateKey<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const Ciphertext<DCRTPoly>&, const SerType::SERJSON&)>(&Serial::SerializeToFile<Ciphertext<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("DeserializeFromFile", static_cast<bool (*)(const std::string&, Ciphertext<DCRTPoly>& ,const SerType::SERJSON&)>(&Serial::DeserializeFromFile<Ciphertext<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    // Binary Serialization
+    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const CryptoContext<DCRTPoly>&, const SerType::SERBINARY&)>(&Serial::SerializeToFile<DCRTPoly>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("DeserializeFromFile", static_cast<bool (*)(const std::string&, CryptoContext<DCRTPoly>& ,const SerType::SERBINARY&)>(&Serial::DeserializeFromFile<DCRTPoly>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const PublicKey<DCRTPoly>&, const SerType::SERBINARY&)>(&Serial::SerializeToFile<PublicKey<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("DeserializeFromFile", static_cast<bool (*)(const std::string&, PublicKey<DCRTPoly>& ,const SerType::SERBINARY&)>(&Serial::DeserializeFromFile<PublicKey<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const PrivateKey<DCRTPoly>&, const SerType::SERBINARY&)>(&Serial::SerializeToFile<PrivateKey<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("DeserializeFromFile", static_cast<bool (*)(const std::string&, PrivateKey<DCRTPoly>& ,const SerType::SERBINARY&)>(&Serial::DeserializeFromFile<PrivateKey<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("SerializeToFile", static_cast<bool (*)(const std::string&, const Ciphertext<DCRTPoly>&, const SerType::SERBINARY&)>(&Serial::SerializeToFile<Ciphertext<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    m.def("DeserializeFromFile", static_cast<bool (*)(const std::string&, Ciphertext<DCRTPoly>& ,const SerType::SERBINARY&)>(&Serial::DeserializeFromFile<Ciphertext<DCRTPoly>>), py::arg("filename"), py::arg("obj"), py::arg("sertype"));
+    
+    
+}
+
+