## ----setup, include = FALSE---------------------------------------------------
# Cap OpenMP to CRAN's two-core policy (see ?openfhe.R::set_num_threads).
library(openfhe.R)
openfhe.R::set_num_threads(2L)
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)

## ----setup-toy----------------------------------------------------------------
library(openfhe.R)

## TOY paramset — fast but zero security. For demos only.
ctx <- bin_fhe_context(BinFHEParamSet$TOY, BinFHEMethod$GINX)

## The secret key generates bootstrap keys that every gate
## will use internally.
sk <- bin_key_gen(ctx)
bin_bt_key_gen(ctx, sk)

## ----binary-gates-------------------------------------------------------------
ct0 <- bin_encrypt(ctx, sk, 0L)
ct1 <- bin_encrypt(ctx, sk, 1L)

truth <- function(gate_name, gate, ct_a, ct_b) {
  r <- eval_bin_gate(ctx, gate, ct_a, ct_b)
  bin_decrypt(ctx, sk, r)
}

## Full truth table for AND / OR / XOR.
grid <- expand.grid(a = c(0L, 1L), b = c(0L, 1L))
grid$AND <- mapply(function(a, b) truth("AND", BinGate$AND,
                                         bin_encrypt(ctx, sk, a),
                                         bin_encrypt(ctx, sk, b)),
                    grid$a, grid$b)
grid$OR  <- mapply(function(a, b) truth("OR", BinGate$OR,
                                         bin_encrypt(ctx, sk, a),
                                         bin_encrypt(ctx, sk, b)),
                    grid$a, grid$b)
grid$XOR <- mapply(function(a, b) truth("XOR", BinGate$XOR,
                                         bin_encrypt(ctx, sk, a),
                                         bin_encrypt(ctx, sk, b)),
                    grid$a, grid$b)
grid

## ----not----------------------------------------------------------------------
bin_decrypt(ctx, sk, eval_not(ctx, ct0))
bin_decrypt(ctx, sk, eval_not(ctx, ct1))

## ----multi-input-setup--------------------------------------------------------
ctx3 <- bin_fhe_context(BinFHEParamSet$STD128_3)
sk3 <- bin_key_gen(ctx3)
bin_bt_key_gen(ctx3, sk3)

p3 <- 6L
encrypt_bit_3 <- function(b) {
  bin_encrypt(ctx3, sk3, as.integer(b),
              output = BinFHEOutput$SMALL_DIM,
              p = p3)
}

## ----and3---------------------------------------------------------------------
## 1 AND 1 AND 0 = 0
cts_110 <- list(encrypt_bit_3(1L),
                encrypt_bit_3(1L),
                encrypt_bit_3(0L))
bin_decrypt(ctx3, sk3,
            eval_bin_gate(ctx3, BinGate$AND3, cts_110),
            p = p3)

## 1 AND 1 AND 1 = 1
cts_111 <- list(encrypt_bit_3(1L),
                encrypt_bit_3(1L),
                encrypt_bit_3(1L))
bin_decrypt(ctx3, sk3,
            eval_bin_gate(ctx3, BinGate$AND3, cts_111),
            p = p3)

## ----majority, eval = FALSE---------------------------------------------------
# ## Majority uses p = 4 (not 2 * num_inputs = 6) per the
# ## upstream boolean-multi-input example's encoding.
# p_maj <- 4L
# encrypt_bit_maj <- function(b) {
#   bin_encrypt(ctx3, sk3, as.integer(b),
#               output = BinFHEOutput$SMALL_DIM,
#               p = p_maj)
# }
# 
# cts <- list(encrypt_bit_maj(1L),
#             encrypt_bit_maj(1L),
#             encrypt_bit_maj(0L))
# bin_decrypt(ctx3, sk3,
#             eval_bin_gate(ctx3, BinGate$MAJORITY, cts),
#             p = p_maj)
# ## 1 (majority of two 1s and one 0)

## ----arb-func-----------------------------------------------------------------
## Build an arb-func context. arb_func = TRUE picks a wider
## paramset; log_q and n control the LARGE_DIM modulus and
## dimension of the functional-bootstrap path.
ctx_f <- bin_fhe_context(BinFHEParamSet$TOY,
                          arb_func = TRUE)
sk_f <- bin_key_gen(ctx_f)
bin_bt_key_gen(ctx_f, sk_f)

p <- get_max_plaintext_space(ctx_f)

## Example function: squared value mod p. The LUT maps
## input i in [0, p) to f(i) = i^2 mod p.
f_square <- function(x, plaintext_modulus) {
  (x * x) %% plaintext_modulus
}
lut <- generate_lut_via_function(f_square, p)

## Encrypt a value in the LARGE_DIM / functional-bootstrap
## path and evaluate.
ct_input <- bin_encrypt(ctx_f, sk_f, 3L,
                        output = BinFHEOutput$LARGE_DIM,
                        p = p)
ct_out <- eval_func(ctx_f, ct_input, lut)
bin_decrypt(ctx_f, sk_f, ct_out, p = p)
## 9 (if p > 9, otherwise 9 mod p)

## ----sign-setup---------------------------------------------------------------
log_q <- 17L
ctx_s <- bin_fhe_context(
  paramset           = BinFHEParamSet$STD128,
  method             = BinFHEMethod$GINX,
  arb_func           = FALSE,
  log_q              = log_q,
  n                  = 0L,
  time_optimization  = FALSE
)
sk_s <- bin_key_gen(ctx_s)
bin_bt_key_gen(ctx_s, sk_s)

Q <- bitwShiftL(1L, log_q)                           # 131072
q <- 4096
factor <- bitwShiftL(1L, log_q - as.integer(log2(q))) # 32
p_s <- get_max_plaintext_space(ctx_s) * factor

## ----sign---------------------------------------------------------------------
center <- p_s %/% 2
for (i in 0:7) {
  msg <- center + i - 3
  ct  <- bin_encrypt(ctx_s, sk_s, msg,
                     output = BinFHEOutput$LARGE_DIM,
                     p = p_s, mod = Q)
  ct_sign <- eval_sign(ctx_s, ct)
  ## Decrypt with p = 2 (sign bit is a single bit).
  result <- bin_decrypt(ctx_s, sk_s, ct_sign, p = 2L)
  cat(sprintf("msg = center%+d => sign bit %d\n", i - 3, result))
}

## ----floor--------------------------------------------------------------------
ct_five <- bin_encrypt(ctx_f, sk_f, 5L,
                       output = BinFHEOutput$LARGE_DIM,
                       p = p)
ct_floor <- eval_floor(ctx_f, ct_five, roundbits = 2L)
class(ct_floor)
## Returns an LWECiphertext holding floor(5 / 2^2) = 1 in
## the rounded encoding.

