mirror of
https://github.com/arnaucube/poulpy.git
synced 2026-02-10 13:16:44 +01:00
Add cross-basek normalization (#90)
* added cross_basek_normalization * updated method signatures to take layouts * fixed cross-base normalization fix #91 fix #93
This commit is contained in:
committed by
GitHub
parent
4da790ea6a
commit
37e13b965c
@@ -1,11 +1,11 @@
|
||||
use poulpy_hal::{
|
||||
api::{VecZnxCopy, VecZnxFillUniform},
|
||||
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, Reset, WriterTo},
|
||||
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, WriterTo},
|
||||
source::Source,
|
||||
};
|
||||
|
||||
use crate::layouts::{
|
||||
GGLWEAutomorphismKey, Infos,
|
||||
Base2K, Degree, Digits, GGLWEAutomorphismKey, GGLWELayoutInfos, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
|
||||
compressed::{Decompress, GGLWESwitchingKeyCompressed},
|
||||
};
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
@@ -17,9 +17,50 @@ pub struct GGLWEAutomorphismKeyCompressed<D: Data> {
|
||||
pub(crate) p: i64,
|
||||
}
|
||||
|
||||
impl<D: Data> LWEInfos for GGLWEAutomorphismKeyCompressed<D> {
|
||||
fn n(&self) -> Degree {
|
||||
self.key.n()
|
||||
}
|
||||
|
||||
fn base2k(&self) -> Base2K {
|
||||
self.key.base2k()
|
||||
}
|
||||
|
||||
fn k(&self) -> TorusPrecision {
|
||||
self.key.k()
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
self.key.size()
|
||||
}
|
||||
}
|
||||
impl<D: Data> GLWEInfos for GGLWEAutomorphismKeyCompressed<D> {
|
||||
fn rank(&self) -> Rank {
|
||||
self.rank_out()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> GGLWELayoutInfos for GGLWEAutomorphismKeyCompressed<D> {
|
||||
fn rank_in(&self) -> Rank {
|
||||
self.key.rank_in()
|
||||
}
|
||||
|
||||
fn rank_out(&self) -> Rank {
|
||||
self.key.rank_out()
|
||||
}
|
||||
|
||||
fn digits(&self) -> Digits {
|
||||
self.key.digits()
|
||||
}
|
||||
|
||||
fn rows(&self) -> Rows {
|
||||
self.key.rows()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> fmt::Debug for GGLWEAutomorphismKeyCompressed<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self)
|
||||
write!(f, "{self}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,16 +70,6 @@ impl<D: DataMut> FillUniform for GGLWEAutomorphismKeyCompressed<D> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> Reset for GGLWEAutomorphismKeyCompressed<D>
|
||||
where
|
||||
MatZnx<D>: Reset,
|
||||
{
|
||||
fn reset(&mut self) {
|
||||
self.key.reset();
|
||||
self.p = 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> fmt::Display for GGLWEAutomorphismKeyCompressed<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "(AutomorphismKeyCompressed: p={}) {}", self.p, self.key)
|
||||
@@ -46,49 +77,34 @@ impl<D: DataRef> fmt::Display for GGLWEAutomorphismKeyCompressed<D> {
|
||||
}
|
||||
|
||||
impl GGLWEAutomorphismKeyCompressed<Vec<u8>> {
|
||||
pub fn alloc(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self {
|
||||
GGLWEAutomorphismKeyCompressed {
|
||||
key: GGLWESwitchingKeyCompressed::alloc(n, basek, k, rows, digits, rank, rank),
|
||||
pub fn alloc<A>(infos: &A) -> Self
|
||||
where
|
||||
A: GGLWELayoutInfos,
|
||||
{
|
||||
debug_assert_eq!(infos.rank_in(), infos.rank_out());
|
||||
Self {
|
||||
key: GGLWESwitchingKeyCompressed::alloc(infos),
|
||||
p: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bytes_of(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize {
|
||||
GGLWESwitchingKeyCompressed::<Vec<u8>>::bytes_of(n, basek, k, rows, digits, rank)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> Infos for GGLWEAutomorphismKeyCompressed<D> {
|
||||
type Inner = MatZnx<D>;
|
||||
|
||||
fn inner(&self) -> &Self::Inner {
|
||||
self.key.inner()
|
||||
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, digits: Digits, rank: Rank) -> Self {
|
||||
Self {
|
||||
key: GGLWESwitchingKeyCompressed::alloc_with(n, base2k, k, rows, digits, rank, rank),
|
||||
p: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn basek(&self) -> usize {
|
||||
self.key.basek()
|
||||
pub fn alloc_bytes<A>(infos: &A) -> usize
|
||||
where
|
||||
A: GGLWELayoutInfos,
|
||||
{
|
||||
debug_assert_eq!(infos.rank_in(), infos.rank_out());
|
||||
GGLWESwitchingKeyCompressed::alloc_bytes(infos)
|
||||
}
|
||||
|
||||
fn k(&self) -> usize {
|
||||
self.key.k()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> GGLWEAutomorphismKeyCompressed<D> {
|
||||
pub fn rank(&self) -> usize {
|
||||
self.key.rank()
|
||||
}
|
||||
|
||||
pub fn digits(&self) -> usize {
|
||||
self.key.digits()
|
||||
}
|
||||
|
||||
pub fn rank_in(&self) -> usize {
|
||||
self.key.rank_in()
|
||||
}
|
||||
|
||||
pub fn rank_out(&self) -> usize {
|
||||
self.key.rank_out()
|
||||
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, digits: Digits, rank: Rank) -> usize {
|
||||
GGLWESwitchingKeyCompressed::alloc_bytes_with(n, base2k, k, rows, digits, rank, rank)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use poulpy_hal::{
|
||||
api::{VecZnxCopy, VecZnxFillUniform},
|
||||
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, Reset, WriterTo},
|
||||
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, WriterTo, ZnxInfos},
|
||||
source::Source,
|
||||
};
|
||||
|
||||
use crate::layouts::{
|
||||
GGLWECiphertext, Infos,
|
||||
Base2K, Degree, Digits, GGLWECiphertext, GGLWELayoutInfos, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
|
||||
compressed::{Decompress, GLWECiphertextCompressed},
|
||||
};
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
@@ -14,16 +14,57 @@ use std::fmt;
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
pub struct GGLWECiphertextCompressed<D: Data> {
|
||||
pub(crate) data: MatZnx<D>,
|
||||
pub(crate) basek: usize,
|
||||
pub(crate) k: usize,
|
||||
pub(crate) rank_out: usize,
|
||||
pub(crate) digits: usize,
|
||||
pub(crate) base2k: Base2K,
|
||||
pub(crate) k: TorusPrecision,
|
||||
pub(crate) rank_out: Rank,
|
||||
pub(crate) digits: Digits,
|
||||
pub(crate) seed: Vec<[u8; 32]>,
|
||||
}
|
||||
|
||||
impl<D: Data> LWEInfos for GGLWECiphertextCompressed<D> {
|
||||
fn n(&self) -> Degree {
|
||||
Degree(self.data.n() as u32)
|
||||
}
|
||||
|
||||
fn base2k(&self) -> Base2K {
|
||||
self.base2k
|
||||
}
|
||||
|
||||
fn k(&self) -> TorusPrecision {
|
||||
self.k
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
self.data.size()
|
||||
}
|
||||
}
|
||||
impl<D: Data> GLWEInfos for GGLWECiphertextCompressed<D> {
|
||||
fn rank(&self) -> Rank {
|
||||
self.rank_out()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> GGLWELayoutInfos for GGLWECiphertextCompressed<D> {
|
||||
fn rank_in(&self) -> Rank {
|
||||
Rank(self.data.cols_in() as u32)
|
||||
}
|
||||
|
||||
fn rank_out(&self) -> Rank {
|
||||
self.rank_out
|
||||
}
|
||||
|
||||
fn digits(&self) -> Digits {
|
||||
self.digits
|
||||
}
|
||||
|
||||
fn rows(&self) -> Rows {
|
||||
Rows(self.data.rows() as u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> fmt::Debug for GGLWECiphertextCompressed<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self)
|
||||
write!(f, "{self}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,133 +74,140 @@ impl<D: DataMut> FillUniform for GGLWECiphertextCompressed<D> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> Reset for GGLWECiphertextCompressed<D>
|
||||
where
|
||||
MatZnx<D>: Reset,
|
||||
{
|
||||
fn reset(&mut self) {
|
||||
self.data.reset();
|
||||
self.basek = 0;
|
||||
self.k = 0;
|
||||
self.digits = 0;
|
||||
self.rank_out = 0;
|
||||
self.seed = Vec::new();
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> fmt::Display for GGLWECiphertextCompressed<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"(GGLWECiphertextCompressed: basek={} k={} digits={}) {}",
|
||||
self.basek, self.k, self.digits, self.data
|
||||
"(GGLWECiphertextCompressed: base2k={} k={} digits={}) {}",
|
||||
self.base2k.0, self.k.0, self.digits.0, self.data
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl GGLWECiphertextCompressed<Vec<u8>> {
|
||||
pub fn alloc(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize, rank_out: usize) -> Self {
|
||||
let size: usize = k.div_ceil(basek);
|
||||
pub fn alloc<A>(infos: &A) -> Self
|
||||
where
|
||||
A: GGLWELayoutInfos,
|
||||
{
|
||||
Self::alloc_with(
|
||||
infos.n(),
|
||||
infos.base2k(),
|
||||
infos.k(),
|
||||
infos.rows(),
|
||||
infos.digits(),
|
||||
infos.rank_in(),
|
||||
infos.rank_out(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn alloc_with(
|
||||
n: Degree,
|
||||
base2k: Base2K,
|
||||
k: TorusPrecision,
|
||||
rows: Rows,
|
||||
digits: Digits,
|
||||
rank_in: Rank,
|
||||
rank_out: Rank,
|
||||
) -> Self {
|
||||
let size: usize = k.0.div_ceil(base2k.0) as usize;
|
||||
debug_assert!(
|
||||
size > digits,
|
||||
"invalid gglwe: ceil(k/basek): {} <= digits: {}",
|
||||
size,
|
||||
digits
|
||||
size as u32 > digits.0,
|
||||
"invalid gglwe: ceil(k/base2k): {size} <= digits: {}",
|
||||
digits.0
|
||||
);
|
||||
|
||||
assert!(
|
||||
rows * digits <= size,
|
||||
"invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
|
||||
rows,
|
||||
digits,
|
||||
size
|
||||
rows.0 * digits.0 <= size as u32,
|
||||
"invalid gglwe: rows: {} * digits:{} > ceil(k/base2k): {size}",
|
||||
rows.0,
|
||||
digits.0,
|
||||
);
|
||||
|
||||
Self {
|
||||
data: MatZnx::alloc(n, rows, rank_in, 1, size),
|
||||
basek,
|
||||
data: MatZnx::alloc(
|
||||
n.into(),
|
||||
rows.into(),
|
||||
rank_in.into(),
|
||||
1,
|
||||
k.0.div_ceil(base2k.0) as usize,
|
||||
),
|
||||
k,
|
||||
rank_out,
|
||||
base2k,
|
||||
digits,
|
||||
seed: vec![[0u8; 32]; rows * rank_in],
|
||||
rank_out,
|
||||
seed: vec![[0u8; 32]; (rows.0 * rank_in.0) as usize],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bytes_of(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize) -> usize {
|
||||
let size: usize = k.div_ceil(basek);
|
||||
pub fn alloc_bytes<A>(infos: &A) -> usize
|
||||
where
|
||||
A: GGLWELayoutInfos,
|
||||
{
|
||||
Self::alloc_bytes_with(
|
||||
infos.n(),
|
||||
infos.base2k(),
|
||||
infos.k(),
|
||||
infos.rows(),
|
||||
infos.digits(),
|
||||
infos.rank_in(),
|
||||
infos.rank_out(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn alloc_bytes_with(
|
||||
n: Degree,
|
||||
base2k: Base2K,
|
||||
k: TorusPrecision,
|
||||
rows: Rows,
|
||||
digits: Digits,
|
||||
rank_in: Rank,
|
||||
_rank_out: Rank,
|
||||
) -> usize {
|
||||
let size: usize = k.0.div_ceil(base2k.0) as usize;
|
||||
debug_assert!(
|
||||
size > digits,
|
||||
"invalid gglwe: ceil(k/basek): {} <= digits: {}",
|
||||
size,
|
||||
digits
|
||||
size as u32 > digits.0,
|
||||
"invalid gglwe: ceil(k/base2k): {size} <= digits: {}",
|
||||
digits.0
|
||||
);
|
||||
|
||||
assert!(
|
||||
rows * digits <= size,
|
||||
"invalid gglwe: rows: {} * digits:{} > ceil(k/basek): {}",
|
||||
rows,
|
||||
digits,
|
||||
size
|
||||
rows.0 * digits.0 <= size as u32,
|
||||
"invalid gglwe: rows: {} * digits:{} > ceil(k/base2k): {size}",
|
||||
rows.0,
|
||||
digits.0,
|
||||
);
|
||||
|
||||
MatZnx::alloc_bytes(n, rows, rank_in, 1, rows)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> Infos for GGLWECiphertextCompressed<D> {
|
||||
type Inner = MatZnx<D>;
|
||||
|
||||
fn inner(&self) -> &Self::Inner {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn basek(&self) -> usize {
|
||||
self.basek
|
||||
}
|
||||
|
||||
fn k(&self) -> usize {
|
||||
self.k
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> GGLWECiphertextCompressed<D> {
|
||||
pub fn rank(&self) -> usize {
|
||||
self.rank_out
|
||||
}
|
||||
|
||||
pub fn digits(&self) -> usize {
|
||||
self.digits
|
||||
}
|
||||
|
||||
pub fn rank_in(&self) -> usize {
|
||||
self.data.cols_in()
|
||||
}
|
||||
|
||||
pub fn rank_out(&self) -> usize {
|
||||
self.rank_out
|
||||
MatZnx::alloc_bytes(
|
||||
n.into(),
|
||||
rows.into(),
|
||||
rank_in.into(),
|
||||
1,
|
||||
k.0.div_ceil(base2k.0) as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> GGLWECiphertextCompressed<D> {
|
||||
pub(crate) fn at(&self, row: usize, col: usize) -> GLWECiphertextCompressed<&[u8]> {
|
||||
let rank_in: usize = self.rank_in().into();
|
||||
GLWECiphertextCompressed {
|
||||
data: self.data.at(row, col),
|
||||
basek: self.basek,
|
||||
k: self.k,
|
||||
base2k: self.base2k,
|
||||
rank: self.rank_out,
|
||||
seed: self.seed[self.rank_in() * row + col],
|
||||
seed: self.seed[rank_in * row + col],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> GGLWECiphertextCompressed<D> {
|
||||
pub(crate) fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertextCompressed<&mut [u8]> {
|
||||
let rank_in: usize = self.rank_in();
|
||||
let rank_in: usize = self.rank_in().into();
|
||||
GLWECiphertextCompressed {
|
||||
data: self.data.at_mut(row, col),
|
||||
basek: self.basek,
|
||||
k: self.k,
|
||||
base2k: self.base2k,
|
||||
rank: self.rank_out,
|
||||
data: self.data.at_mut(row, col),
|
||||
seed: self.seed[rank_in * row + col], // Warning: value is copied and not borrow mut
|
||||
}
|
||||
}
|
||||
@@ -167,12 +215,12 @@ impl<D: DataMut> GGLWECiphertextCompressed<D> {
|
||||
|
||||
impl<D: DataMut> ReaderFrom for GGLWECiphertextCompressed<D> {
|
||||
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
|
||||
self.k = reader.read_u64::<LittleEndian>()? as usize;
|
||||
self.basek = reader.read_u64::<LittleEndian>()? as usize;
|
||||
self.digits = reader.read_u64::<LittleEndian>()? as usize;
|
||||
self.rank_out = reader.read_u64::<LittleEndian>()? as usize;
|
||||
let seed_len = reader.read_u64::<LittleEndian>()? as usize;
|
||||
self.seed = vec![[0u8; 32]; seed_len];
|
||||
self.k = TorusPrecision(reader.read_u32::<LittleEndian>()?);
|
||||
self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
|
||||
self.digits = Digits(reader.read_u32::<LittleEndian>()?);
|
||||
self.rank_out = Rank(reader.read_u32::<LittleEndian>()?);
|
||||
let seed_len: u32 = reader.read_u32::<LittleEndian>()?;
|
||||
self.seed = vec![[0u8; 32]; seed_len as usize];
|
||||
for s in &mut self.seed {
|
||||
reader.read_exact(s)?;
|
||||
}
|
||||
@@ -182,11 +230,11 @@ impl<D: DataMut> ReaderFrom for GGLWECiphertextCompressed<D> {
|
||||
|
||||
impl<D: DataRef> WriterTo for GGLWECiphertextCompressed<D> {
|
||||
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
writer.write_u64::<LittleEndian>(self.k as u64)?;
|
||||
writer.write_u64::<LittleEndian>(self.basek as u64)?;
|
||||
writer.write_u64::<LittleEndian>(self.digits as u64)?;
|
||||
writer.write_u64::<LittleEndian>(self.rank_out as u64)?;
|
||||
writer.write_u64::<LittleEndian>(self.seed.len() as u64)?;
|
||||
writer.write_u32::<LittleEndian>(self.k.into())?;
|
||||
writer.write_u32::<LittleEndian>(self.base2k.into())?;
|
||||
writer.write_u32::<LittleEndian>(self.digits.into())?;
|
||||
writer.write_u32::<LittleEndian>(self.rank_out.into())?;
|
||||
writer.write_u32::<LittleEndian>(self.seed.len() as u32)?;
|
||||
for s in &self.seed {
|
||||
writer.write_all(s)?;
|
||||
}
|
||||
@@ -201,14 +249,12 @@ where
|
||||
fn decompress(&mut self, module: &Module<B>, other: &GGLWECiphertextCompressed<DR>) {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
use poulpy_hal::layouts::ZnxInfos;
|
||||
|
||||
assert_eq!(
|
||||
self.n(),
|
||||
other.data.n(),
|
||||
other.n(),
|
||||
"invalid receiver: self.n()={} != other.n()={}",
|
||||
self.n(),
|
||||
other.data.n()
|
||||
other.n()
|
||||
);
|
||||
assert_eq!(
|
||||
self.size(),
|
||||
@@ -241,8 +287,8 @@ where
|
||||
);
|
||||
}
|
||||
|
||||
let rank_in: usize = self.rank_in();
|
||||
let rows: usize = self.rows();
|
||||
let rank_in: usize = self.rank_in().into();
|
||||
let rows: usize = self.rows().into();
|
||||
|
||||
(0..rank_in).for_each(|col_i| {
|
||||
(0..rows).for_each(|row_i| {
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use poulpy_hal::{
|
||||
api::{VecZnxCopy, VecZnxFillUniform},
|
||||
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, Reset, WriterTo},
|
||||
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, WriterTo},
|
||||
source::Source,
|
||||
};
|
||||
|
||||
use crate::layouts::{
|
||||
GGLWESwitchingKey, Infos,
|
||||
Base2K, Degree, Digits, GGLWELayoutInfos, GGLWESwitchingKey, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
|
||||
compressed::{Decompress, GGLWECiphertextCompressed},
|
||||
};
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
@@ -18,9 +18,50 @@ pub struct GGLWESwitchingKeyCompressed<D: Data> {
|
||||
pub(crate) sk_out_n: usize, // Degree of sk_out
|
||||
}
|
||||
|
||||
impl<D: Data> LWEInfos for GGLWESwitchingKeyCompressed<D> {
|
||||
fn n(&self) -> Degree {
|
||||
self.key.n()
|
||||
}
|
||||
|
||||
fn base2k(&self) -> Base2K {
|
||||
self.key.base2k()
|
||||
}
|
||||
|
||||
fn k(&self) -> TorusPrecision {
|
||||
self.key.k()
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
self.key.size()
|
||||
}
|
||||
}
|
||||
impl<D: Data> GLWEInfos for GGLWESwitchingKeyCompressed<D> {
|
||||
fn rank(&self) -> Rank {
|
||||
self.rank_out()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> GGLWELayoutInfos for GGLWESwitchingKeyCompressed<D> {
|
||||
fn rank_in(&self) -> Rank {
|
||||
self.key.rank_in()
|
||||
}
|
||||
|
||||
fn rank_out(&self) -> Rank {
|
||||
self.key.rank_out()
|
||||
}
|
||||
|
||||
fn digits(&self) -> Digits {
|
||||
self.key.digits()
|
||||
}
|
||||
|
||||
fn rows(&self) -> Rows {
|
||||
self.key.rows()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> fmt::Debug for GGLWESwitchingKeyCompressed<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self)
|
||||
write!(f, "{self}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,17 +71,6 @@ impl<D: DataMut> FillUniform for GGLWESwitchingKeyCompressed<D> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> Reset for GGLWESwitchingKeyCompressed<D>
|
||||
where
|
||||
MatZnx<D>: Reset,
|
||||
{
|
||||
fn reset(&mut self) {
|
||||
self.key.reset();
|
||||
self.sk_in_n = 0;
|
||||
self.sk_out_n = 0;
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> fmt::Display for GGLWESwitchingKeyCompressed<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
@@ -51,51 +81,51 @@ impl<D: DataRef> fmt::Display for GGLWESwitchingKeyCompressed<D> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> Infos for GGLWESwitchingKeyCompressed<D> {
|
||||
type Inner = MatZnx<D>;
|
||||
|
||||
fn inner(&self) -> &Self::Inner {
|
||||
self.key.inner()
|
||||
}
|
||||
|
||||
fn basek(&self) -> usize {
|
||||
self.key.basek()
|
||||
}
|
||||
|
||||
fn k(&self) -> usize {
|
||||
self.key.k()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> GGLWESwitchingKeyCompressed<D> {
|
||||
pub fn rank(&self) -> usize {
|
||||
self.key.rank()
|
||||
}
|
||||
|
||||
pub fn digits(&self) -> usize {
|
||||
self.key.digits()
|
||||
}
|
||||
|
||||
pub fn rank_in(&self) -> usize {
|
||||
self.key.rank_in()
|
||||
}
|
||||
|
||||
pub fn rank_out(&self) -> usize {
|
||||
self.key.rank_out()
|
||||
}
|
||||
}
|
||||
|
||||
impl GGLWESwitchingKeyCompressed<Vec<u8>> {
|
||||
pub fn alloc(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize, rank_out: usize) -> Self {
|
||||
pub fn alloc<A>(infos: &A) -> Self
|
||||
where
|
||||
A: GGLWELayoutInfos,
|
||||
{
|
||||
GGLWESwitchingKeyCompressed {
|
||||
key: GGLWECiphertextCompressed::alloc(n, basek, k, rows, digits, rank_in, rank_out),
|
||||
key: GGLWECiphertextCompressed::alloc(infos),
|
||||
sk_in_n: 0,
|
||||
sk_out_n: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bytes_of(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank_in: usize) -> usize {
|
||||
GGLWECiphertextCompressed::bytes_of(n, basek, k, rows, digits, rank_in)
|
||||
pub fn alloc_with(
|
||||
n: Degree,
|
||||
base2k: Base2K,
|
||||
k: TorusPrecision,
|
||||
rows: Rows,
|
||||
digits: Digits,
|
||||
rank_in: Rank,
|
||||
rank_out: Rank,
|
||||
) -> Self {
|
||||
GGLWESwitchingKeyCompressed {
|
||||
key: GGLWECiphertextCompressed::alloc_with(n, base2k, k, rows, digits, rank_in, rank_out),
|
||||
sk_in_n: 0,
|
||||
sk_out_n: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn alloc_bytes<A>(infos: &A) -> usize
|
||||
where
|
||||
A: GGLWELayoutInfos,
|
||||
{
|
||||
GGLWECiphertextCompressed::alloc_bytes(infos)
|
||||
}
|
||||
|
||||
pub fn alloc_bytes_with(
|
||||
n: Degree,
|
||||
base2k: Base2K,
|
||||
k: TorusPrecision,
|
||||
rows: Rows,
|
||||
digits: Digits,
|
||||
rank_in: Rank,
|
||||
rank_out: Rank,
|
||||
) -> usize {
|
||||
GGLWECiphertextCompressed::alloc_bytes_with(n, base2k, k, rows, digits, rank_in, rank_out)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use poulpy_hal::{
|
||||
api::{VecZnxCopy, VecZnxFillUniform},
|
||||
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, Reset, WriterTo},
|
||||
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, WriterTo},
|
||||
source::Source,
|
||||
};
|
||||
|
||||
use crate::layouts::{
|
||||
GGLWETensorKey, Infos,
|
||||
Base2K, Degree, Digits, GGLWELayoutInfos, GGLWETensorKey, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
|
||||
compressed::{Decompress, GGLWESwitchingKeyCompressed},
|
||||
};
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
@@ -16,9 +16,49 @@ pub struct GGLWETensorKeyCompressed<D: Data> {
|
||||
pub(crate) keys: Vec<GGLWESwitchingKeyCompressed<D>>,
|
||||
}
|
||||
|
||||
impl<D: Data> LWEInfos for GGLWETensorKeyCompressed<D> {
|
||||
fn n(&self) -> Degree {
|
||||
self.keys[0].n()
|
||||
}
|
||||
|
||||
fn base2k(&self) -> Base2K {
|
||||
self.keys[0].base2k()
|
||||
}
|
||||
|
||||
fn k(&self) -> TorusPrecision {
|
||||
self.keys[0].k()
|
||||
}
|
||||
fn size(&self) -> usize {
|
||||
self.keys[0].size()
|
||||
}
|
||||
}
|
||||
impl<D: Data> GLWEInfos for GGLWETensorKeyCompressed<D> {
|
||||
fn rank(&self) -> Rank {
|
||||
self.rank_out()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> GGLWELayoutInfos for GGLWETensorKeyCompressed<D> {
|
||||
fn rank_in(&self) -> Rank {
|
||||
self.rank_out()
|
||||
}
|
||||
|
||||
fn rank_out(&self) -> Rank {
|
||||
self.keys[0].rank_out()
|
||||
}
|
||||
|
||||
fn digits(&self) -> Digits {
|
||||
self.keys[0].digits()
|
||||
}
|
||||
|
||||
fn rows(&self) -> Rows {
|
||||
self.keys[0].rows()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> fmt::Debug for GGLWETensorKeyCompressed<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self)
|
||||
write!(f, "{self}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,76 +70,79 @@ impl<D: DataMut> FillUniform for GGLWETensorKeyCompressed<D> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> Reset for GGLWETensorKeyCompressed<D>
|
||||
where
|
||||
MatZnx<D>: Reset,
|
||||
{
|
||||
fn reset(&mut self) {
|
||||
self.keys
|
||||
.iter_mut()
|
||||
.for_each(|key: &mut GGLWESwitchingKeyCompressed<D>| key.reset())
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> fmt::Display for GGLWETensorKeyCompressed<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
writeln!(f, "(GLWETensorKeyCompressed)",)?;
|
||||
for (i, key) in self.keys.iter().enumerate() {
|
||||
write!(f, "{}: {}", i, key)?;
|
||||
write!(f, "{i}: {key}")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl GGLWETensorKeyCompressed<Vec<u8>> {
|
||||
pub fn alloc(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self {
|
||||
pub fn alloc<A>(infos: &A) -> Self
|
||||
where
|
||||
A: GGLWELayoutInfos,
|
||||
{
|
||||
assert_eq!(
|
||||
infos.rank_in(),
|
||||
infos.rank_out(),
|
||||
"rank_in != rank_out is not supported for GGLWETensorKeyCompressed"
|
||||
);
|
||||
Self::alloc_with(
|
||||
infos.n(),
|
||||
infos.base2k(),
|
||||
infos.k(),
|
||||
infos.rows(),
|
||||
infos.digits(),
|
||||
infos.rank_out(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, digits: Digits, rank: Rank) -> Self {
|
||||
let mut keys: Vec<GGLWESwitchingKeyCompressed<Vec<u8>>> = Vec::new();
|
||||
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
|
||||
let pairs: u32 = (((rank.0 + 1) * rank.0) >> 1).max(1);
|
||||
(0..pairs).for_each(|_| {
|
||||
keys.push(GGLWESwitchingKeyCompressed::alloc(
|
||||
n, basek, k, rows, digits, 1, rank,
|
||||
keys.push(GGLWESwitchingKeyCompressed::alloc_with(
|
||||
n,
|
||||
base2k,
|
||||
k,
|
||||
rows,
|
||||
digits,
|
||||
Rank(1),
|
||||
rank,
|
||||
));
|
||||
});
|
||||
Self { keys }
|
||||
}
|
||||
|
||||
pub fn bytes_of(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize {
|
||||
let pairs: usize = (((rank + 1) * rank) >> 1).max(1);
|
||||
pairs * GGLWESwitchingKeyCompressed::bytes_of(n, basek, k, rows, digits, 1)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> Infos for GGLWETensorKeyCompressed<D> {
|
||||
type Inner = MatZnx<D>;
|
||||
|
||||
fn inner(&self) -> &Self::Inner {
|
||||
self.keys[0].inner()
|
||||
pub fn alloc_bytes<A>(infos: &A) -> usize
|
||||
where
|
||||
A: GGLWELayoutInfos,
|
||||
{
|
||||
assert_eq!(
|
||||
infos.rank_in(),
|
||||
infos.rank_out(),
|
||||
"rank_in != rank_out is not supported for GGLWETensorKeyCompressed"
|
||||
);
|
||||
let rank_out: usize = infos.rank_out().into();
|
||||
let pairs: usize = (((rank_out + 1) * rank_out) >> 1).max(1);
|
||||
pairs
|
||||
* GGLWESwitchingKeyCompressed::alloc_bytes_with(
|
||||
infos.n(),
|
||||
infos.base2k(),
|
||||
infos.k(),
|
||||
infos.rows(),
|
||||
infos.digits(),
|
||||
Rank(1),
|
||||
infos.rank_out(),
|
||||
)
|
||||
}
|
||||
|
||||
fn basek(&self) -> usize {
|
||||
self.keys[0].basek()
|
||||
}
|
||||
|
||||
fn k(&self) -> usize {
|
||||
self.keys[0].k()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> GGLWETensorKeyCompressed<D> {
|
||||
pub fn rank(&self) -> usize {
|
||||
self.keys[0].rank()
|
||||
}
|
||||
|
||||
pub fn digits(&self) -> usize {
|
||||
self.keys[0].digits()
|
||||
}
|
||||
|
||||
pub fn rank_in(&self) -> usize {
|
||||
self.keys[0].rank_in()
|
||||
}
|
||||
|
||||
pub fn rank_out(&self) -> usize {
|
||||
self.keys[0].rank_out()
|
||||
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, digits: Digits, rank: Rank) -> usize {
|
||||
let pairs: usize = (((rank.0 + 1) * rank.0) >> 1).max(1) as usize;
|
||||
pairs * GGLWESwitchingKeyCompressed::alloc_bytes_with(n, base2k, k, rows, digits, Rank(1), rank)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +177,7 @@ impl<D: DataMut> GGLWETensorKeyCompressed<D> {
|
||||
if i > j {
|
||||
std::mem::swap(&mut i, &mut j);
|
||||
};
|
||||
let rank: usize = self.rank();
|
||||
let rank: usize = self.rank_out().into();
|
||||
&mut self.keys[i * rank + j - (i * (i + 1) / 2)]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use poulpy_hal::{
|
||||
api::{VecZnxCopy, VecZnxFillUniform},
|
||||
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, Reset, WriterTo},
|
||||
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, WriterTo, ZnxInfos},
|
||||
source::Source,
|
||||
};
|
||||
|
||||
use crate::layouts::{
|
||||
GGSWCiphertext, Infos,
|
||||
Base2K, Degree, Digits, GGSWCiphertext, GGSWInfos, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
|
||||
compressed::{Decompress, GLWECiphertextCompressed},
|
||||
};
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
@@ -14,13 +14,45 @@ use std::fmt;
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
pub struct GGSWCiphertextCompressed<D: Data> {
|
||||
pub(crate) data: MatZnx<D>,
|
||||
pub(crate) basek: usize,
|
||||
pub(crate) k: usize,
|
||||
pub(crate) digits: usize,
|
||||
pub(crate) rank: usize,
|
||||
pub(crate) k: TorusPrecision,
|
||||
pub(crate) base2k: Base2K,
|
||||
pub(crate) digits: Digits,
|
||||
pub(crate) rank: Rank,
|
||||
pub(crate) seed: Vec<[u8; 32]>,
|
||||
}
|
||||
|
||||
impl<D: Data> LWEInfos for GGSWCiphertextCompressed<D> {
|
||||
fn n(&self) -> Degree {
|
||||
Degree(self.data.n() as u32)
|
||||
}
|
||||
|
||||
fn base2k(&self) -> Base2K {
|
||||
self.base2k
|
||||
}
|
||||
|
||||
fn k(&self) -> TorusPrecision {
|
||||
self.k
|
||||
}
|
||||
fn size(&self) -> usize {
|
||||
self.data.size()
|
||||
}
|
||||
}
|
||||
impl<D: Data> GLWEInfos for GGSWCiphertextCompressed<D> {
|
||||
fn rank(&self) -> Rank {
|
||||
self.rank
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> GGSWInfos for GGSWCiphertextCompressed<D> {
|
||||
fn digits(&self) -> Digits {
|
||||
self.digits
|
||||
}
|
||||
|
||||
fn rows(&self) -> Rows {
|
||||
Rows(self.data.rows() as u32)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> fmt::Debug for GGSWCiphertextCompressed<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.data)
|
||||
@@ -31,23 +63,12 @@ impl<D: DataRef> fmt::Display for GGSWCiphertextCompressed<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"(GGSWCiphertextCompressed: basek={} k={} digits={}) {}",
|
||||
self.basek, self.k, self.digits, self.data
|
||||
"(GGSWCiphertextCompressed: base2k={} k={} digits={}) {}",
|
||||
self.base2k, self.k, self.digits, self.data
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> Reset for GGSWCiphertextCompressed<D> {
|
||||
fn reset(&mut self) {
|
||||
self.data.reset();
|
||||
self.basek = 0;
|
||||
self.k = 0;
|
||||
self.digits = 0;
|
||||
self.rank = 0;
|
||||
self.seed = Vec::new();
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> FillUniform for GGSWCiphertextCompressed<D> {
|
||||
fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
|
||||
self.data.fill_uniform(log_bound, source);
|
||||
@@ -55,114 +76,123 @@ impl<D: DataMut> FillUniform for GGSWCiphertextCompressed<D> {
|
||||
}
|
||||
|
||||
impl GGSWCiphertextCompressed<Vec<u8>> {
|
||||
pub fn alloc(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> Self {
|
||||
let size: usize = k.div_ceil(basek);
|
||||
debug_assert!(digits > 0, "invalid ggsw: `digits` == 0");
|
||||
pub fn alloc<A>(infos: &A) -> Self
|
||||
where
|
||||
A: GGSWInfos,
|
||||
{
|
||||
Self::alloc_with(
|
||||
infos.n(),
|
||||
infos.base2k(),
|
||||
infos.k(),
|
||||
infos.rows(),
|
||||
infos.digits(),
|
||||
infos.rank(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, digits: Digits, rank: Rank) -> Self {
|
||||
let size: usize = k.0.div_ceil(base2k.0) as usize;
|
||||
debug_assert!(
|
||||
size > digits,
|
||||
"invalid ggsw: ceil(k/basek): {} <= digits: {}",
|
||||
size,
|
||||
digits
|
||||
size as u32 > digits.0,
|
||||
"invalid ggsw: ceil(k/base2k): {size} <= digits: {}",
|
||||
digits.0
|
||||
);
|
||||
|
||||
assert!(
|
||||
rows * digits <= size,
|
||||
"invalid ggsw: rows: {} * digits:{} > ceil(k/basek): {}",
|
||||
rows,
|
||||
digits,
|
||||
size
|
||||
rows.0 * digits.0 <= size as u32,
|
||||
"invalid ggsw: rows: {} * digits:{} > ceil(k/base2k): {size}",
|
||||
rows.0,
|
||||
digits.0,
|
||||
);
|
||||
|
||||
Self {
|
||||
data: MatZnx::alloc(n, rows, rank + 1, 1, k.div_ceil(basek)),
|
||||
basek,
|
||||
data: MatZnx::alloc(
|
||||
n.into(),
|
||||
rows.into(),
|
||||
(rank + 1).into(),
|
||||
1,
|
||||
k.0.div_ceil(base2k.0) as usize,
|
||||
),
|
||||
k,
|
||||
base2k,
|
||||
digits,
|
||||
rank,
|
||||
seed: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bytes_of(n: usize, basek: usize, k: usize, rows: usize, digits: usize, rank: usize) -> usize {
|
||||
let size: usize = k.div_ceil(basek);
|
||||
pub fn alloc_bytes<A>(infos: &A) -> usize
|
||||
where
|
||||
A: GGSWInfos,
|
||||
{
|
||||
Self::alloc_bytes_with(
|
||||
infos.n(),
|
||||
infos.base2k(),
|
||||
infos.k(),
|
||||
infos.rows(),
|
||||
infos.digits(),
|
||||
infos.rank(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, digits: Digits, rank: Rank) -> usize {
|
||||
let size: usize = k.0.div_ceil(base2k.0) as usize;
|
||||
debug_assert!(
|
||||
size > digits,
|
||||
"invalid ggsw: ceil(k/basek): {} <= digits: {}",
|
||||
size,
|
||||
digits
|
||||
size as u32 > digits.0,
|
||||
"invalid ggsw: ceil(k/base2k): {size} <= digits: {}",
|
||||
digits.0
|
||||
);
|
||||
|
||||
assert!(
|
||||
rows * digits <= size,
|
||||
"invalid ggsw: rows: {} * digits:{} > ceil(k/basek): {}",
|
||||
rows,
|
||||
digits,
|
||||
size
|
||||
rows.0 * digits.0 <= size as u32,
|
||||
"invalid ggsw: rows: {} * digits:{} > ceil(k/base2k): {size}",
|
||||
rows.0,
|
||||
digits.0,
|
||||
);
|
||||
|
||||
MatZnx::alloc_bytes(n, rows, rank + 1, 1, size)
|
||||
MatZnx::alloc_bytes(
|
||||
n.into(),
|
||||
rows.into(),
|
||||
(rank + 1).into(),
|
||||
1,
|
||||
k.0.div_ceil(base2k.0) as usize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> GGSWCiphertextCompressed<D> {
|
||||
pub fn at(&self, row: usize, col: usize) -> GLWECiphertextCompressed<&[u8]> {
|
||||
let rank: usize = self.rank().into();
|
||||
GLWECiphertextCompressed {
|
||||
data: self.data.at(row, col),
|
||||
basek: self.basek,
|
||||
k: self.k,
|
||||
rank: self.rank(),
|
||||
seed: self.seed[row * (self.rank() + 1) + col],
|
||||
base2k: self.base2k,
|
||||
rank: self.rank,
|
||||
seed: self.seed[row * (rank + 1) + col],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> GGSWCiphertextCompressed<D> {
|
||||
pub fn at_mut(&mut self, row: usize, col: usize) -> GLWECiphertextCompressed<&mut [u8]> {
|
||||
let rank: usize = self.rank();
|
||||
let rank: usize = self.rank().into();
|
||||
GLWECiphertextCompressed {
|
||||
data: self.data.at_mut(row, col),
|
||||
basek: self.basek,
|
||||
k: self.k,
|
||||
rank,
|
||||
base2k: self.base2k,
|
||||
rank: self.rank,
|
||||
seed: self.seed[row * (rank + 1) + col],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> Infos for GGSWCiphertextCompressed<D> {
|
||||
type Inner = MatZnx<D>;
|
||||
|
||||
fn inner(&self) -> &Self::Inner {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn basek(&self) -> usize {
|
||||
self.basek
|
||||
}
|
||||
|
||||
fn k(&self) -> usize {
|
||||
self.k
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> GGSWCiphertextCompressed<D> {
|
||||
pub fn rank(&self) -> usize {
|
||||
self.rank
|
||||
}
|
||||
|
||||
pub fn digits(&self) -> usize {
|
||||
self.digits
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> ReaderFrom for GGSWCiphertextCompressed<D> {
|
||||
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
|
||||
self.k = reader.read_u64::<LittleEndian>()? as usize;
|
||||
self.basek = reader.read_u64::<LittleEndian>()? as usize;
|
||||
self.digits = reader.read_u64::<LittleEndian>()? as usize;
|
||||
self.rank = reader.read_u64::<LittleEndian>()? as usize;
|
||||
let seed_len = reader.read_u64::<LittleEndian>()? as usize;
|
||||
self.k = TorusPrecision(reader.read_u32::<LittleEndian>()?);
|
||||
self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
|
||||
self.digits = Digits(reader.read_u32::<LittleEndian>()?);
|
||||
self.rank = Rank(reader.read_u32::<LittleEndian>()?);
|
||||
let seed_len: usize = reader.read_u32::<LittleEndian>()? as usize;
|
||||
self.seed = vec![[0u8; 32]; seed_len];
|
||||
for s in &mut self.seed {
|
||||
reader.read_exact(s)?;
|
||||
@@ -173,11 +203,11 @@ impl<D: DataMut> ReaderFrom for GGSWCiphertextCompressed<D> {
|
||||
|
||||
impl<D: DataRef> WriterTo for GGSWCiphertextCompressed<D> {
|
||||
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
writer.write_u64::<LittleEndian>(self.k as u64)?;
|
||||
writer.write_u64::<LittleEndian>(self.basek as u64)?;
|
||||
writer.write_u64::<LittleEndian>(self.digits as u64)?;
|
||||
writer.write_u64::<LittleEndian>(self.rank as u64)?;
|
||||
writer.write_u64::<LittleEndian>(self.seed.len() as u64)?;
|
||||
writer.write_u32::<LittleEndian>(self.k.into())?;
|
||||
writer.write_u32::<LittleEndian>(self.base2k.into())?;
|
||||
writer.write_u32::<LittleEndian>(self.digits.into())?;
|
||||
writer.write_u32::<LittleEndian>(self.rank.into())?;
|
||||
writer.write_u32::<LittleEndian>(self.seed.len() as u32)?;
|
||||
for s in &self.seed {
|
||||
writer.write_all(s)?;
|
||||
}
|
||||
@@ -195,8 +225,8 @@ where
|
||||
assert_eq!(self.rank(), other.rank())
|
||||
}
|
||||
|
||||
let rows: usize = self.rows();
|
||||
let rank: usize = self.rank();
|
||||
let rows: usize = self.rows().into();
|
||||
let rank: usize = self.rank().into();
|
||||
(0..rows).for_each(|row_i| {
|
||||
(0..rank + 1).for_each(|col_j| {
|
||||
self.at_mut(row_i, col_j)
|
||||
|
||||
@@ -1,25 +1,48 @@
|
||||
use poulpy_hal::{
|
||||
api::{VecZnxCopy, VecZnxFillUniform},
|
||||
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, Reset, VecZnx, WriterTo},
|
||||
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, VecZnx, WriterTo, ZnxInfos},
|
||||
source::Source,
|
||||
};
|
||||
|
||||
use crate::layouts::{GLWECiphertext, Infos, compressed::Decompress};
|
||||
use crate::layouts::{Base2K, Degree, GLWECiphertext, GLWEInfos, LWEInfos, Rank, TorusPrecision, compressed::Decompress};
|
||||
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
pub struct GLWECiphertextCompressed<D: Data> {
|
||||
pub(crate) data: VecZnx<D>,
|
||||
pub(crate) basek: usize,
|
||||
pub(crate) k: usize,
|
||||
pub(crate) rank: usize,
|
||||
pub(crate) base2k: Base2K,
|
||||
pub(crate) k: TorusPrecision,
|
||||
pub(crate) rank: Rank,
|
||||
pub(crate) seed: [u8; 32],
|
||||
}
|
||||
|
||||
impl<D: Data> LWEInfos for GLWECiphertextCompressed<D> {
|
||||
fn base2k(&self) -> Base2K {
|
||||
self.base2k
|
||||
}
|
||||
|
||||
fn k(&self) -> TorusPrecision {
|
||||
self.k
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
self.data.size()
|
||||
}
|
||||
|
||||
fn n(&self) -> Degree {
|
||||
Degree(self.data.n() as u32)
|
||||
}
|
||||
}
|
||||
impl<D: Data> GLWEInfos for GLWECiphertextCompressed<D> {
|
||||
fn rank(&self) -> Rank {
|
||||
self.rank
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> fmt::Debug for GLWECiphertextCompressed<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self)
|
||||
write!(f, "{self}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,75 +50,57 @@ impl<D: DataRef> fmt::Display for GLWECiphertextCompressed<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"GLWECiphertextCompressed: basek={} k={} rank={} seed={:?}: {}",
|
||||
self.basek(),
|
||||
"GLWECiphertextCompressed: base2k={} k={} rank={} seed={:?}: {}",
|
||||
self.base2k(),
|
||||
self.k(),
|
||||
self.rank,
|
||||
self.rank(),
|
||||
self.seed,
|
||||
self.data
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> Reset for GLWECiphertextCompressed<D> {
|
||||
fn reset(&mut self) {
|
||||
self.data.reset();
|
||||
self.basek = 0;
|
||||
self.k = 0;
|
||||
self.rank = 0;
|
||||
self.seed = [0u8; 32];
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> FillUniform for GLWECiphertextCompressed<D> {
|
||||
fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
|
||||
self.data.fill_uniform(log_bound, source);
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> Infos for GLWECiphertextCompressed<D> {
|
||||
type Inner = VecZnx<D>;
|
||||
|
||||
fn inner(&self) -> &Self::Inner {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn basek(&self) -> usize {
|
||||
self.basek
|
||||
}
|
||||
|
||||
fn k(&self) -> usize {
|
||||
self.k
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> GLWECiphertextCompressed<D> {
|
||||
pub fn rank(&self) -> usize {
|
||||
self.rank
|
||||
}
|
||||
}
|
||||
|
||||
impl GLWECiphertextCompressed<Vec<u8>> {
|
||||
pub fn alloc(n: usize, basek: usize, k: usize, rank: usize) -> Self {
|
||||
pub fn alloc<A>(infos: &A) -> Self
|
||||
where
|
||||
A: GLWEInfos,
|
||||
{
|
||||
Self::alloc_with(infos.n(), infos.base2k(), infos.k(), infos.rank())
|
||||
}
|
||||
|
||||
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rank: Rank) -> Self {
|
||||
Self {
|
||||
data: VecZnx::alloc(n, 1, k.div_ceil(basek)),
|
||||
basek,
|
||||
data: VecZnx::alloc(n.into(), 1, k.0.div_ceil(base2k.0) as usize),
|
||||
base2k,
|
||||
k,
|
||||
rank,
|
||||
seed: [0u8; 32],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bytes_of(n: usize, basek: usize, k: usize) -> usize {
|
||||
GLWECiphertext::bytes_of(n, basek, k, 1)
|
||||
pub fn alloc_bytes<A>(infos: &A) -> usize
|
||||
where
|
||||
A: GLWEInfos,
|
||||
{
|
||||
Self::alloc_bytes_with(infos.n(), infos.base2k(), infos.k())
|
||||
}
|
||||
|
||||
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision) -> usize {
|
||||
VecZnx::alloc_bytes(n.into(), 1, k.0.div_ceil(base2k.0) as usize)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> ReaderFrom for GLWECiphertextCompressed<D> {
|
||||
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
|
||||
self.k = reader.read_u64::<LittleEndian>()? as usize;
|
||||
self.basek = reader.read_u64::<LittleEndian>()? as usize;
|
||||
self.rank = reader.read_u64::<LittleEndian>()? as usize;
|
||||
self.k = TorusPrecision(reader.read_u32::<LittleEndian>()?);
|
||||
self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
|
||||
self.rank = Rank(reader.read_u32::<LittleEndian>()?);
|
||||
reader.read_exact(&mut self.seed)?;
|
||||
self.data.read_from(reader)
|
||||
}
|
||||
@@ -103,9 +108,9 @@ impl<D: DataMut> ReaderFrom for GLWECiphertextCompressed<D> {
|
||||
|
||||
impl<D: DataRef> WriterTo for GLWECiphertextCompressed<D> {
|
||||
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
writer.write_u64::<LittleEndian>(self.k as u64)?;
|
||||
writer.write_u64::<LittleEndian>(self.basek as u64)?;
|
||||
writer.write_u64::<LittleEndian>(self.rank as u64)?;
|
||||
writer.write_u32::<LittleEndian>(self.k.into())?;
|
||||
writer.write_u32::<LittleEndian>(self.base2k.into())?;
|
||||
writer.write_u32::<LittleEndian>(self.rank.into())?;
|
||||
writer.write_all(&self.seed)?;
|
||||
self.data.write_to(writer)
|
||||
}
|
||||
@@ -118,14 +123,12 @@ where
|
||||
fn decompress(&mut self, module: &Module<B>, other: &GLWECiphertextCompressed<DR>) {
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
use poulpy_hal::layouts::ZnxInfos;
|
||||
|
||||
assert_eq!(
|
||||
self.n(),
|
||||
other.data.n(),
|
||||
other.n(),
|
||||
"invalid receiver: self.n()={} != other.n()={}",
|
||||
self.n(),
|
||||
other.data.n()
|
||||
other.n()
|
||||
);
|
||||
assert_eq!(
|
||||
self.size(),
|
||||
@@ -164,15 +167,12 @@ impl<D: DataMut> GLWECiphertext<D> {
|
||||
debug_assert_eq!(self.size(), other.size());
|
||||
}
|
||||
|
||||
let k: usize = other.k;
|
||||
let basek: usize = other.basek;
|
||||
let cols: usize = other.rank() + 1;
|
||||
module.vec_znx_copy(&mut self.data, 0, &other.data, 0);
|
||||
(1..cols).for_each(|i| {
|
||||
module.vec_znx_fill_uniform(basek, &mut self.data, i, source);
|
||||
(1..(other.rank() + 1).into()).for_each(|i| {
|
||||
module.vec_znx_fill_uniform(other.base2k.into(), &mut self.data, i, source);
|
||||
});
|
||||
|
||||
self.basek = basek;
|
||||
self.k = k;
|
||||
self.base2k = other.base2k;
|
||||
self.k = other.k;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,62 @@
|
||||
use std::fmt;
|
||||
|
||||
use poulpy_hal::{
|
||||
api::{
|
||||
SvpApplyDftToDftInplace, SvpPPolAlloc, SvpPPolAllocBytes, SvpPrepare, VecZnxAddInplace, VecZnxAddNormal,
|
||||
VecZnxBigNormalize, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxFillUniform, VecZnxIdftApplyConsume, VecZnxNormalize,
|
||||
VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubABInplace,
|
||||
},
|
||||
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, Reset, WriterTo},
|
||||
layouts::{Data, DataMut, DataRef, FillUniform, ReaderFrom, WriterTo},
|
||||
source::Source,
|
||||
};
|
||||
|
||||
use crate::layouts::{GLWEToLWESwitchingKey, Infos, compressed::GGLWESwitchingKeyCompressed};
|
||||
use crate::layouts::{
|
||||
Base2K, Degree, Digits, GGLWELayoutInfos, GLWEInfos, LWEInfos, Rank, Rows, TorusPrecision,
|
||||
compressed::GGLWESwitchingKeyCompressed,
|
||||
};
|
||||
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
pub struct GLWEToLWESwitchingKeyCompressed<D: Data>(pub(crate) GGLWESwitchingKeyCompressed<D>);
|
||||
|
||||
impl<D: Data> LWEInfos for GLWEToLWESwitchingKeyCompressed<D> {
|
||||
fn base2k(&self) -> Base2K {
|
||||
self.0.base2k()
|
||||
}
|
||||
|
||||
fn k(&self) -> TorusPrecision {
|
||||
self.0.k()
|
||||
}
|
||||
|
||||
fn n(&self) -> Degree {
|
||||
self.0.n()
|
||||
}
|
||||
fn size(&self) -> usize {
|
||||
self.0.size()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> GLWEInfos for GLWEToLWESwitchingKeyCompressed<D> {
|
||||
fn rank(&self) -> Rank {
|
||||
self.rank_out()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> GGLWELayoutInfos for GLWEToLWESwitchingKeyCompressed<D> {
|
||||
fn rank_in(&self) -> Rank {
|
||||
self.0.rank_in()
|
||||
}
|
||||
|
||||
fn digits(&self) -> Digits {
|
||||
self.0.digits()
|
||||
}
|
||||
|
||||
fn rank_out(&self) -> Rank {
|
||||
self.0.rank_out()
|
||||
}
|
||||
|
||||
fn rows(&self) -> Rows {
|
||||
self.0.rows()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> fmt::Debug for GLWEToLWESwitchingKeyCompressed<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self)
|
||||
write!(f, "{self}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,52 +66,12 @@ impl<D: DataMut> FillUniform for GLWEToLWESwitchingKeyCompressed<D> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> Reset for GLWEToLWESwitchingKeyCompressed<D> {
|
||||
fn reset(&mut self) {
|
||||
self.0.reset();
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> fmt::Display for GLWEToLWESwitchingKeyCompressed<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "(GLWEToLWESwitchingKeyCompressed) {}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> Infos for GLWEToLWESwitchingKeyCompressed<D> {
|
||||
type Inner = MatZnx<D>;
|
||||
|
||||
fn inner(&self) -> &Self::Inner {
|
||||
self.0.inner()
|
||||
}
|
||||
|
||||
fn basek(&self) -> usize {
|
||||
self.0.basek()
|
||||
}
|
||||
|
||||
fn k(&self) -> usize {
|
||||
self.0.k()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> GLWEToLWESwitchingKeyCompressed<D> {
|
||||
pub fn digits(&self) -> usize {
|
||||
self.0.digits()
|
||||
}
|
||||
|
||||
pub fn rank(&self) -> usize {
|
||||
self.0.rank()
|
||||
}
|
||||
|
||||
pub fn rank_in(&self) -> usize {
|
||||
self.0.rank_in()
|
||||
}
|
||||
|
||||
pub fn rank_out(&self) -> usize {
|
||||
self.0.rank_out()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> ReaderFrom for GLWEToLWESwitchingKeyCompressed<D> {
|
||||
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
|
||||
self.0.read_from(reader)
|
||||
@@ -86,31 +85,53 @@ impl<D: DataRef> WriterTo for GLWEToLWESwitchingKeyCompressed<D> {
|
||||
}
|
||||
|
||||
impl GLWEToLWESwitchingKeyCompressed<Vec<u8>> {
|
||||
pub fn alloc(n: usize, basek: usize, k: usize, rows: usize, rank_in: usize) -> Self {
|
||||
Self(GGLWESwitchingKeyCompressed::alloc(
|
||||
n, basek, k, rows, 1, rank_in, 1,
|
||||
pub fn alloc<A>(infos: &A) -> Self
|
||||
where
|
||||
A: GGLWELayoutInfos,
|
||||
{
|
||||
debug_assert_eq!(
|
||||
infos.rank_out().0,
|
||||
1,
|
||||
"rank_out > 1 is unsupported for GLWEToLWESwitchingKeyCompressed"
|
||||
);
|
||||
debug_assert_eq!(
|
||||
infos.digits().0,
|
||||
1,
|
||||
"digits > 1 is unsupported for GLWEToLWESwitchingKeyCompressed"
|
||||
);
|
||||
Self(GGLWESwitchingKeyCompressed::alloc(infos))
|
||||
}
|
||||
|
||||
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, rank_in: Rank) -> Self {
|
||||
Self(GGLWESwitchingKeyCompressed::alloc_with(
|
||||
n,
|
||||
base2k,
|
||||
k,
|
||||
rows,
|
||||
Digits(1),
|
||||
rank_in,
|
||||
Rank(1),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn encrypt_sk_scratch_space<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank_in: usize) -> usize
|
||||
pub fn alloc_bytes<A>(infos: &A) -> usize
|
||||
where
|
||||
Module<B>: VecZnxDftAllocBytes
|
||||
+ VecZnxBigNormalize<B>
|
||||
+ VecZnxDftApply<B>
|
||||
+ SvpApplyDftToDftInplace<B>
|
||||
+ VecZnxIdftApplyConsume<B>
|
||||
+ VecZnxNormalizeTmpBytes
|
||||
+ VecZnxFillUniform
|
||||
+ VecZnxSubABInplace
|
||||
+ VecZnxAddInplace
|
||||
+ VecZnxNormalizeInplace<B>
|
||||
+ VecZnxAddNormal
|
||||
+ VecZnxNormalize<B>
|
||||
+ VecZnxSub
|
||||
+ SvpPrepare<B>
|
||||
+ SvpPPolAllocBytes
|
||||
+ SvpPPolAlloc<B>,
|
||||
A: GGLWELayoutInfos,
|
||||
{
|
||||
GLWEToLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k, rank_in)
|
||||
debug_assert_eq!(
|
||||
infos.rank_out().0,
|
||||
1,
|
||||
"rank_out > 1 is unsupported for GLWEToLWESwitchingKeyCompressed"
|
||||
);
|
||||
debug_assert_eq!(
|
||||
infos.digits().0,
|
||||
1,
|
||||
"digits > 1 is unsupported for GLWEToLWESwitchingKeyCompressed"
|
||||
);
|
||||
GGLWESwitchingKeyCompressed::alloc_bytes(infos)
|
||||
}
|
||||
|
||||
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, rank_in: Rank) -> usize {
|
||||
GGLWESwitchingKeyCompressed::alloc_bytes_with(n, base2k, k, rows, Digits(1), rank_in, Rank(1))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,25 +2,41 @@ use std::fmt;
|
||||
|
||||
use poulpy_hal::{
|
||||
api::ZnFillUniform,
|
||||
layouts::{
|
||||
Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, Reset, VecZnx, WriterTo, ZnxInfos, ZnxView, ZnxViewMut,
|
||||
},
|
||||
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, WriterTo, Zn, ZnxInfos, ZnxView, ZnxViewMut},
|
||||
source::Source,
|
||||
};
|
||||
|
||||
use crate::layouts::{Infos, LWECiphertext, SetMetaData, compressed::Decompress};
|
||||
use crate::layouts::{Base2K, Degree, LWECiphertext, LWEInfos, TorusPrecision, compressed::Decompress};
|
||||
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
pub struct LWECiphertextCompressed<D: Data> {
|
||||
pub(crate) data: VecZnx<D>,
|
||||
pub(crate) k: usize,
|
||||
pub(crate) basek: usize,
|
||||
pub(crate) data: Zn<D>,
|
||||
pub(crate) k: TorusPrecision,
|
||||
pub(crate) base2k: Base2K,
|
||||
pub(crate) seed: [u8; 32],
|
||||
}
|
||||
|
||||
impl<D: Data> LWEInfos for LWECiphertextCompressed<D> {
|
||||
fn base2k(&self) -> Base2K {
|
||||
self.base2k
|
||||
}
|
||||
|
||||
fn k(&self) -> TorusPrecision {
|
||||
self.k
|
||||
}
|
||||
|
||||
fn n(&self) -> Degree {
|
||||
Degree(self.data.n() as u32)
|
||||
}
|
||||
|
||||
fn size(&self) -> usize {
|
||||
self.data.size()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> fmt::Debug for LWECiphertextCompressed<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self)
|
||||
write!(f, "{self}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +44,8 @@ impl<D: DataRef> fmt::Display for LWECiphertextCompressed<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"LWECiphertextCompressed: basek={} k={} seed={:?}: {}",
|
||||
self.basek(),
|
||||
"LWECiphertextCompressed: base2k={} k={} seed={:?}: {}",
|
||||
self.base2k(),
|
||||
self.k(),
|
||||
self.seed,
|
||||
self.data
|
||||
@@ -37,18 +53,6 @@ impl<D: DataRef> fmt::Display for LWECiphertextCompressed<D> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> Reset for LWECiphertextCompressed<D>
|
||||
where
|
||||
VecZnx<D>: Reset,
|
||||
{
|
||||
fn reset(&mut self) {
|
||||
self.data.reset();
|
||||
self.basek = 0;
|
||||
self.k = 0;
|
||||
self.seed = [0u8; 32];
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> FillUniform for LWECiphertextCompressed<D> {
|
||||
fn fill_uniform(&mut self, log_bound: usize, source: &mut Source) {
|
||||
self.data.fill_uniform(log_bound, source);
|
||||
@@ -56,46 +60,31 @@ impl<D: DataMut> FillUniform for LWECiphertextCompressed<D> {
|
||||
}
|
||||
|
||||
impl LWECiphertextCompressed<Vec<u8>> {
|
||||
pub fn alloc(basek: usize, k: usize) -> Self {
|
||||
pub fn alloc<A>(infos: &A) -> Self
|
||||
where
|
||||
A: LWEInfos,
|
||||
{
|
||||
Self::alloc_with(infos.base2k(), infos.k())
|
||||
}
|
||||
|
||||
pub fn alloc_with(base2k: Base2K, k: TorusPrecision) -> Self {
|
||||
Self {
|
||||
data: VecZnx::alloc(1, 1, k.div_ceil(basek)),
|
||||
data: Zn::alloc(1, 1, k.0.div_ceil(base2k.0) as usize),
|
||||
k,
|
||||
basek,
|
||||
base2k,
|
||||
seed: [0u8; 32],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> Infos for LWECiphertextCompressed<D>
|
||||
where
|
||||
VecZnx<D>: ZnxInfos,
|
||||
{
|
||||
type Inner = VecZnx<D>;
|
||||
|
||||
fn n(&self) -> usize {
|
||||
&self.inner().n() - 1
|
||||
pub fn alloc_bytes<A>(infos: &A) -> usize
|
||||
where
|
||||
A: LWEInfos,
|
||||
{
|
||||
Self::alloc_bytes_with(infos.base2k(), infos.k())
|
||||
}
|
||||
|
||||
fn inner(&self) -> &Self::Inner {
|
||||
&self.data
|
||||
}
|
||||
|
||||
fn basek(&self) -> usize {
|
||||
self.basek
|
||||
}
|
||||
|
||||
fn k(&self) -> usize {
|
||||
self.k
|
||||
}
|
||||
}
|
||||
|
||||
impl<DataSelf: DataMut> SetMetaData for LWECiphertextCompressed<DataSelf> {
|
||||
fn set_k(&mut self, k: usize) {
|
||||
self.k = k
|
||||
}
|
||||
|
||||
fn set_basek(&mut self, basek: usize) {
|
||||
self.basek = basek
|
||||
pub fn alloc_bytes_with(base2k: Base2K, k: TorusPrecision) -> usize {
|
||||
Zn::alloc_bytes(1, 1, k.0.div_ceil(base2k.0) as usize)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,8 +92,8 @@ use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||
|
||||
impl<D: DataMut> ReaderFrom for LWECiphertextCompressed<D> {
|
||||
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
|
||||
self.k = reader.read_u64::<LittleEndian>()? as usize;
|
||||
self.basek = reader.read_u64::<LittleEndian>()? as usize;
|
||||
self.k = TorusPrecision(reader.read_u32::<LittleEndian>()?);
|
||||
self.base2k = Base2K(reader.read_u32::<LittleEndian>()?);
|
||||
reader.read_exact(&mut self.seed)?;
|
||||
self.data.read_from(reader)
|
||||
}
|
||||
@@ -112,8 +101,8 @@ impl<D: DataMut> ReaderFrom for LWECiphertextCompressed<D> {
|
||||
|
||||
impl<D: DataRef> WriterTo for LWECiphertextCompressed<D> {
|
||||
fn write_to<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> {
|
||||
writer.write_u64::<LittleEndian>(self.k as u64)?;
|
||||
writer.write_u64::<LittleEndian>(self.basek as u64)?;
|
||||
writer.write_u32::<LittleEndian>(self.k.into())?;
|
||||
writer.write_u32::<LittleEndian>(self.base2k.into())?;
|
||||
writer.write_all(&self.seed)?;
|
||||
self.data.write_to(writer)
|
||||
}
|
||||
@@ -126,7 +115,13 @@ where
|
||||
fn decompress(&mut self, module: &Module<B>, other: &LWECiphertextCompressed<DR>) {
|
||||
debug_assert_eq!(self.size(), other.size());
|
||||
let mut source: Source = Source::new(other.seed);
|
||||
module.zn_fill_uniform(self.n(), other.basek(), &mut self.data, 0, &mut source);
|
||||
module.zn_fill_uniform(
|
||||
self.n().into(),
|
||||
other.base2k().into(),
|
||||
&mut self.data,
|
||||
0,
|
||||
&mut source,
|
||||
);
|
||||
(0..self.size()).for_each(|i| {
|
||||
self.data.at_mut(0, i)[0] = other.data.at(0, i)[0];
|
||||
});
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
use poulpy_hal::{
|
||||
api::{
|
||||
SvpApplyDftToDftInplace, SvpPPolAlloc, SvpPPolAllocBytes, SvpPrepare, VecZnxAddInplace, VecZnxAddNormal,
|
||||
VecZnxBigNormalize, VecZnxCopy, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxFillUniform, VecZnxIdftApplyConsume,
|
||||
VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubABInplace,
|
||||
},
|
||||
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, Reset, WriterTo},
|
||||
api::{VecZnxCopy, VecZnxFillUniform},
|
||||
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, WriterTo},
|
||||
source::Source,
|
||||
};
|
||||
|
||||
use crate::layouts::{
|
||||
Infos, LWESwitchingKey,
|
||||
Base2K, Degree, Digits, GGLWELayoutInfos, GLWEInfos, LWEInfos, LWESwitchingKey, Rank, Rows, TorusPrecision,
|
||||
compressed::{Decompress, GGLWESwitchingKeyCompressed},
|
||||
};
|
||||
use std::fmt;
|
||||
@@ -17,9 +13,49 @@ use std::fmt;
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
pub struct LWESwitchingKeyCompressed<D: Data>(pub(crate) GGLWESwitchingKeyCompressed<D>);
|
||||
|
||||
impl<D: Data> LWEInfos for LWESwitchingKeyCompressed<D> {
|
||||
fn base2k(&self) -> Base2K {
|
||||
self.0.base2k()
|
||||
}
|
||||
|
||||
fn k(&self) -> TorusPrecision {
|
||||
self.0.k()
|
||||
}
|
||||
|
||||
fn n(&self) -> Degree {
|
||||
self.0.n()
|
||||
}
|
||||
fn size(&self) -> usize {
|
||||
self.0.size()
|
||||
}
|
||||
}
|
||||
impl<D: Data> GLWEInfos for LWESwitchingKeyCompressed<D> {
|
||||
fn rank(&self) -> Rank {
|
||||
self.rank_out()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> GGLWELayoutInfos for LWESwitchingKeyCompressed<D> {
|
||||
fn digits(&self) -> Digits {
|
||||
self.0.digits()
|
||||
}
|
||||
|
||||
fn rank_in(&self) -> Rank {
|
||||
self.0.rank_in()
|
||||
}
|
||||
|
||||
fn rank_out(&self) -> Rank {
|
||||
self.0.rank_out()
|
||||
}
|
||||
|
||||
fn rows(&self) -> Rows {
|
||||
self.0.rows()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> fmt::Debug for LWESwitchingKeyCompressed<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self)
|
||||
write!(f, "{self}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,52 +65,12 @@ impl<D: DataMut> FillUniform for LWESwitchingKeyCompressed<D> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> Reset for LWESwitchingKeyCompressed<D> {
|
||||
fn reset(&mut self) {
|
||||
self.0.reset();
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> fmt::Display for LWESwitchingKeyCompressed<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "(LWESwitchingKeyCompressed) {}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> Infos for LWESwitchingKeyCompressed<D> {
|
||||
type Inner = MatZnx<D>;
|
||||
|
||||
fn inner(&self) -> &Self::Inner {
|
||||
self.0.inner()
|
||||
}
|
||||
|
||||
fn basek(&self) -> usize {
|
||||
self.0.basek()
|
||||
}
|
||||
|
||||
fn k(&self) -> usize {
|
||||
self.0.k()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> LWESwitchingKeyCompressed<D> {
|
||||
pub fn digits(&self) -> usize {
|
||||
self.0.digits()
|
||||
}
|
||||
|
||||
pub fn rank(&self) -> usize {
|
||||
self.0.rank()
|
||||
}
|
||||
|
||||
pub fn rank_in(&self) -> usize {
|
||||
self.0.rank_in()
|
||||
}
|
||||
|
||||
pub fn rank_out(&self) -> usize {
|
||||
self.0.rank_out()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> ReaderFrom for LWESwitchingKeyCompressed<D> {
|
||||
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
|
||||
self.0.read_from(reader)
|
||||
@@ -88,32 +84,64 @@ impl<D: DataRef> WriterTo for LWESwitchingKeyCompressed<D> {
|
||||
}
|
||||
|
||||
impl LWESwitchingKeyCompressed<Vec<u8>> {
|
||||
pub fn alloc(n: usize, basek: usize, k: usize, rows: usize) -> Self {
|
||||
Self(GGLWESwitchingKeyCompressed::alloc(
|
||||
n, basek, k, rows, 1, 1, 1,
|
||||
pub fn alloc<A>(infos: &A) -> Self
|
||||
where
|
||||
A: GGLWELayoutInfos,
|
||||
{
|
||||
debug_assert_eq!(
|
||||
infos.digits().0,
|
||||
1,
|
||||
"digits > 1 is not supported for LWESwitchingKeyCompressed"
|
||||
);
|
||||
debug_assert_eq!(
|
||||
infos.rank_in().0,
|
||||
1,
|
||||
"rank_in > 1 is not supported for LWESwitchingKeyCompressed"
|
||||
);
|
||||
debug_assert_eq!(
|
||||
infos.rank_out().0,
|
||||
1,
|
||||
"rank_out > 1 is not supported for LWESwitchingKeyCompressed"
|
||||
);
|
||||
Self(GGLWESwitchingKeyCompressed::alloc(infos))
|
||||
}
|
||||
|
||||
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows) -> Self {
|
||||
Self(GGLWESwitchingKeyCompressed::alloc_with(
|
||||
n,
|
||||
base2k,
|
||||
k,
|
||||
rows,
|
||||
Digits(1),
|
||||
Rank(1),
|
||||
Rank(1),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn encrypt_sk_scratch_space<B: Backend>(module: &Module<B>, basek: usize, k: usize) -> usize
|
||||
pub fn alloc_bytes<A>(infos: &A) -> usize
|
||||
where
|
||||
Module<B>: VecZnxDftAllocBytes
|
||||
+ VecZnxBigNormalize<B>
|
||||
+ VecZnxDftApply<B>
|
||||
+ SvpApplyDftToDftInplace<B>
|
||||
+ VecZnxIdftApplyConsume<B>
|
||||
+ VecZnxNormalizeTmpBytes
|
||||
+ VecZnxFillUniform
|
||||
+ VecZnxSubABInplace
|
||||
+ VecZnxAddInplace
|
||||
+ VecZnxNormalizeInplace<B>
|
||||
+ VecZnxAddNormal
|
||||
+ VecZnxNormalize<B>
|
||||
+ VecZnxSub
|
||||
+ SvpPrepare<B>
|
||||
+ SvpPPolAllocBytes
|
||||
+ SvpPPolAlloc<B>,
|
||||
A: GGLWELayoutInfos,
|
||||
{
|
||||
LWESwitchingKey::encrypt_sk_scratch_space(module, basek, k)
|
||||
debug_assert_eq!(
|
||||
infos.digits().0,
|
||||
1,
|
||||
"digits > 1 is not supported for LWESwitchingKey"
|
||||
);
|
||||
debug_assert_eq!(
|
||||
infos.rank_in().0,
|
||||
1,
|
||||
"rank_in > 1 is not supported for LWESwitchingKey"
|
||||
);
|
||||
debug_assert_eq!(
|
||||
infos.rank_out().0,
|
||||
1,
|
||||
"rank_out > 1 is not supported for LWESwitchingKey"
|
||||
);
|
||||
GGLWESwitchingKeyCompressed::alloc_bytes(infos)
|
||||
}
|
||||
|
||||
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows) -> usize {
|
||||
GGLWESwitchingKeyCompressed::alloc_bytes_with(n, base2k, k, rows, Digits(1), Rank(1), Rank(1))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
use poulpy_hal::{
|
||||
api::{
|
||||
SvpApplyDftToDftInplace, SvpPPolAlloc, SvpPPolAllocBytes, SvpPrepare, VecZnxAddInplace, VecZnxAddNormal,
|
||||
VecZnxBigNormalize, VecZnxCopy, VecZnxDftAllocBytes, VecZnxDftApply, VecZnxFillUniform, VecZnxIdftApplyConsume,
|
||||
VecZnxNormalize, VecZnxNormalizeInplace, VecZnxNormalizeTmpBytes, VecZnxSub, VecZnxSubABInplace,
|
||||
},
|
||||
layouts::{Backend, Data, DataMut, DataRef, FillUniform, MatZnx, Module, ReaderFrom, Reset, WriterTo},
|
||||
api::{VecZnxCopy, VecZnxFillUniform},
|
||||
layouts::{Backend, Data, DataMut, DataRef, FillUniform, Module, ReaderFrom, WriterTo},
|
||||
source::Source,
|
||||
};
|
||||
|
||||
use crate::layouts::{
|
||||
Infos, LWEToGLWESwitchingKey,
|
||||
Base2K, Degree, Digits, GGLWELayoutInfos, GLWEInfos, LWEInfos, LWEToGLWESwitchingKey, Rank, Rows, TorusPrecision,
|
||||
compressed::{Decompress, GGLWESwitchingKeyCompressed},
|
||||
};
|
||||
use std::fmt;
|
||||
@@ -17,9 +13,49 @@ use std::fmt;
|
||||
#[derive(PartialEq, Eq, Clone)]
|
||||
pub struct LWEToGLWESwitchingKeyCompressed<D: Data>(pub(crate) GGLWESwitchingKeyCompressed<D>);
|
||||
|
||||
impl<D: Data> LWEInfos for LWEToGLWESwitchingKeyCompressed<D> {
|
||||
fn n(&self) -> Degree {
|
||||
self.0.n()
|
||||
}
|
||||
|
||||
fn base2k(&self) -> Base2K {
|
||||
self.0.base2k()
|
||||
}
|
||||
|
||||
fn k(&self) -> TorusPrecision {
|
||||
self.0.k()
|
||||
}
|
||||
fn size(&self) -> usize {
|
||||
self.0.size()
|
||||
}
|
||||
}
|
||||
impl<D: Data> GLWEInfos for LWEToGLWESwitchingKeyCompressed<D> {
|
||||
fn rank(&self) -> Rank {
|
||||
self.rank_out()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> GGLWELayoutInfos for LWEToGLWESwitchingKeyCompressed<D> {
|
||||
fn digits(&self) -> Digits {
|
||||
self.0.digits()
|
||||
}
|
||||
|
||||
fn rank_in(&self) -> Rank {
|
||||
self.0.rank_in()
|
||||
}
|
||||
|
||||
fn rank_out(&self) -> Rank {
|
||||
self.0.rank_out()
|
||||
}
|
||||
|
||||
fn rows(&self) -> Rows {
|
||||
self.0.rows()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> fmt::Debug for LWEToGLWESwitchingKeyCompressed<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self)
|
||||
write!(f, "{self}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,52 +65,12 @@ impl<D: DataMut> FillUniform for LWEToGLWESwitchingKeyCompressed<D> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> Reset for LWEToGLWESwitchingKeyCompressed<D> {
|
||||
fn reset(&mut self) {
|
||||
self.0.reset();
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataRef> fmt::Display for LWEToGLWESwitchingKeyCompressed<D> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "(LWEToGLWESwitchingKeyCompressed) {}", self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> Infos for LWEToGLWESwitchingKeyCompressed<D> {
|
||||
type Inner = MatZnx<D>;
|
||||
|
||||
fn inner(&self) -> &Self::Inner {
|
||||
self.0.inner()
|
||||
}
|
||||
|
||||
fn basek(&self) -> usize {
|
||||
self.0.basek()
|
||||
}
|
||||
|
||||
fn k(&self) -> usize {
|
||||
self.0.k()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Data> LWEToGLWESwitchingKeyCompressed<D> {
|
||||
pub fn digits(&self) -> usize {
|
||||
self.0.digits()
|
||||
}
|
||||
|
||||
pub fn rank(&self) -> usize {
|
||||
self.0.rank()
|
||||
}
|
||||
|
||||
pub fn rank_in(&self) -> usize {
|
||||
self.0.rank_in()
|
||||
}
|
||||
|
||||
pub fn rank_out(&self) -> usize {
|
||||
self.0.rank_out()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: DataMut> ReaderFrom for LWEToGLWESwitchingKeyCompressed<D> {
|
||||
fn read_from<R: std::io::Read>(&mut self, reader: &mut R) -> std::io::Result<()> {
|
||||
self.0.read_from(reader)
|
||||
@@ -88,32 +84,54 @@ impl<D: DataRef> WriterTo for LWEToGLWESwitchingKeyCompressed<D> {
|
||||
}
|
||||
|
||||
impl LWEToGLWESwitchingKeyCompressed<Vec<u8>> {
|
||||
pub fn alloc(n: usize, basek: usize, k: usize, rows: usize, rank_out: usize) -> Self {
|
||||
Self(GGLWESwitchingKeyCompressed::alloc(
|
||||
n, basek, k, rows, 1, 1, rank_out,
|
||||
pub fn alloc<A>(infos: &A) -> Self
|
||||
where
|
||||
A: GGLWELayoutInfos,
|
||||
{
|
||||
debug_assert_eq!(
|
||||
infos.digits().0,
|
||||
1,
|
||||
"digits > 1 is not supported for LWEToGLWESwitchingKeyCompressed"
|
||||
);
|
||||
debug_assert_eq!(
|
||||
infos.rank_in().0,
|
||||
1,
|
||||
"rank_in > 1 is not supported for LWEToGLWESwitchingKeyCompressed"
|
||||
);
|
||||
Self(GGLWESwitchingKeyCompressed::alloc(infos))
|
||||
}
|
||||
|
||||
pub fn alloc_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, rank_out: Rank) -> Self {
|
||||
Self(GGLWESwitchingKeyCompressed::alloc_with(
|
||||
n,
|
||||
base2k,
|
||||
k,
|
||||
rows,
|
||||
Digits(1),
|
||||
Rank(1),
|
||||
rank_out,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn encrypt_sk_scratch_space<B: Backend>(module: &Module<B>, basek: usize, k: usize, rank_out: usize) -> usize
|
||||
pub fn alloc_bytes<A>(infos: &A) -> usize
|
||||
where
|
||||
Module<B>: VecZnxDftAllocBytes
|
||||
+ VecZnxBigNormalize<B>
|
||||
+ VecZnxDftApply<B>
|
||||
+ SvpApplyDftToDftInplace<B>
|
||||
+ VecZnxIdftApplyConsume<B>
|
||||
+ VecZnxNormalizeTmpBytes
|
||||
+ VecZnxFillUniform
|
||||
+ VecZnxSubABInplace
|
||||
+ VecZnxAddInplace
|
||||
+ VecZnxNormalizeInplace<B>
|
||||
+ VecZnxAddNormal
|
||||
+ VecZnxNormalize<B>
|
||||
+ VecZnxSub
|
||||
+ SvpPrepare<B>
|
||||
+ SvpPPolAllocBytes
|
||||
+ SvpPPolAlloc<B>,
|
||||
A: GGLWELayoutInfos,
|
||||
{
|
||||
LWEToGLWESwitchingKey::encrypt_sk_scratch_space(module, basek, k, rank_out)
|
||||
debug_assert_eq!(
|
||||
infos.rank_in().0,
|
||||
1,
|
||||
"rank_in > 1 is not supported for LWEToGLWESwitchingKey"
|
||||
);
|
||||
debug_assert_eq!(
|
||||
infos.digits().0,
|
||||
1,
|
||||
"digits > 1 is not supported for LWEToGLWESwitchingKey"
|
||||
);
|
||||
GGLWESwitchingKeyCompressed::alloc_bytes(infos)
|
||||
}
|
||||
|
||||
pub fn alloc_bytes_with(n: Degree, base2k: Base2K, k: TorusPrecision, rows: Rows, rank_out: Rank) -> usize {
|
||||
GGLWESwitchingKeyCompressed::alloc_bytes_with(n, base2k, k, rows, Digits(1), Rank(1), rank_out)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user