├── .gitignore ├── CModulus.cpp ├── CModulus.h ├── Ciphertext.cpp ├── Ciphertext.h ├── DoubleCRT.cpp ├── DoubleCRT.h ├── FHE-SI.cpp ├── FHE-SI.h ├── FHEContext.cpp ├── FHEContext.h ├── IndexMap.h ├── IndexSet.cpp ├── IndexSet.h ├── LICENSE ├── Makefile ├── Matrix.cpp ├── Matrix.h ├── NumbTh.cpp ├── NumbTh.h ├── PAlgebra.cpp ├── PAlgebra.h ├── PAlgebraMod.cpp ├── Plaintext.cpp ├── Plaintext.h ├── PlaintextSpace.cpp ├── PlaintextSpace.h ├── README ├── Regression.h ├── Serialization.cpp ├── Serialization.h ├── SingleCRT.cpp ├── SingleCRT.h ├── Statistics.h ├── Test_AddMul.cpp ├── Test_General.cpp ├── Test_Regression.cpp ├── Test_Statistics.cpp ├── Util.cpp ├── Util.h ├── bluestein.cpp ├── bluestein.h └── scripts └── generateRandomData.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | build/ 4 | tests/ 5 | *.sh 6 | *~ 7 | *.out 8 | *.output 9 | -------------------------------------------------------------------------------- /CModulus.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012,2013 IBM Corp. 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | * See the GNU General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License along 13 | * with this program; if not, write to the Free Software Foundation, Inc., 14 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 15 | */ 16 | 17 | /* 18 | * CModulus.cpp - supports forward and backward length-m FFT transformations 19 | * 20 | * This is a wrapper around the bluesteinFFT routines, for one modulus q. 21 | * Two classes are defined here, Cmodulus for a small moduli (long) and 22 | * CModulus for a large ones (ZZ). These classes are otherwise identical 23 | * hence they are implemented using a class template. 24 | * 25 | * On initialization, it initizlies NTL's zz_pContext/ZZ_pContext for this q 26 | * and computes a 2m-th root of unity r mod q and also r^{-1} mod q. 27 | * Thereafter this class provides FFT and iFFT routines that converts between 28 | * time & frequency domains. Some tables are computed the first time that 29 | * each dierctions is called, which are then used in subsequent computations. 30 | * 31 | * The "time domain" polynomials are represented as ZZX, whic are reduced 32 | * modulo Phi_m(X). The "frequency domain" are jusr vectors of integers 33 | * (vec_long or vec_ZZ), that store only the evaluation in primitive m-th 34 | * roots of unity. 35 | */ 36 | #include "NumbTh.h" 37 | #include "CModulus.h" 38 | 39 | // Some simple functions that should have been provided by NTL but are not 40 | inline bool IsZero(long i) { return (i==0); } 41 | inline void conv(NTL::vec_zz_p& to, NTL::vec_long& from) 42 | { 43 | to.SetLength(from.length()); 44 | for (long i=0; i 60 | void Cmod::privateInit(const PAlgebra& zms, 61 | const zz& rt) 62 | { 63 | context.restore(); // set NTL's current modulus 64 | zmStar = &zms; 65 | q = zp::modulus(); 66 | root = rt; 67 | 68 | // First find a 2m-th root of unity modulo q, if not given 69 | if (IsZero(root)) { 70 | context.restore(); // Set the current modulus to q 71 | zp rtp; 72 | unsigned e = 2*getM(); 73 | FindPrimitiveRoot(rtp,e); 74 | if (IsZero(rtp)) // sanity check 75 | Error("Cmod::compRoots(): no 2m'th roots of unity mod q"); 76 | root = rep(rtp); 77 | } 78 | rInv = InvMod(root,q); // set rInv = root^{-1} mod q 79 | 80 | // allocate memory (current modulus was defined above) 81 | freeSpace(); // just in case 82 | powers = new zpx(); 83 | Rb = new fftrep(); 84 | ipowers = new zpx(); 85 | iRb = new fftrep(); 86 | } 87 | 88 | 89 | template 90 | void Cmod::FFT(zzv &y, const ZZX& x) const 91 | { 92 | context.restore(); 93 | zp rt; 94 | zpx in, out; 95 | 96 | conv(in,x); // convert input to zpx format 97 | conv(rt, root); // convert root to zp format 98 | 99 | BluesteinFFT(out, in, getM(), rt, *powers, *Rb); // call the FFT routine 100 | 101 | // copy the result to the output vector y, keeping only the 102 | // entries corresponding to primitive roots of unity 103 | y.SetLength(zmStar->phiM()); 104 | unsigned i,j; 105 | for (i=j=0; iinZmStar(i)) y[j++] = rep(coeff(out,i)); 107 | } 108 | 109 | template 110 | void Cmod::iFFT(ZZX &x, const zzv& y) const 111 | { 112 | context.restore(); 113 | zp rt; 114 | zpx in, out, pwrs; 115 | 116 | // convert input to zpx format, initializing only the coeffs i s.t. (i,m)=1 117 | in.SetMaxLength(getM()); 118 | unsigned i,j; 119 | for (i=j=0; iinZmStar(i)) SetCoeff(in, i, y[j++]); 121 | in.normalize(); 122 | conv(rt, rInv); // convert rInv to zp format 123 | 124 | BluesteinFFT(out, in, getM(), rt, *ipowers, *iRb); // call the FFT routine 125 | out /= getM(); // normalization 126 | 127 | // reduce the result mod (Phi_m(X),q) and copy to the output polynomial x 128 | conv(in, zmStar->PhimX()); // convert Phi_m(X) to zpx format 129 | rem(out, out, in); // out %= (Phi_m(X),q) 130 | 131 | conv(x,out); // convert output to ZZX format 132 | } 133 | 134 | 135 | // instantiating the template classes 136 | template class Cmod; // small q 137 | template class Cmod; // large q 138 | 139 | 140 | -------------------------------------------------------------------------------- /CModulus.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012,2013 IBM Corp. 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | * See the GNU General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License along 13 | * with this program; if not, write to the Free Software Foundation, Inc., 14 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 15 | */ 16 | 17 | #ifndef _CModulus_H_ 18 | #define _CModulus_H_ 19 | /* CModulus.h - supports forward and backward length-m FFT transformations 20 | * 21 | * This is a wrapper around the bluesteinFFT routines, for one modulus q. 22 | * Two classes are defined here, Cmodulus for a small moduli (long) and 23 | * CModulus for a large ones (ZZ). These classes are otherwise identical 24 | * hence they are implemented using a class template. 25 | * 26 | * On initialization, it initizlies NTL's zz_pContext/ZZ_pContext for this q 27 | * and computes a 2m-th root of unity r mod q and also r^{-1} mod q. 28 | * Thereafter this class provides FFT and iFFT routines that converts between 29 | * time & frequency domains. Some tables are computed the first time that 30 | * each dierctions is called, which are then used in subsequent computations. 31 | * 32 | * The "time domain" polynomials are represented as ZZX, whic are reduced 33 | * modulo Phi_m(X). The "frequency domain" are jusr vectors of integers 34 | * (vec_long or vec_ZZ), that store only the evaluation in primitive m-th 35 | * roots of unity. 36 | */ 37 | #include "PAlgebra.h" 38 | #include "bluestein.h" 39 | 40 | //NTL_CLIENT 41 | 42 | template 43 | class Cmod { 44 | zz q; // the modulus 45 | zpContext context; // NTL's tables for this modulus 46 | 47 | // points to the Zm* structure, m is FFT size 48 | const PAlgebra* zmStar; 49 | // That should have been a reference-counted pointer, 50 | // using regular pointer is just asking for trouble 51 | 52 | zz root; // 2m-th root of unity modulo q 53 | zz rInv; // root^{-1} mod q 54 | 55 | zpx* powers; // tables for forward FFT 56 | fftrep* Rb; 57 | 58 | zpx* ipowers; // tables for backward FFT 59 | fftrep* iRb; 60 | 61 | void privateInit(const PAlgebra&, const zz& rt);// Allocate memory and compute roots 62 | void freeSpace() { 63 | if (powers) { delete powers; powers=NULL; } 64 | if (Rb) { delete Rb; Rb=NULL; } 65 | if (ipowers) { delete ipowers; ipowers=NULL; } 66 | if (iRb) { delete iRb; iRb = NULL; } 67 | } 68 | 69 | public: 70 | 71 | // Destructor and constructors 72 | 73 | ~Cmod() { freeSpace(); } // destructor 74 | 75 | // Default constructor 76 | Cmod():zmStar(NULL),powers(NULL),Rb(NULL),ipowers(NULL),iRb(NULL){} 77 | 78 | Cmod(const Cmod &other): 79 | zmStar(NULL),powers(NULL),Rb(NULL),ipowers(NULL),iRb(NULL) 80 | { *this = other; } 81 | 82 | // specify m, q, and optional root 83 | Cmod(const PAlgebra &zms, const zz &qq, const zz &rt): 84 | zmStar(NULL),powers(NULL),Rb(NULL),ipowers(NULL),iRb(NULL) 85 | { init(zms, qq, rt); } 86 | 87 | // specify only m and optional root, q is the NTL current modulus 88 | Cmod(const PAlgebra &zms, const zz &rt): 89 | zmStar(NULL),powers(NULL),Rb(NULL),ipowers(NULL),iRb(NULL) 90 | { init(zms, rt); } 91 | 92 | // a "twisted" copy constructor with a different value of m 93 | Cmod(const PAlgebra &zms, const Cmod &other): 94 | zmStar(NULL),powers(NULL),Rb(NULL),ipowers(NULL),iRb(NULL) 95 | { init(zms,other); } 96 | 97 | Cmod& operator=(const Cmod &other) { 98 | q = other.q; 99 | context = other.context; 100 | zmStar = other.zmStar; // Yes, really copy this point 101 | 102 | root = other.root; 103 | rInv = other.rInv; 104 | 105 | // copy data, not pointers in these fields 106 | freeSpace(); // just in case 107 | if (other.powers) powers = new zpx(*(other.powers)); 108 | if (other.Rb) Rb = new fftrep(*(other.Rb)); 109 | if (other.ipowers) ipowers = new zpx(*(other.ipowers)); 110 | if (other.iRb) iRb = new fftrep(*(other.iRb)); 111 | 112 | return *this; 113 | } 114 | 115 | // Initializing an empty Cmod object 116 | 117 | // specify m, q, and optional root 118 | void init(const PAlgebra &zms, zz qq, const zz &rt) { 119 | if (zmStar) return; // do not overwrite an initialized object 120 | context = zpContext(qq); 121 | privateInit(zms,rt); 122 | } 123 | 124 | // specify only m and optional root, q is the NTL current modulus 125 | void init(const PAlgebra &zms, const zz &rt) { 126 | if (zmStar) return; // do not overwrite an initialized object 127 | context.save(); 128 | privateInit(zms,rt); 129 | } 130 | 131 | // "twisted" copy with a different value of m 132 | void init(const PAlgebra &zms, const Cmod &other) { 133 | if (zmStar) return; // do not overwrite an initialized object 134 | 135 | if (other.getM() == zms.M()) { 136 | *this = other; return; // just copy the whole thing 137 | } 138 | 139 | zz rt; 140 | context = other.context; 141 | if (other.getM()%zms.M()==0) { // use the info in other 142 | context.restore(); // setup zp::modulus() 143 | zp rtp; 144 | conv(rtp,other.root); 145 | power(rtp, rtp, other.getM()/zms.M()); 146 | rt = rep(rtp); 147 | } 148 | else conv(rt,0); 149 | 150 | privateInit(zms, rt); 151 | } 152 | 153 | // utility methods 154 | 155 | const PAlgebra &ZmStar() const { return *zmStar; } 156 | unsigned getM() const { return zmStar->M(); } 157 | unsigned getPhiM() const { return zmStar->phiM(); } 158 | const zz& getQ() const { return q; } 159 | const zz& getRoot() const { return root; } 160 | 161 | void restoreModulus() {context.restore();} // restore NTL's current modulus 162 | 163 | // FFT routines 164 | 165 | void FFT(zzv &y, const ZZX& x) const; // y = FFT(x) 166 | void iFFT(ZZX &x, const zzv& y) const; // x = FFT^{-1}(y) 167 | }; 168 | 169 | typedef Cmod Cmodulus; 170 | typedef Cmod CModulus; 171 | 172 | #endif // ifdef _CModulus_H_ 173 | -------------------------------------------------------------------------------- /Ciphertext.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "FHEContext.h" 4 | #include "Ciphertext.h" 5 | #include "Util.h" 6 | 7 | bool CiphertextPart::operator==(const CiphertextPart &other) const { 8 | return poly == other.poly; 9 | } 10 | 11 | CiphertextPart &CiphertextPart::operator+=(const ZZX &other) { 12 | poly += other; 13 | return *this; 14 | } 15 | 16 | CiphertextPart &CiphertextPart::operator+=(const CiphertextPart &other) { 17 | poly += other.poly; 18 | return *this; 19 | } 20 | 21 | CiphertextPart &CiphertextPart::operator*=(long l) { 22 | for (long i = 0; i <= deg(poly); i++) { 23 | poly.rep[i] *= l; 24 | Reduce(poly.rep[i], context.logQ); 25 | } 26 | return *this; 27 | } 28 | 29 | CiphertextPart &CiphertextPart::operator*=(const ZZX &other) { 30 | poly *= other; 31 | rem(poly, poly, context.zMstar.PhimX()); 32 | for (long i = 0; i <= deg(poly); i++) { 33 | Reduce(poly.rep[i], context.logQ); 34 | } 35 | return *this; 36 | } 37 | 38 | CiphertextPart &CiphertextPart::operator*=(const ZZ_pX &other) { 39 | return operator*=(to_ZZX(other)); 40 | } 41 | 42 | CiphertextPart &CiphertextPart::operator*=(const CiphertextPart &other) { 43 | poly *= other.poly; 44 | return *this; 45 | } 46 | 47 | CiphertextPart &CiphertextPart::operator%=(const ZZ &mod) { 48 | for (long i = 0; i <= deg(poly); i++) { 49 | poly.rep[i] %= mod; 50 | } 51 | return *this; 52 | } 53 | 54 | CiphertextPart &CiphertextPart::operator>>=(long k) { 55 | DoubleCRT tmp(poly); 56 | tmp >>= k; 57 | tmp.toPoly(poly); 58 | return *this; 59 | } 60 | 61 | CiphertextPart &CiphertextPart::operator=(const CiphertextPart &other) { 62 | if (&context != &other.context) { 63 | Error("Incompatible contexts."); 64 | } 65 | this->poly = other.poly; 66 | return *this; 67 | } 68 | 69 | ostream &operator<<(ostream &os, const CiphertextPart &ctxt) { 70 | return (os << ctxt.poly); 71 | } 72 | 73 | void Ciphertext::Initialize(unsigned n, const FHEcontext &context) { 74 | this->context = &context; 75 | parts.assign(n, CiphertextPart(context)); 76 | } 77 | 78 | unsigned Ciphertext::size() const { 79 | return scaledUp ? tProd.size() : parts.size(); 80 | } 81 | 82 | void Ciphertext::ByteDecompPart(vector::iterator &decompPosition, 83 | const CiphertextPart &part) { 84 | long nbytes = context->ndigits * context->decompSize; 85 | unsigned char bytes[nbytes]; 86 | 87 | unsigned char zeroes[context->decompSize]; 88 | memset(zeroes, 0, sizeof(zeroes)); 89 | 90 | long degree = deg(part.poly); 91 | 92 | for (long i = 0; i <= degree; i++) { 93 | ZZ coef = coeff(part.poly, i); 94 | Reduce(coef, context->logQ, true); 95 | BytesFromZZ(bytes, coef, nbytes); 96 | 97 | for(unsigned digit = 0; digit < context->ndigits; digit++) { 98 | unsigned char *num = &bytes[digit*context->decompSize]; 99 | 100 | if(memcmp(num, zeroes, context->decompSize)) { 101 | SetCoeff(decompPosition[digit].poly, i, ZZFromBytes(num, context->decompSize)); 102 | } 103 | } 104 | } 105 | } 106 | 107 | Ciphertext &Ciphertext::ByteDecomp() { 108 | vector origParts = parts; 109 | 110 | parts.clear(); 111 | parts.resize(origParts.size()*context->ndigits); 112 | 113 | vector::iterator decompPosition = parts.end(); 114 | 115 | for(vector::reverse_iterator rit = origParts.rbegin(); 116 | rit != origParts.rend(); ++rit) { 117 | decompPosition -= context->ndigits; 118 | ByteDecompPart(decompPosition, *rit); 119 | } 120 | return *this; 121 | } 122 | 123 | Ciphertext &Ciphertext::operator+=(const Ciphertext &other) { 124 | assert(scaledUp == other.scaledUp); 125 | 126 | if (!scaledUp) { 127 | unsigned i = 0; 128 | for (; i < parts.size() && i < other.parts.size(); i++) { 129 | parts[i] += other.parts[i]; 130 | ReduceCoefficients(parts[i].poly, context->logQ); 131 | } 132 | for (; i < other.parts.size(); i++) { 133 | parts.push_back(other.parts[i]); 134 | } 135 | } else{ 136 | unsigned i = 0; 137 | for (; i < tProd.size() && i < other.tProd.size(); i++) { 138 | tProd[i] += other.tProd[i]; 139 | } 140 | for (; i < other.tProd.size(); i++) { 141 | tProd.push_back(other.tProd[i]); 142 | } 143 | } 144 | return *this; 145 | } 146 | 147 | Ciphertext &Ciphertext::operator+=(const ZZX &other) { 148 | ZZX scaledConstant(other); 149 | for (int i = 0; i <= deg(scaledConstant); i++) { 150 | scaledConstant.rep[i] <<= context->logQ; 151 | scaledConstant.rep[i] /= to_ZZ(context->ModulusP()); 152 | } 153 | 154 | if (!scaledUp) { 155 | parts[0] += scaledConstant; 156 | ReduceCoefficients(parts[0].poly, context->logQ); 157 | } else { 158 | tProd[0] += scaledConstant; 159 | } 160 | return *this; 161 | } 162 | 163 | Ciphertext &Ciphertext::operator+=(const ZZ_pX &other) { 164 | return operator+=(to_ZZX(other)); 165 | } 166 | 167 | Ciphertext &Ciphertext::operator*=(const Ciphertext &other) { 168 | // Convert from coefficient to DoubleCRT 169 | vector c1(parts.size()), c2(other.parts.size()); 170 | for (unsigned i = 0; i < parts.size(); i++) { 171 | c1[i] = DoubleCRT(parts[i].poly * context->ModulusP()); 172 | } 173 | 174 | for (unsigned i = 0; i < other.parts.size(); i++) { 175 | c2[i] = DoubleCRT(other.parts[i].poly); 176 | } 177 | 178 | // Evaluate tensor product using DoubleCRT representation 179 | tProd.assign(c1.size()+c2.size()-1, DoubleCRT(*context)); 180 | for (unsigned i = 0; i < c1.size(); i++) { 181 | for (unsigned j = 0; j < c2.size(); j++) { 182 | DoubleCRT tmp = c1[i]; 183 | tmp *= c2[j]; 184 | tProd[i+j] += tmp; 185 | } 186 | } 187 | 188 | parts.clear(); 189 | scaledUp = true; 190 | 191 | return *this; 192 | } 193 | 194 | void Ciphertext::ScaleDown() { 195 | if (!scaledUp) return; 196 | 197 | ZZ q = activeContext->modulusQ; 198 | ZZ q2 = 2*q; 199 | 200 | // Convert back to coefficient to round 201 | parts.clear(); 202 | for (unsigned i = 0; i < tProd.size(); i++) { 203 | ZZX part; 204 | tProd[i].toPoly(part); 205 | 206 | for (long j = 0; j <= deg(part); j++) { 207 | part.rep[j] *= 2; 208 | part.rep[j] += q; 209 | part.rep[j] /= q2; 210 | } 211 | 212 | part.normalize(); 213 | ReduceCoefficients(part, context->logQ); 214 | parts.push_back(CiphertextPart(part)); 215 | } 216 | scaledUp = false; 217 | tProd.clear(); 218 | } 219 | 220 | void Ciphertext::SetTensorRepresentation(vector &repr) { 221 | parts.clear(); 222 | swap(tProd, repr); 223 | scaledUp = true; 224 | } 225 | 226 | void Ciphertext::Clear() { 227 | tProd.clear(); 228 | scaledUp = false; 229 | 230 | parts.clear(); 231 | } 232 | 233 | Ciphertext &Ciphertext::operator*=(long l) { 234 | if (!scaledUp) { 235 | for (unsigned i = 0; i < parts.size(); i++) { 236 | parts[i] *= l; 237 | } 238 | } else { 239 | for (unsigned i = 0; i < tProd.size(); i++) { 240 | tProd[i] *= l; 241 | } 242 | } 243 | return *this; 244 | } 245 | 246 | Ciphertext &Ciphertext::operator*=(const ZZX &other) { 247 | if (!scaledUp) { // May want to consider doing this in dCRT 248 | for (unsigned i = 0; i < parts.size(); i++) { 249 | parts[i] *= other; 250 | } 251 | } else { 252 | DoubleCRT otherCRT(other, *context); 253 | for (unsigned i = 0; i < tProd.size(); i++) { 254 | tProd[i] *= otherCRT; 255 | } 256 | } 257 | return *this; 258 | } 259 | 260 | Ciphertext &Ciphertext::operator*=(const ZZ_pX &other) { 261 | return operator*=(to_ZZX(other)); 262 | } 263 | 264 | Ciphertext &Ciphertext::operator>>=(long k) { 265 | if (!scaledUp) { 266 | for (unsigned i = 0; i < parts.size(); i++) { 267 | parts[i] >>= k; 268 | } 269 | } else { 270 | for (unsigned i = 0; i < tProd.size(); i++) { 271 | tProd[i] >>= k; 272 | } 273 | } 274 | return *this; 275 | } 276 | 277 | Ciphertext &Ciphertext::operator=(const Ciphertext &other) { 278 | this->context = other.context; 279 | this->parts = other.parts; 280 | this->tProd = other.tProd; 281 | this->scaledUp = other.scaledUp; 282 | return *this; 283 | } 284 | 285 | CiphertextPart Ciphertext::GetPart(unsigned ind) const { 286 | return parts[ind]; 287 | } 288 | 289 | CiphertextPart &Ciphertext::operator[](unsigned ind) { 290 | return parts[ind]; 291 | } 292 | 293 | ostream &operator<<(ostream &os, const Ciphertext &ctxt) { 294 | if (!ctxt.scaledUp) { 295 | for (unsigned i = 0; i < ctxt.size(); i++) { 296 | os << ctxt.GetPart(i) << ", "; 297 | } 298 | } else { 299 | PrintVector(ctxt.tProd); 300 | } 301 | return os; 302 | } 303 | -------------------------------------------------------------------------------- /Ciphertext.h: -------------------------------------------------------------------------------- 1 | #ifndef _CIPHERTEXT_H_ 2 | #define _CIPHERTEXT_H_ 3 | 4 | #include 5 | #include 6 | #include "DoubleCRT.h" 7 | #include "Util.h" 8 | #include "FHE-SI.h" 9 | 10 | class FHESIPubKey; 11 | 12 | class CiphertextPart { 13 | const FHEcontext &context; 14 | 15 | public: 16 | ZZX poly; 17 | 18 | CiphertextPart() : context(*activeContext) {} 19 | CiphertextPart(const FHEcontext &context) : context(context) {} 20 | CiphertextPart(const long val) : context(*activeContext) { 21 | poly = to_ZZX(val); 22 | } 23 | explicit CiphertextPart(const ZZX &poly) : context(*activeContext) { 24 | this->poly = poly; 25 | } 26 | 27 | bool operator==(const CiphertextPart &other) const; 28 | 29 | CiphertextPart &operator+=(const ZZX &other); 30 | CiphertextPart &operator+=(const CiphertextPart &other); 31 | 32 | CiphertextPart &operator*=(const ZZX &other); 33 | CiphertextPart &operator*=(const ZZ_pX &other); 34 | CiphertextPart &operator*=(const CiphertextPart &other); 35 | CiphertextPart &operator*=(long l); 36 | 37 | CiphertextPart &operator%=(const ZZ &mod); 38 | 39 | CiphertextPart &operator>>=(long k); 40 | 41 | CiphertextPart &operator=(const CiphertextPart &other); 42 | 43 | friend ostream &operator<<(ostream &os, const CiphertextPart &ctxt); 44 | }; 45 | 46 | class Ciphertext { 47 | const FHEcontext *context; 48 | void ByteDecompPart(vector::iterator &smallPolys, 49 | const CiphertextPart &part); 50 | 51 | vector tProd; 52 | bool scaledUp; 53 | public: 54 | // WARNING: You should pass in a public key to construct ciphertexts! These constructors 55 | // are provided for serialization purposes! 56 | Ciphertext() : context(activeContext) { 57 | scaledUp = false; 58 | } 59 | 60 | Ciphertext(const FHEcontext &context) : context(&context) { 61 | scaledUp = false; 62 | } 63 | 64 | Ciphertext(const FHESIPubKey &pk) : context(&pk.GetContext()) { 65 | scaledUp = false; 66 | } 67 | 68 | vector parts; 69 | 70 | void Initialize(unsigned n, const FHEcontext &context); 71 | unsigned size() const; 72 | 73 | Ciphertext &ByteDecomp(); 74 | 75 | Ciphertext &operator+=(const Ciphertext &other); 76 | Ciphertext &operator+=(const ZZX &other); 77 | Ciphertext &operator+=(const ZZ_pX &other); 78 | 79 | Ciphertext &operator*=(const Ciphertext &other); 80 | Ciphertext &operator*=(long l); 81 | 82 | Ciphertext &operator*=(const ZZX &other); 83 | Ciphertext &operator*=(const ZZ_pX &other); 84 | 85 | Ciphertext &operator>>=(long k); 86 | 87 | void Clear(); 88 | void ScaleDown(); 89 | void SetTensorRepresentation(vector &repr); 90 | 91 | CiphertextPart GetPart(unsigned ind) const; 92 | CiphertextPart &operator[](unsigned ind); 93 | 94 | Ciphertext &operator=(const Ciphertext &other); 95 | 96 | friend ostream &operator<<(ostream &os, const Ciphertext &ctxt); 97 | }; 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /DoubleCRT.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012,2013 IBM Corp. 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | * See the GNU General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License along 13 | * with this program; if not, write to the Free Software Foundation, Inc., 14 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 15 | */ 16 | 17 | #ifndef _DoubleCRT_H_ 18 | #define _DoubleCRT_H_ 19 | /* DoubleCRT.h - This class holds an integer polynomial in double-CRT form 20 | * 21 | * Double-CRT form is a matrix of L rows and phi(m) columns. The i'th row 22 | * contains the FFT of the element wrt the ith prime, i.e. the evaluations 23 | * of the polynomial at the primitive mth roots of unity mod the ith prime. 24 | * The polynomial thus represented is defined modulo the product of all the 25 | * primes in use. 26 | * 27 | * The list of primes is defined by the data member indexMap. 28 | * indexMap.getIndexSet() defines the set of indices of primes 29 | * associated with this DoubleCRT object: they index the 30 | * primes stored in the associated FHEContext. 31 | * 32 | * Arithmetic operations are computed modulo the product of the primes in use 33 | * and also modulo Phi_m(X). Arithmetic operations can only be applied to 34 | * DoubleCRT objects relative to the same context, trying to add/multiply 35 | * objects that have different FHEContext objects will raise an error. 36 | */ 37 | 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include "NumbTh.h" 44 | #include "IndexMap.h" 45 | 46 | NTL_CLIENT 47 | 48 | // NTL decleration of a matrix of long integers 49 | 50 | //NTL_matrix_decl(long,vec_long,vec_vec_long,mat_long) 51 | //NTL_eq_matrix_decl(long,vec_long,vec_vec_long,mat_long) 52 | //NTL_io_matrix_decl(long,vec_long,vec_vec_long,mat_long) 53 | 54 | #include "FHEContext.h" 55 | 56 | class SingleCRT; 57 | 58 | 59 | class DoubleCRTHelper : public IndexMapInit { 60 | private: 61 | long val; 62 | 63 | public: 64 | 65 | DoubleCRTHelper(const FHEcontext& context) { 66 | val = context.zMstar.phiM(); 67 | } 68 | 69 | virtual void init(vec_long& v) { 70 | v.FixLength(val); 71 | } 72 | 73 | virtual IndexMapInit * clone() const { 74 | return new DoubleCRTHelper(*this); 75 | } 76 | 77 | private: 78 | DoubleCRTHelper(); // disable default constructor 79 | }; 80 | 81 | 82 | 83 | class DoubleCRT { 84 | const FHEcontext& context; // the context 85 | IndexMap map; // the data itself: if the i'th prime is in use then 86 | // map[i] is the vector of evaluation wrt this prime 87 | 88 | // a "sanity check" function, verifies consistency of the map with current 89 | // moduli chain, an error is raised if they are not consistent 90 | void verify(); 91 | 92 | 93 | // Generic operators, Fnc is either AddMod, SubMod, or MulMod. (This should 94 | // have been a template, but gcc refuses to cooperate.) 95 | // The behavior when *this and other use different primes depends on the flag 96 | // matchIndexSets. When it is set to true then the effective modulus is 97 | // determined by the union of the two index sets; otherwise, the index set 98 | // of *this. 99 | 100 | DoubleCRT& Op(const DoubleCRT &other, long (*Fnc)(long, long, long), 101 | bool matchIndexSets=true); 102 | 103 | DoubleCRT& Op(const ZZ &num, long (*Fnc)(long, long, long)); 104 | 105 | DoubleCRT& Op(const ZZX &poly, long (*Fnc)(long, long, long)); 106 | 107 | public: 108 | 109 | // Constructors and assignment operators 110 | 111 | // representing an integer polynomial as DoubleCRT. If the set of primes 112 | // to use is not specified, the resulting object uses all the primes in 113 | // the context. If the coefficients of poly are larger than the product of 114 | // the used primes, they are effectively reduced modulo that product 115 | 116 | DoubleCRT(const DoubleCRT& other); // copy constructor 117 | 118 | DoubleCRT(const ZZX&poly, const FHEcontext& _context, const IndexSet& indexSet); 119 | DoubleCRT(const ZZX&poly, const FHEcontext& _context); 120 | 121 | explicit DoubleCRT(const ZZX&poly); 122 | // uses the "active context", run-time error if it is NULL 123 | // declare "explicit" to avoid implicit type conversion 124 | 125 | // Without specifying a ZZX, we get the zero polynomial 126 | 127 | DoubleCRT(const FHEcontext &_context, const IndexSet& indexSet); 128 | 129 | explicit DoubleCRT(const FHEcontext &_context); 130 | // declare "explicit" to avoid implicit type conversion 131 | 132 | DoubleCRT(); 133 | // uses the "active context", run-time error if it is NULL 134 | 135 | 136 | // Assignment operator, the following two lines are equivalent: 137 | // DoubleCRT dCRT(poly, context, indexSet); 138 | // or 139 | // DoubleCRT dCRT(context, indexSet); dCRT = poly; 140 | 141 | DoubleCRT& operator=(const DoubleCRT& other); 142 | 143 | DoubleCRT& operator=(const SingleCRT& other); 144 | DoubleCRT& operator=(const ZZX& poly); 145 | DoubleCRT& operator=(const ZZ& num); 146 | DoubleCRT& operator=(const long num) { *this = to_ZZ(num); return *this; } 147 | 148 | // Recovering the polynomial in coefficient representation. This yields an 149 | // integer polynomial with coefficients in [-P/2,P/2], unless the positive 150 | // flag is set to true, in which case we get coefficients in [0,P-1] (P is 151 | // the product of all moduli used). Using the optional IndexSet param 152 | // we compute the polynomial reduced modulo the product of only the ptimes 153 | // in that set. 154 | 155 | void toPoly(ZZX& p, const IndexSet& s, bool positive=false) const; 156 | void toPoly(ZZX& p, bool positive=false) const; 157 | 158 | // The variant toPolyMod has another argument, which is a modulus Q, and it 159 | // computes toPoly() mod Q. This is offerred as a separate function in the 160 | // hope that one day we will figure out a more efficient method of computing 161 | // this. Right now it is not implemented 162 | // 163 | // void toPolyMod(ZZX& p, const ZZ &Q, const IndexSet& s) const; 164 | 165 | 166 | // FIXME: comparison between objects with different contexts should be an error 167 | bool operator==(const DoubleCRT& other) const { 168 | return &context == &other.context && map == other.map; 169 | } 170 | 171 | bool operator!=(const DoubleCRT& other) const { 172 | return !(*this==other); 173 | } 174 | 175 | // Set to zero, one 176 | DoubleCRT& SetZero() { 177 | *this = ZZ::zero(); 178 | return *this; 179 | } 180 | 181 | DoubleCRT& SetOne() { 182 | *this = 1; 183 | return *this; 184 | } 185 | 186 | // expand the index set by s1. 187 | // it is assumed that s1 is disjoint from the current index set. 188 | void addPrimes(const IndexSet& s1); 189 | 190 | // expand index set by s1, and scale by f*(f^{-1} mod p), 191 | // where f is the product of all the primes in s1. 192 | // it is assumed that s1 is disjoint from the current index set. 193 | // Returns the logarithm of the product of the added factors. 194 | double addPrimesAndScale(const IndexSet& s1); 195 | 196 | // remove s1 from the index set 197 | void removePrimes(const IndexSet& s1) { 198 | map.remove(s1); 199 | } 200 | 201 | 202 | // Arithmetic operations. Only the "destructive" versions are used, 203 | // i.e., a += b is implemented but not a + b. 204 | 205 | // Addition, negation, subtraction 206 | DoubleCRT& operator+=(const DoubleCRT &other) { 207 | return Op(other, NTL::AddMod); 208 | } 209 | 210 | DoubleCRT& operator+=(const ZZX &poly) { 211 | return Op(poly, NTL::AddMod); 212 | } 213 | 214 | DoubleCRT& operator+=(const ZZ &num) { 215 | return Op(num, NTL::AddMod); 216 | } 217 | 218 | DoubleCRT& operator+=(long num) { 219 | return Op(to_ZZ(num), NTL::AddMod); 220 | } 221 | 222 | DoubleCRT& operator-=(const DoubleCRT &other) { 223 | return Op(other,NTL::SubMod); 224 | } 225 | 226 | DoubleCRT& operator-=(const ZZX &poly) { 227 | return Op(poly,NTL::SubMod); 228 | } 229 | 230 | DoubleCRT& operator-=(const ZZ &num) { 231 | return Op(num, NTL::SubMod); 232 | } 233 | 234 | DoubleCRT& operator-=(long num) { 235 | return Op(to_ZZ(num), NTL::SubMod); 236 | } 237 | 238 | // These are the prefix versions, ++dcrt and --dcrt. 239 | DoubleCRT& operator++() { return (*this += 1); }; 240 | DoubleCRT& operator--() { return (*this -= 1); }; 241 | 242 | // These are the postfix versions -- return type is void, 243 | // so it is offered just for style... 244 | void operator++(int) { *this += 1; }; 245 | void operator--(int) { *this -= 1; }; 246 | 247 | 248 | // Multiplication 249 | DoubleCRT& operator*=(const DoubleCRT &other) { 250 | return Op(other,NTL::MulMod); 251 | } 252 | 253 | DoubleCRT& operator*=(const ZZX &poly) { 254 | return Op(poly,NTL::MulMod); 255 | } 256 | 257 | DoubleCRT& operator*=(const ZZ &num) { 258 | return Op(num,NTL::MulMod); 259 | } 260 | 261 | DoubleCRT& operator*=(long num) { 262 | return Op(to_ZZ(num),NTL::MulMod); 263 | } 264 | 265 | 266 | // Procedural equivalents, supporting also the matchIndexSets flag 267 | void Add(const DoubleCRT &other, bool matchIndexSets=true) { 268 | Op(other, NTL::AddMod, matchIndexSets); 269 | } 270 | 271 | void Sub(const DoubleCRT &other, bool matchIndexSets=true) { 272 | Op(other, NTL::SubMod, matchIndexSets); 273 | } 274 | 275 | void Mul(const DoubleCRT &other, bool matchIndexSets=true) { 276 | Op(other, NTL::MulMod, matchIndexSets); 277 | } 278 | 279 | // Division by constant 280 | DoubleCRT& operator/=(const ZZ &num); 281 | DoubleCRT& operator/=(long num) { return (*this /= to_ZZ(num)); } 282 | 283 | 284 | // Small-exponent polynomial exponentiation 285 | void Exp(long k); 286 | 287 | 288 | // Apply the automorphism F(X) --> F(X^k) (with gcd(k,m)=1) 289 | void automorph(long k); 290 | DoubleCRT& operator>>=(long k) { automorph(k); return *this; } 291 | 292 | 293 | // Utilities 294 | 295 | const FHEcontext& getContext() const { return context; } 296 | const IndexMap& getMap() const { return map; } 297 | const IndexSet& getIndexSet() const { return map.getIndexSet(); } 298 | 299 | void setMap(const IndexMap &newMap) { map = newMap; } 300 | 301 | // Choose random DoubleCRT's, either at random or with small/Gaussian 302 | // coefficients. 303 | 304 | // fills each row i w/ random ints mod pi, uses NTL's PRG 305 | void randomize(const ZZ* seed=NULL); 306 | 307 | // Coefficients are -1/0/1, Prob[0]=1/2 308 | void sampleSmall() { 309 | ZZX poly; 310 | ::sampleSmall(poly,context.zMstar.phiM()); // degree-(phi(m)-1) polynomial 311 | *this = poly; // convert to DoubleCRT 312 | } 313 | 314 | // Coefficients are -1/0/1 with pre-specified number of nonzeros 315 | void sampleHWt(long Hwt) { 316 | ZZX poly; 317 | ::sampleHWt(poly,Hwt,context.zMstar.phiM()); 318 | *this = poly; // convert to DoubleCRT 319 | } 320 | 321 | // Coefficients are Gaussians 322 | void sampleGaussian(double stdev=0.0) { 323 | if (stdev==0.0) stdev=to_double(context.stdev); 324 | ZZX poly; 325 | ::sampleGaussian(poly, context.zMstar.phiM(), stdev); 326 | *this = poly; // convert to DoubleCRT 327 | } 328 | 329 | 330 | // makes a corresponding SingleCRT object, restricted to 331 | // the given index set, if specified 332 | void toSingleCRT(SingleCRT& scrt, const IndexSet& s) const; 333 | void toSingleCRT(SingleCRT& scrt) const; 334 | 335 | // used to implement modulus switching 336 | void scaleDownToSet(const IndexSet& s); 337 | 338 | ZZ getCoefficientModulus() const { 339 | return context.productOfPrimes(); 340 | } 341 | 342 | 343 | friend ostream &operator<<(ostream &os, const DoubleCRT &p) { 344 | ZZX poly; 345 | p.toPoly(poly); 346 | 347 | return (os << poly); 348 | } 349 | 350 | #if 0 351 | // This makes a SingleCRT objects with the same levels as *this, converts 352 | // the numLvls starting levels fromLvl to ZZX format, and SETS TO ZERO all 353 | // the other levels in the SingleCRT object. 354 | 355 | // FIXME: not sure about the semantics here... VJS... 356 | 357 | 358 | // I/O: ONLY the matrix is outputted/recovered, not the moduli chain!! 359 | // An error is raised on input if this is not consistent with the current chain 360 | 361 | friend istream& operator>> (istream &s, DoubleCRT &d) 362 | { s >> d.data; d.fixIndexSet(); d.verify(); return s; } 363 | #endif 364 | 365 | }; 366 | 367 | 368 | 369 | 370 | inline void conv(DoubleCRT &d, const ZZX &p) { d=p; } 371 | 372 | inline DoubleCRT to_DoubleCRT(const ZZX& p) { 373 | return DoubleCRT(p); 374 | } 375 | 376 | inline void conv(ZZX &p, const DoubleCRT &d) { d.toPoly(p); } 377 | 378 | inline ZZX to_ZZX(const DoubleCRT &d) { ZZX p; d.toPoly(p); return p; } 379 | 380 | inline void conv(DoubleCRT& d, const SingleCRT& s) { d=s; } 381 | 382 | #endif // #ifndef _DoubleCRT_H_ 383 | -------------------------------------------------------------------------------- /FHE-SI.cpp: -------------------------------------------------------------------------------- 1 | #include "DoubleCRT.h" 2 | #include "FHE-SI.h" 3 | #include 4 | #include 5 | #include "Plaintext.h" 6 | #include "Util.h" 7 | #include "Ciphertext.h" 8 | #include "Serialization.h" 9 | 10 | void FHESIPubKey::Encrypt(Ciphertext &ctxt_, const Plaintext &ptxt) const { 11 | Ciphertext &ctxt = (Ciphertext &)ctxt_; 12 | ctxt.Initialize(2, context); 13 | 14 | ZZX small; 15 | for (unsigned i = 0; i < context.zMstar.phiM(); i++) { 16 | SetCoeff(small, i, RandomBnd(2)); 17 | } 18 | 19 | DoubleCRT r(small, context); 20 | DoubleCRT e(context); 21 | 22 | vector ciphertext = publicKey; 23 | for (size_t i = 0; i < ciphertext.size(); i++) { 24 | e.sampleGaussian(); 25 | e *= context.ModulusP(); 26 | 27 | ciphertext[i] *= r; 28 | ciphertext[i] += e; 29 | ciphertext[i].toPoly(ctxt[i].poly); 30 | } 31 | ctxt[0] += (context.modulusQ / context.ModulusP()) * to_ZZX(ptxt.message); 32 | 33 | for (size_t i = 0; i < ciphertext.size(); i++) { 34 | ReduceCoefficients(ctxt[i].poly, context.logQ); 35 | } 36 | } 37 | 38 | const FHEcontext &FHESIPubKey::GetContext() const { 39 | return context; 40 | } 41 | 42 | void FHESIPubKey::Init(const FHESISecKey &secKey_) { 43 | const FHESISecKey &secKey = (const FHESISecKey &)secKey_; 44 | ZZX c0, c1; 45 | sampleGaussian(c0, context.zMstar.phiM(), context.stdev); 46 | SampleRandom(c1, context.modulusQ, context.zMstar.phiM()); 47 | 48 | ZZX tmp; 49 | secKey.GetRepresentation()[1].toPoly(tmp); 50 | tmp *= c1; 51 | 52 | c0 += tmp; 53 | rem(c0, c0, context.zMstar.PhimX()); 54 | c1 *= -1; 55 | 56 | ReduceCoefficients(c0, context.logQ); 57 | ReduceCoefficients(c1, context.logQ); 58 | 59 | publicKey.clear(); 60 | publicKey.push_back(DoubleCRT(c0)); 61 | publicKey.push_back(DoubleCRT(c1)); 62 | } 63 | 64 | const vector &FHESIPubKey::GetRepresentation() const { 65 | return publicKey; 66 | } 67 | 68 | void FHESIPubKey::UpdateRepresentation(const vector &rep) { 69 | publicKey = rep; 70 | } 71 | 72 | void FHESIPubKey::Export(ofstream &out) const { 73 | ::Export(out, publicKey); 74 | } 75 | 76 | void FHESIPubKey::Import(ifstream &in) { 77 | ::Import(in, publicKey); 78 | } 79 | 80 | ostream &operator<<(ostream &os, const FHESIPubKey &pubKey_) { 81 | const FHESIPubKey &pubKey = (const FHESIPubKey &)pubKey_; 82 | return (os << pubKey.publicKey[0] << ", " << 83 | pubKey.publicKey[1]); 84 | } 85 | 86 | void FHESISecKey::Init(const FHEcontext &context) { 87 | sKeys.assign(2, DoubleCRT(context)); 88 | sKeys[0] = 1; 89 | 90 | sKeys[1].sampleHWt(64); 91 | } 92 | 93 | void FHESISecKey::Decrypt(Plaintext &ptxt, const Ciphertext &ctxt_) const { 94 | const Ciphertext &ctxt = (const Ciphertext &)ctxt_; 95 | 96 | vector ctxtPolys, sKeyPolys; 97 | for (size_t i = 0; i < sKeys.size(); i++) { 98 | ctxtPolys.push_back(DoubleCRT(ctxt.GetPart(i).poly)); 99 | sKeyPolys.push_back(DoubleCRT(sKeys[i])); 100 | } 101 | 102 | DoubleCRT tmp; 103 | DotProduct(tmp, ctxtPolys, sKeyPolys); 104 | 105 | ZZX zPtxt; 106 | tmp.toPoly(zPtxt); 107 | 108 | ZZ modulusP; 109 | modulusP = context.ModulusP(); 110 | 111 | ZZ q = context.modulusQ; 112 | ZZ q2 = 2*q; 113 | for(long i = 0; i <= deg(zPtxt); i++) { 114 | zPtxt.rep[i] *= 2*modulusP; 115 | zPtxt.rep[i] += q; 116 | zPtxt.rep[i] /= q2; 117 | SetCoeff(ptxt.message, i, to_ZZ_p(zPtxt.rep[i])); 118 | } 119 | } 120 | 121 | const vector &FHESISecKey::GetRepresentation() const { 122 | return sKeys; 123 | } 124 | 125 | const FHEcontext &FHESISecKey::GetContext() const { 126 | return context; 127 | } 128 | 129 | size_t FHESISecKey::GetSize() const { 130 | return sKeys.size(); 131 | } 132 | 133 | void FHESISecKey::UpdateRepresentation(vector &rep) { 134 | sKeys = rep; 135 | } 136 | 137 | void FHESISecKey::Export(ofstream &out) const { 138 | ::Export(out, sKeys); 139 | } 140 | 141 | void FHESISecKey::Import(ifstream &in) { 142 | ::Import(in, sKeys); 143 | } 144 | 145 | ostream &operator<<(ostream &os, const FHESISecKey &secKey_) { 146 | const FHESISecKey &secKey = (const FHESISecKey &)secKey_; 147 | for (auto it = secKey.sKeys.begin(); it != secKey.sKeys.end(); it++) { 148 | os << *it; 149 | } 150 | return os; 151 | } 152 | 153 | void KeySwitchSI::Init(const FHESISecKey &src_, const FHESISecKey &dst_) { 154 | const FHESISecKey &src = (const FHESISecKey &)src_; 155 | const FHESISecKey &dst = (const FHESISecKey &)dst_; 156 | 157 | #ifdef DEBUG 158 | assert(&src.GetContext() == &dst.GetContext()); 159 | assert(dst.GetSize() == 2); // Target keys should be in canonical form: (1,t) 160 | #endif 161 | 162 | vector s = src.GetRepresentation(); 163 | vector sCoeff(s.size()); 164 | for (size_t i = 0; i < sCoeff.size(); i++) { 165 | s[i].toPoly(sCoeff[i]); 166 | } 167 | 168 | DoubleCRT t = dst.GetRepresentation()[1]; 169 | size_t n = src.GetSize(); 170 | 171 | vector A(context.ndigits*n); 172 | vector b(context.ndigits*n); 173 | vector errors(context.ndigits*n); 174 | 175 | unsigned ind = 0; 176 | for (unsigned i = 0; i < n; i++) { 177 | for (unsigned j = 0; j < context.ndigits; j++, ind++) { 178 | ZZX poly; 179 | SampleRandom(poly, context.modulusQ, context.zMstar.phiM()); 180 | 181 | A[ind] = DoubleCRT(poly, context); 182 | b[ind] = A[ind]; 183 | A[ind] *= -1; 184 | 185 | b[ind] *= t; 186 | 187 | ZZX bCoeff; 188 | b[ind].toPoly(bCoeff); 189 | 190 | ZZX err; 191 | sampleGaussian(err, context.zMstar.phiM(), context.stdev); 192 | 193 | bCoeff += err; 194 | errors[ind] = err; 195 | bCoeff += sCoeff[i]; 196 | 197 | for (long k = 0; k <= deg(sCoeff[i]); k++) { 198 | sCoeff[i].rep[k] <<= (8*context.decompSize); 199 | } 200 | 201 | ReduceCoefficients(bCoeff, context.logQ); 202 | b[ind] = DoubleCRT(bCoeff); 203 | } 204 | } 205 | 206 | keySwitchMatrix.resize(2); 207 | keySwitchMatrix[0] = b; 208 | keySwitchMatrix[1] = A; 209 | } 210 | 211 | void KeySwitchSI::InitS2(const FHESISecKey &s_) { 212 | const FHESISecKey &s = (const FHESISecKey &)s_; 213 | vector sKeys = s.GetRepresentation(); 214 | 215 | vector tKeys; 216 | tKeys.assign(sKeys.size()*2-1, sKeys[1]); 217 | tKeys[0] = sKeys[0]; 218 | 219 | for (unsigned i = 2; i < tKeys.size(); i++) { 220 | tKeys[i] *= tKeys[i-1]; 221 | } 222 | 223 | FHESISecKey tensoredKey = FHESISecKey(s.GetContext()); 224 | tensoredKey.UpdateRepresentation(tKeys); 225 | 226 | Init(tensoredKey, s); 227 | } 228 | 229 | void KeySwitchSI::InitAutomorph(const FHESISecKey &s_, unsigned k) { 230 | const FHESISecKey &s = (const FHESISecKey &)s_; 231 | vector sKeys = s.GetRepresentation(); 232 | 233 | FHESISecKey automorphedKey(s.GetContext()); 234 | for (unsigned i = 0; i < sKeys.size(); i++) { 235 | sKeys[i].automorph(k); 236 | } 237 | automorphedKey.UpdateRepresentation(sKeys); 238 | Init(automorphedKey, s); 239 | } 240 | 241 | void KeySwitchSI::ApplyKeySwitch(Ciphertext &ctxt_) const { 242 | Ciphertext &ctxt = (Ciphertext &)ctxt_; 243 | ctxt.ScaleDown(); 244 | ctxt.ByteDecomp(); 245 | 246 | vector byteDecomp(ctxt.parts.size()); 247 | for (size_t i = 0; i < byteDecomp.size(); i++) { 248 | byteDecomp[i] = DoubleCRT(ctxt.parts[i].poly); 249 | } 250 | 251 | vector newCtxt(keySwitchMatrix.size()); 252 | for (size_t i = 0; i < keySwitchMatrix.size(); i++) { 253 | DoubleCRT dotProd; 254 | DotProduct(dotProd, keySwitchMatrix[i], byteDecomp); 255 | dotProd.toPoly(newCtxt[i].poly); 256 | ReduceCoefficients(newCtxt[i].poly, context.logQ); 257 | } 258 | 259 | ctxt.parts = newCtxt; 260 | } 261 | 262 | const vector> &KeySwitchSI::GetRepresentation() const { 263 | return keySwitchMatrix; 264 | } 265 | 266 | void KeySwitchSI::UpdateRepresentation(const vector> &rep) { 267 | keySwitchMatrix = rep; 268 | } 269 | 270 | void KeySwitchSI::Export(ofstream &out) const { 271 | ::Export(out, keySwitchMatrix); 272 | } 273 | 274 | void KeySwitchSI::Import(ifstream &in) { 275 | ::Import(in, keySwitchMatrix); 276 | } 277 | 278 | KeySwitchSI &KeySwitchSI::operator=(const KeySwitchSI &other) { 279 | if (&context != &other.context) { 280 | Error("KeySwitchSI assignment: context mismatch"); 281 | } 282 | keySwitchMatrix = other.keySwitchMatrix; 283 | return *this; 284 | } 285 | 286 | ostream &operator<<(ostream &os, const KeySwitchSI &keySwitch) { 287 | PrintVector(keySwitch.keySwitchMatrix, os); 288 | return os; 289 | } 290 | -------------------------------------------------------------------------------- /FHE-SI.h: -------------------------------------------------------------------------------- 1 | #ifndef _FHE_SI_H_ 2 | #define _FHE_SI_H_ 3 | 4 | #include "DoubleCRT.h" 5 | #include "Util.h" 6 | #include 7 | #include 8 | #include 9 | #include "Plaintext.h" 10 | #include 11 | 12 | class Ciphertext; 13 | 14 | class FHESISecKey { 15 | public: 16 | FHESISecKey() : context(*activeContext) { 17 | Init(*activeContext); 18 | } 19 | 20 | FHESISecKey(const FHEcontext &context) : context(context) { 21 | Init(context); 22 | } 23 | 24 | void Init(const FHEcontext &context); 25 | 26 | void Decrypt(Plaintext &plaintext, const Ciphertext &ciphertext) const; 27 | 28 | const FHEcontext &GetContext() const; 29 | size_t GetSize() const; 30 | 31 | const vector &GetRepresentation() const; 32 | void UpdateRepresentation(vector &rep); 33 | 34 | void Export(ofstream &out) const; 35 | void Import(ifstream &in); 36 | 37 | friend ostream &operator<<(ostream &os, const FHESISecKey &secKey); 38 | 39 | private: 40 | vector sKeys; 41 | const FHEcontext &context; 42 | }; 43 | 44 | class FHESIPubKey { 45 | friend class Ciphertext; 46 | const FHEcontext &context; 47 | vector publicKey; 48 | public: 49 | FHESIPubKey(const FHEcontext &context) : context(context) {} 50 | 51 | FHESIPubKey(const FHESISecKey &secKey) : context(secKey.GetContext()) { 52 | Init(secKey); 53 | } 54 | 55 | FHESIPubKey(const FHESISecKey &secKey, const FHEcontext &context) : context(context) { 56 | Init(secKey); 57 | } 58 | 59 | void Encrypt(Ciphertext &ctxt, const Plaintext &ptxt) const; 60 | void Init(const FHESISecKey &secKey); 61 | 62 | const FHEcontext &GetContext() const; 63 | 64 | const vector &GetRepresentation() const; 65 | void UpdateRepresentation(const vector &rep); 66 | 67 | void Export(ofstream &out) const; 68 | void Import(ifstream &in); 69 | 70 | friend ostream &operator<<(ostream &os, const FHESIPubKey &secKey); 71 | }; 72 | 73 | class KeySwitchSI { 74 | public: 75 | KeySwitchSI() : context(*activeContext) {} 76 | 77 | KeySwitchSI(FHEcontext &context) : context(context) {} 78 | 79 | KeySwitchSI(const FHESISecKey &src, const FHESISecKey &dst) : context(*activeContext) { 80 | Init(src, dst); 81 | } 82 | KeySwitchSI(const FHESISecKey &src, const FHESISecKey &dst, const FHEcontext &context) : context(context) { 83 | Init(src, dst); 84 | } 85 | 86 | KeySwitchSI(const FHESISecKey &s) : context(*activeContext) { 87 | InitS2(s); 88 | } 89 | KeySwitchSI(const FHESISecKey &s, const FHEcontext &context) : context(context) { 90 | InitS2(s); 91 | } 92 | 93 | KeySwitchSI(const FHESISecKey &s, unsigned k) : context(*activeContext) { 94 | InitAutomorph(s, k); 95 | } 96 | KeySwitchSI(const FHESISecKey &s, const FHEcontext &context, unsigned k) : context(context) { 97 | InitAutomorph(s, k); 98 | } 99 | 100 | void Init(const FHESISecKey &src, const FHESISecKey &dst); 101 | void InitS2(const FHESISecKey &s); 102 | void InitAutomorph(const FHESISecKey &s, unsigned k); 103 | void ApplyKeySwitch(Ciphertext &ctxt) const; 104 | 105 | const vector> &GetRepresentation() const; 106 | void UpdateRepresentation(const vector> &rep); 107 | 108 | void Export(ofstream &out) const; 109 | void Import(ifstream &in); 110 | 111 | KeySwitchSI &operator=(const KeySwitchSI &other); 112 | 113 | friend ostream &operator<<(ostream &os, const KeySwitchSI &keySwitch); 114 | private: 115 | const FHEcontext &context; 116 | vector> keySwitchMatrix; 117 | }; 118 | 119 | #endif 120 | -------------------------------------------------------------------------------- /FHEContext.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012,2013 IBM Corp. 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | * See the GNU General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License along 13 | * with this program; if not, write to the Free Software Foundation, Inc., 14 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 15 | */ 16 | 17 | #include "FHEContext.h" 18 | #include "Serialization.h" 19 | 20 | // A global variable, pointing to the "current" context 21 | FHEcontext* activeContext = NULL; 22 | 23 | void FHEcontext::productOfPrimes(ZZ& p, const IndexSet& s) const { 24 | p = 1; 25 | for (long i = s.first(); i <= s.last(); i = s.next(i)) 26 | p *= ithPrime(i); 27 | 28 | } 29 | 30 | void FHEcontext::AddPrime(long p, bool special, long root) { 31 | long twoM = 2 * zMstar.M(); // ensure p-1 is divisible by 2m 32 | 33 | //if (special) twoM *= context.ModulusP(); 34 | assert( ProbPrime(p) && p % twoM == 1 && !inChain(p) ); 35 | 36 | long i = moduli.size(); 37 | moduli.push_back( Cmodulus(zMstar, p, root) ); 38 | 39 | if (special) 40 | specialPrimes.insert(i); 41 | else 42 | ctxtPrimes.insert(i); 43 | } 44 | 45 | void FHEcontext::ExportSIContext(ofstream &out) { 46 | Export(out, zMstar.M()); 47 | Export(out, logQ); 48 | Export(out, ModulusP()); 49 | Export(out, Generator()); 50 | Export(out, decompSize); 51 | 52 | uint32_t size = moduli.size(); 53 | Export(out, size); 54 | for (unsigned i = 0; i < moduli.size(); i++) { 55 | long q = moduli[i].getQ(); 56 | long root = moduli[i].getRoot(); 57 | Export(out, q); 58 | Export(out, root); 59 | } 60 | } 61 | 62 | void FHEcontext::ImportSIContext(ifstream &in) { 63 | unsigned m, logQ, generator, decompSize; 64 | ZZ p; 65 | 66 | Import(in, m); 67 | Import(in, logQ); 68 | Import(in, p); 69 | Import(in, generator); 70 | Import(in, decompSize); 71 | 72 | Init(m, logQ, p, generator, decompSize); 73 | uint32_t size; 74 | Import(in, size); 75 | long q, root; 76 | for (unsigned i = 0; i < size; i++) { 77 | Import(in, q); 78 | Import(in, root); 79 | AddPrime(q, false, root); 80 | } 81 | } 82 | 83 | void FHEcontext::SetUpSIContext(long xi) { 84 | AddPrimesBySize(*this, log(modulusQ)*2+log(ModulusP())+log(zMstar.phiM())*2+log(2)+log(xi), false); 85 | } 86 | 87 | // Adds to the chain primes whose product is at least totalSize bits 88 | double AddPrimesBySize(FHEcontext& context, double totalSize, bool special) { 89 | if (!context.zMstar.M() || context.zMstar.M() > (1<<20)) // sanity checks 90 | Error("AddModuli1: m undefined or larger than 2^20"); 91 | 92 | long p = (1UL << NTL_SP_NBITS)-1; // Start from as large prime as possible 93 | long twoM = 2 * context.zMstar.M(); // make p-1 divisible by 2m 94 | //if(special) twoM *= context.ModulusP(); // ensure congruent to 1 mod ptxtSpace 95 | p -= (p%twoM); // 0 mod 2m 96 | p += twoM +1; // 1 mod 2m, a 2m quantity is subtracted below 97 | 98 | bool lastPrime = false; 99 | double sizeLeft = totalSize; // how much is left to do 100 | while (sizeLeft > 0.0) { 101 | // A bit of convoluted logic attempting not to overshoot totalSize by much 102 | if (sizeLeft < log((double)p) && !lastPrime) { // decrease p 103 | lastPrime = true; 104 | p = ceil(exp(sizeLeft)); 105 | p -= (p%twoM)-1; // make p-1 divisible by 2m 106 | twoM = -twoM; // increase p below, rather than decreasing it 107 | } 108 | do { p -= twoM; } while (!ProbPrime(p)); // next prime 109 | if (!context.inChain(p)) { 110 | context.AddPrime(p, special); 111 | sizeLeft -= log((double)p); 112 | } 113 | } 114 | return totalSize-sizeLeft; 115 | } 116 | 117 | 118 | // Adds nPrimes primes to the chain, returns the bitsize of the product of 119 | // all primes in the chain. 120 | double AddPrimesByNumber(FHEcontext& context, long nPrimes, 121 | long p, bool special) { 122 | if (!context.zMstar.M() || context.zMstar.M() > (1<<20)) // sanity checks 123 | Error("FHEcontext::AddModuli2: m undefined or larger than 2^20"); 124 | 125 | long twoM = 2 * context.zMstar.M(); 126 | 127 | // make sure that p>0 and that p-1 is divisible by m 128 | if (p<1) p = 1; 129 | p -= (p % twoM) -1; 130 | 131 | double sizeSoFar = 0.0; 132 | while (nPrimes>0) { 133 | do { p += twoM; } while (!ProbPrime(p)); // next prime 134 | if (!context.inChain(p)) { 135 | context.AddPrime(p, special); 136 | nPrimes -= 1; 137 | sizeSoFar += log((double)p); 138 | } 139 | } 140 | return sizeSoFar; 141 | } 142 | -------------------------------------------------------------------------------- /FHEContext.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012,2013 IBM Corp. 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | * See the GNU General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License along 13 | * with this program; if not, write to the Free Software Foundation, Inc., 14 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 15 | */ 16 | 17 | #ifndef _FHEcontext_H_ 18 | #define _FHEcontext_H_ 19 | 20 | #include 21 | #include 22 | #include "PAlgebra.h" 23 | #include "CModulus.h" 24 | #include "IndexSet.h" 25 | #include "Util.h" 26 | #include "PlaintextSpace.h" 27 | 28 | class FHEcontext; 29 | 30 | // Convenience routines 31 | 32 | // Adds to the chain primes whose product is at least e^totalSize, 33 | // returns natural log of the product of all added primes 34 | double AddPrimesBySize(FHEcontext& context, double totalSize, 35 | bool special=false); 36 | 37 | // Adds nPrimes primes to the chain 38 | // returns natural log of the product of all added primes 39 | double AddPrimesByNumber(FHEcontext& context, long nPrimes, 40 | long startAt=1, 41 | bool special=false); 42 | 43 | extern FHEcontext* activeContext; // Points to the "current" context 44 | 45 | class FHEcontext { 46 | vector moduli; // Cmodulus objects for the different primes 47 | // This is private since the implementation assumes that the list of 48 | // primes only grows and no prime is ever modified or removed. 49 | 50 | PlaintextSpace ptxtSpace; 51 | public: 52 | // FHEContext is meant for convenience, not encapsulation: Most data 53 | // members are public and can be initialized by the application program. 54 | 55 | PAlgebra zMstar; // The structure of Zm* 56 | //PAlgebraModTwo modTwo; // The structure of Z[X]/(Phi_m(X),2) 57 | //PAlgebraMod2r mod2r; // The structure of Z[X]/(Phi_m(X),2^r) 58 | 59 | // The public encryption key and "fresh" ciphertexts are encrypted relative 60 | // to only a subset of the prime, to allow for mod-UP during key-switching. 61 | // In ctxtPrimes we keep the indexes of this subset. Namely, for a ciphertext 62 | // part p in a fresh ciphertext we have p.getMap().getIndexSet()==ctxtPrimes. 63 | // For convenience, we also keep in specialPrimes the complemeting subset, 64 | // i.e., specialPrimes = [0,numPrimes()-1] \setminus ctxtPrimes 65 | IndexSet ctxtPrimes; 66 | IndexSet specialPrimes; 67 | 68 | // The different columns in any key-switching matrix contain encryptions 69 | // of multiplies of the secret key, sk, B1*sk, B2*B1*sk, B3*B2*B1*sk,... 70 | // with each Bi a product of a few "non-special" primes in the chain. The 71 | // digits data member indicate which primes correspond to each of the Bi's. 72 | // These are all IndexSet objects, whose union is the subset ctxtPrimes. 73 | // The number of Bi's is one less than the number of columns in the key 74 | // switching matrices (since the 1st column encrypts sk, without any Bi's), 75 | // but we keep in the digits vector also an entry for the primes that do 76 | // not participate in any Bi (so digits.size() is the same as the number 77 | // of columns in the key switching matrices). 78 | 79 | vector digits; // digits of ctxt/columns of key-switching matrix 80 | 81 | double stdev; // stdev is sqrt(variance) of the LWE error (default=3.2) 82 | 83 | ZZ modulusQ; 84 | unsigned logQ; 85 | 86 | unsigned decompSize; // number of bytes in each digit 87 | unsigned ndigits; 88 | 89 | unsigned primesNeeded; 90 | 91 | FHEcontext(unsigned m, unsigned logQ, unsigned p, 92 | unsigned generator, unsigned decompSize = 3) { 93 | Init(m, logQ, to_ZZ(p), generator, decompSize); 94 | } 95 | 96 | FHEcontext(unsigned m, unsigned logQ, const ZZ &p, 97 | unsigned generator, unsigned decompSize = 3) { 98 | Init(m, logQ, p, generator, decompSize); 99 | } 100 | 101 | FHEcontext(ifstream &in) { 102 | ImportSIContext(in); 103 | } 104 | 105 | void Init(unsigned m, unsigned logQ, const ZZ &p, unsigned generator, unsigned decompSize = 3) { 106 | stdev = 3.2; 107 | 108 | zMstar.init(m, generator); 109 | 110 | this->logQ = logQ; 111 | this->modulusQ = 1; 112 | this->modulusQ <<= logQ; 113 | 114 | this->decompSize = decompSize; 115 | this->ndigits = (logQ + 8*decompSize - 1) / (8*decompSize); 116 | 117 | ptxtSpace.Init(zMstar.PhimX(), p, generator); 118 | } 119 | 120 | void ExportSIContext(ofstream &out); 121 | void ImportSIContext(ifstream &in); 122 | 123 | void SetUpSIContext(long xi = 1); 124 | void SetUpBGVContext(unsigned L, unsigned c, long w, long pSize, long p0Size); 125 | 126 | unsigned Generator() const { 127 | return ptxtSpace.generator; 128 | } 129 | 130 | const ZZ &ModulusP() const { 131 | return ptxtSpace.p; 132 | } 133 | 134 | const PlaintextSpace &GetPlaintextSpace() const { 135 | return ptxtSpace; 136 | } 137 | 138 | void SetP(const ZZ &p) { 139 | ZZ_p::init(p); 140 | ptxtSpace.Init(zMstar.PhimX(), p); 141 | } 142 | 143 | long ithPrime(unsigned i) const { 144 | return (i= numPrimes()) 183 | Error("FHEContext::logOfProduct: IndexSet has too many rows"); 184 | 185 | double ans = 0.0; 186 | for (long i = s.first(); i <= s.last(); i = s.next(i)) 187 | ans += logOfPrime(i); 188 | return ans; 189 | } 190 | 191 | void AddPrime(long p, bool special, long root = 0); 192 | 193 | friend ostream &operator<<(ostream &os, const FHEcontext &context) { 194 | os << "logQ: " << context.logQ << endl 195 | << "p: " << context.ModulusP() << endl 196 | << "g: " << context.Generator() << endl 197 | << "primes: ["; 198 | for (unsigned i = 0; i < context.moduli.size(); i++) { 199 | os << context.moduli[i].getQ() << ", "; 200 | } 201 | os << "]" << endl; 202 | 203 | return os; 204 | } 205 | 206 | }; 207 | 208 | #endif 209 | -------------------------------------------------------------------------------- /IndexMap.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012,2013 IBM Corp. 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | * See the GNU General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License along 13 | * with this program; if not, write to the Free Software Foundation, Inc., 14 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 15 | */ 16 | 17 | #ifndef _IndexMap 18 | #define _IndexMap 19 | 20 | #include "IndexSet.h" 21 | #include 22 | #include 23 | #include 24 | 25 | /**************** 26 | 27 | The generic class IndexMap: implements a map 28 | indexed by a dynamic index set. Additionally, it 29 | allows new elements of the map to be initialized 30 | in a flexible manner. 31 | 32 | 33 | template class IndexMap { 34 | 35 | public: 36 | 37 | IndexMap(); // the empty map 38 | 39 | IndexMap(IndexMapInit *_init); 40 | // this associates a method for initializing new 41 | // elements in the map. When a new index j is added 42 | // to the index set, an object t of type T is created 43 | // using the default constructor for T, after which 44 | // the function _init->init(t) is called (t is passed 45 | // by reference. To use this feature, you need to 46 | // derive a subclass of IndexMapInit that defines 47 | // the init function. This "helper object" should 48 | // be created using operator new, and the pointer is 49 | // "exclusively owned" by the map object. 50 | 51 | const IndexSet& getIndexSet(); 52 | // get the underlying index set 53 | 54 | T& operator[] (long j); 55 | const T& operator[] (long j) const; 56 | // access functions: will raise an error 57 | // if j does not belong to the current index set 58 | 59 | void insert(long j); 60 | void insert(const IndexSet& s); 61 | void remove(long j); 62 | void remove(const IndexSet& s); 63 | // insert/remove indices from index set... 64 | // insertion will cause new T objects to be created, 65 | // using the default constructor, 66 | // and possibly initilized via the IndexMapInit pointer. 67 | // deletion may cause objects to be destroyed. 68 | 69 | 70 | }; 71 | 72 | ******************/ 73 | 74 | using namespace std; 75 | 76 | template < class T > class IndexMapInit { 77 | public: 78 | virtual void init(T&) = 0; // override with initialization code 79 | virtual IndexMapInit * clone() const = 0; 80 | // override with code to create a pointer fresh copy 81 | }; 82 | 83 | template < class T > class IndexMap { 84 | 85 | tr1::unordered_map map; 86 | IndexSet indexSet; 87 | IndexMapInit *init; 88 | 89 | public: 90 | 91 | IndexMap(const IndexMap& other) { 92 | map = other.map; 93 | indexSet = other.indexSet; 94 | init = other.init ? other.init->clone() : NULL; 95 | } 96 | 97 | IndexMap() { init = NULL; } 98 | explicit IndexMap(IndexMapInit *_init) { init = _init; } 99 | 100 | ~IndexMap() { if (init) delete init; } 101 | 102 | const IndexSet& getIndexSet() const { return indexSet; } 103 | 104 | T& operator[] (long j) { 105 | assert(indexSet.contains(j)); 106 | return map[j]; 107 | } 108 | 109 | IndexMap& operator=(const IndexMap& other) { 110 | if (this == &other) return *this; 111 | map = other.map; 112 | indexSet = other.indexSet; 113 | if (init) delete init; 114 | init = other.init ? other.init->clone() : NULL; 115 | return *this; 116 | } 117 | 118 | const T& operator[] (long j) const { 119 | assert(indexSet.contains(j)); 120 | // unordered_map does not support a const [] operator, 121 | // so we have to artificially strip away the const-ness here 122 | tr1::unordered_map & map1 = 123 | const_cast< tr1::unordered_map & > (map); 124 | return map1[j]; 125 | } 126 | 127 | void insert(long j) { 128 | if (!indexSet.contains(j)) { 129 | indexSet.insert(j); 130 | if (init) init->init(map[j]); 131 | } 132 | } 133 | 134 | 135 | void insert(const IndexSet& s) { 136 | for (long i = s.first(); i <= s.last(); i = s.next(i)) 137 | insert(i); 138 | } 139 | 140 | void remove(long j) { indexSet.remove(j); map.erase(j); } 141 | 142 | void remove(const IndexSet& s) { 143 | for (long i = s.first(); i <= s.last(); i = s.next(i)) 144 | map.erase(i); 145 | indexSet.remove(s); 146 | } 147 | 148 | void clear() { 149 | map.clear(); 150 | indexSet.clear(); 151 | } 152 | 153 | long first() const { 154 | return indexSet.first(); 155 | } 156 | 157 | long last() const { 158 | return indexSet.last(); 159 | } 160 | 161 | long next(long ind) const { 162 | return indexSet.next(ind); 163 | } 164 | 165 | friend ostream& operator<<(ostream &os, const IndexMap &map) { 166 | for (long i = map.first(); i != map.last(); i = map.next(i)) { 167 | os << i << ": " << map[i] << endl; 168 | } 169 | return os; 170 | } 171 | }; 172 | 173 | template 174 | bool operator==(const IndexMap& map1, const IndexMap& map2) { 175 | if (map1.getIndexSet() != map2.getIndexSet()) return false; 176 | const IndexSet& s = map1.getIndexSet(); 177 | for (long i = s.first(); i <= s.last(); i = s.next(i)) { 178 | if (map1[i] == map2[i]) continue; 179 | return false; 180 | } 181 | return true; 182 | } 183 | 184 | template 185 | bool operator!=(const IndexMap& map1, const IndexMap& map2) { 186 | return !(map1 == map2); 187 | } 188 | 189 | #endif 190 | -------------------------------------------------------------------------------- /IndexSet.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012,2013 IBM Corp. 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | * See the GNU General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License along 13 | * with this program; if not, write to the Free Software Foundation, Inc., 14 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 15 | */ 16 | 17 | #include "IndexSet.h" 18 | 19 | const IndexSet& IndexSet::emptySet() 20 | { 21 | static IndexSet empty; 22 | return empty; 23 | } 24 | 25 | // constructs an interval, low to high 26 | void IndexSet::intervalConstructor(long low, long high) { 27 | assert(low >= 0); 28 | 29 | if (high < low) { 30 | _first = 0; _last = -1; _card = 0; 31 | } 32 | else { 33 | rep.resize(high+1); 34 | for (long i = 0; i < low; i++) 35 | rep[i] = false; 36 | for (long i = low; i <= high; i++) 37 | rep[i] = true; 38 | 39 | _first = low; _last = high; _card = high-low+1; 40 | } 41 | } 42 | 43 | long IndexSet::next(long j) const { 44 | if (_card == 0) return j+1; 45 | if (j >= _last) return j + 1; 46 | if (j < _first) return _first; 47 | j++; 48 | while (rep[j] == false) j++; 49 | return j; 50 | } 51 | 52 | long IndexSet::prev(long j) const { 53 | if (_card == 0) return j-1; 54 | if (j > _last) return _last; 55 | if (j <= _first) return j-1; 56 | j--; 57 | while (rep[j] == false) j--; 58 | return j; 59 | } 60 | 61 | bool IndexSet::contains(long j) const { 62 | if (j < _first || j > _last) return false; 63 | return rep[j]; 64 | } 65 | 66 | 67 | bool IndexSet::contains(const IndexSet& s) const { 68 | for (long i = s.first(); i <= s.last(); i = s.next(i)) 69 | if (!contains(i)) return false; 70 | return true; 71 | } 72 | 73 | 74 | bool IndexSet::disjointFrom(const IndexSet& s) const 75 | { 76 | // quick tests for some common cases 77 | if (card() == 0 || s.card() == 0 78 | || last() < s.first() || s.last() < first()) return true; 79 | 80 | for (long i = s.first(); i <= s.last(); i = s.next(i)) 81 | if (contains(i)) return false; 82 | return true; 83 | } 84 | 85 | 86 | 87 | bool IndexSet::operator==(const IndexSet& s) const { 88 | if (this == &s) return true; 89 | if (_card != s._card) return false; 90 | if (_first != s._first) return false; 91 | if (_last != s._last) return false; 92 | 93 | return equal(rep.begin()+_first, rep.begin()+_last+1, 94 | s.rep.begin()+_first); 95 | // NOTE: maybe vector optimizes this??? 96 | } 97 | 98 | void IndexSet::clear() { 99 | rep.resize(0); 100 | _first = 0; _last = -1; _card = 0; 101 | } 102 | 103 | void IndexSet::insert(long j) { 104 | assert(j >= 0); 105 | 106 | long oldSize = rep.size(); 107 | if (j >= oldSize) { 108 | rep.resize(j+1); 109 | for (long i = oldSize; i <= j; i++) rep[i] = false; 110 | } 111 | 112 | if (_card == 0) { 113 | _first = _last = j; 114 | _card = 1; 115 | } 116 | else { 117 | if (j > _last) _last = j; 118 | if (j < _first) _first = j; 119 | if (rep[j] == false) _card++; 120 | } 121 | 122 | rep[j] = true; 123 | } 124 | 125 | void IndexSet::remove(long j) { 126 | assert(j >= 0); 127 | 128 | if (j >= (long) rep.size()) return; 129 | if (rep[j] == false) return; 130 | 131 | long newFirst = _first, newLast = _last; 132 | 133 | if (_card == 1) { 134 | newFirst = 0; 135 | newLast = -1; 136 | } 137 | else { 138 | if (_last == j) newLast = prev(_last); 139 | if (_first == j) newFirst = next(_first); 140 | } 141 | 142 | _first = newFirst; 143 | _last = newLast; 144 | _card--; 145 | rep[j] = false; 146 | } 147 | 148 | void IndexSet::insert(const IndexSet& s) { 149 | if (this == &s) return; 150 | if (s.card() == 0) return; 151 | if (card() == 0) { 152 | *this = s; 153 | return; 154 | } 155 | 156 | for (long i = s.last(); i >= s.first(); i = s.prev(i)) insert(i); 157 | // NOTE: traversal done from high to low so as to trigger at 158 | // at most one resize 159 | 160 | } 161 | 162 | void IndexSet::remove(const IndexSet& s) { 163 | if (this == &s) { clear(); return; } 164 | if (s.card() == 0) return; 165 | if (card() == 0) return; 166 | 167 | for (long i = s.first(); i <= s.last(); i = s.next(i)) remove(i); 168 | // NOTE: traversal order should not matter here 169 | } 170 | 171 | 172 | void IndexSet::retain(const IndexSet& s) { 173 | if (this == &s) return; 174 | if (s.card() == 0) { clear(); return; } 175 | if (card() == 0) return; 176 | 177 | for (long i = first(); i <= last(); i = next(i)) { 178 | if (!s.contains(i)) remove(i); 179 | } 180 | } 181 | 182 | // union 183 | IndexSet operator|(const IndexSet& s, const IndexSet& t) { 184 | IndexSet r = s; 185 | r.insert(t); 186 | return r; 187 | } 188 | 189 | // intersection 190 | IndexSet operator&(const IndexSet& s, const IndexSet& t) { 191 | IndexSet r = s; 192 | r.retain(t); 193 | return r; 194 | } 195 | 196 | // exclusive-or 197 | IndexSet operator^(const IndexSet& s, const IndexSet& t) { 198 | IndexSet r = s | t; 199 | r.remove(s & t); 200 | return r; 201 | } 202 | 203 | // set minus 204 | IndexSet operator/(const IndexSet& s, const IndexSet& t) { 205 | IndexSet r = s; 206 | r.remove(t); 207 | return r; 208 | } 209 | 210 | 211 | ostream& operator << (ostream& str, const IndexSet& set) 212 | { 213 | if (set.card() == 0) { 214 | str << "[]"; 215 | } 216 | else if (set.card() == 1) { 217 | str << "[" << set.first() << "]"; 218 | } 219 | else { 220 | str << "[" << set.first(); 221 | for (long i = set.next(set.first()); i <= set.last(); i = set.next(i)) 222 | str << " " << i; 223 | str << "]"; 224 | } 225 | 226 | return str; 227 | } 228 | 229 | 230 | // functional card 231 | long card(const IndexSet& s) { return s.card(); } 232 | 233 | // functional "contains" 234 | bool operator<=(const IndexSet& s1, const IndexSet& s2) { 235 | return s2.contains(s1); 236 | } 237 | 238 | bool operator<(const IndexSet& s1, const IndexSet& s2) { 239 | return card(s1) < card(s2) && s2.contains(s1); 240 | } 241 | 242 | bool operator>=(const IndexSet& s1, const IndexSet& s2) { 243 | return s1.contains(s2); 244 | } 245 | 246 | bool operator>(const IndexSet& s1, const IndexSet& s2) { 247 | return card(s2) < card(s1) && s1.contains(s2); 248 | } 249 | 250 | 251 | #if 0 252 | int main() 253 | { 254 | cout << "hello world\n"; 255 | 256 | IndexSet s1 = IndexSet(1) | IndexSet(3) | IndexSet(5); 257 | 258 | IndexSet s2 = IndexSet(2, 5); 259 | 260 | s1 = s1 / s2; 261 | 262 | cout << s1.first() << "\n"; 263 | cout << s1.last() << "\n"; 264 | cout << s1.card() << "\n"; 265 | 266 | cout << s1 << "\n"; 267 | } 268 | #endif 269 | 270 | 271 | -------------------------------------------------------------------------------- /IndexSet.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012,2013 IBM Corp. 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | * See the GNU General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License along 13 | * with this program; if not, write to the Free Software Foundation, Inc., 14 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 15 | */ 16 | 17 | #ifndef _IndexSet 18 | #define _IndexSet 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | using namespace std; 25 | 26 | class IndexSet { 27 | 28 | vector rep; 29 | // NOTE: modern versions of C++ are supposed 30 | // to implement this efficiently as a "specialized template class". 31 | // Older versions of C++ define the equivalent class bit_vector. 32 | 33 | long _first, _last, _card; 34 | 35 | // Invariant: if _card == 0, then _first = 0, _last = -1; 36 | // otherwise, _first (resp. _last) is the lowest (resp. highest) 37 | // index in the set. 38 | // In any case, the vector rep always defines the characterstic 39 | // function of the set. 40 | 41 | // private helper function 42 | void intervalConstructor(long low, long high); 43 | 44 | public: 45 | 46 | /*** constructors ***/ 47 | 48 | // no-arg constructor - creates empty set 49 | IndexSet() { 50 | _first = 0; _last = -1; _card = 0; 51 | } 52 | 53 | // constructs an interval, low to high 54 | IndexSet(long low, long high) { 55 | intervalConstructor(low, high); 56 | } 57 | 58 | // constructs a singleton set 59 | explicit 60 | IndexSet(long j) { 61 | intervalConstructor(j, j); 62 | } 63 | 64 | // copy constructor: use the built-in copy constructor 65 | 66 | /*** asignment ***/ 67 | 68 | // assignment: use the built-in assignment operator 69 | 70 | /*** access methods ***/ 71 | 72 | long first() const { return _first; } 73 | long last() const { return _last; } 74 | long card() const { return _card; } 75 | 76 | // return the next element after j, if any; 77 | // otherwise j+1 78 | 79 | long next(long j) const; 80 | 81 | // return the previous element before j, if any; 82 | // otherwise j-1 83 | long prev(long j) const; 84 | 85 | // NOTE: you can iterate through a set as follows: 86 | // for (long i = s.first(); i <= s.last(); i = s.next(i)) ... 87 | 88 | // returns true iff the set contains j 89 | bool contains(long j) const; 90 | 91 | // returns true iff the set contains s 92 | bool contains(const IndexSet& s) const; 93 | 94 | // returns true iff the set is disjoint from s 95 | bool disjointFrom(const IndexSet& s) const; 96 | 97 | /*** comparison ***/ 98 | 99 | bool operator==(const IndexSet& s) const; 100 | 101 | bool operator!=(const IndexSet& s) const { 102 | return !(*this == s); 103 | } 104 | 105 | /*** update methods ***/ 106 | 107 | // set to the empty set 108 | void clear(); 109 | 110 | // add j to the set 111 | void insert(long j); 112 | 113 | // remove j from the set 114 | void remove(long j); 115 | 116 | // add s to the set (union) 117 | void insert(const IndexSet& s); 118 | 119 | // remove s from the set (set minus) 120 | void remove(const IndexSet& s); 121 | 122 | // retain only those elements that are also in s (intersection) 123 | void retain(const IndexSet& s); 124 | 125 | // read-only access to an empty set 126 | static const IndexSet& emptySet(); 127 | }; 128 | 129 | // some high-level convenience methods...not very efficient... 130 | // not sure if we really need these 131 | 132 | // union 133 | IndexSet operator|(const IndexSet& s, const IndexSet& t); 134 | 135 | // intersection 136 | IndexSet operator&(const IndexSet& s, const IndexSet& t); 137 | 138 | // exclusive-or 139 | IndexSet operator^(const IndexSet& s, const IndexSet& t); 140 | 141 | // set minus 142 | IndexSet operator/(const IndexSet& s, const IndexSet& t); 143 | 144 | // output operator 145 | ostream& operator << (ostream& str, const IndexSet& set); 146 | 147 | // functional card 148 | long card(const IndexSet& s); 149 | 150 | inline bool empty(const IndexSet& s) { return s.card()==0; } 151 | 152 | // functional "contains" 153 | bool operator<=(const IndexSet& s1, const IndexSet& s2); 154 | 155 | bool operator<(const IndexSet& s1, const IndexSet& s2); 156 | 157 | bool operator>=(const IndexSet& s1, const IndexSet& s2); 158 | 159 | bool operator>(const IndexSet& s1, const IndexSet& s2); 160 | 161 | // functional disjoint 162 | inline bool disjoint(const IndexSet& s1, const IndexSet& s2) 163 | { return s1.disjointFrom(s2); } 164 | 165 | #endif 166 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC = g++ 2 | 3 | CFLAGS = -g -O2 -Wall -std=c++0x -I/usr/local/include -Wno-delete-non-virtual-dtor 4 | 5 | BUILD = build 6 | TESTS = tests 7 | 8 | LDPATH = -L/usr/lib/ -L$(CURDIR)/$(BUILD) 9 | LDLIBS = -lntl -lgmp -lm 10 | 11 | SRC = PlaintextSpace.cpp CModulus.cpp FHEContext.cpp PAlgebra.cpp SingleCRT.cpp \ 12 | DoubleCRT.cpp NumbTh.cpp bluestein.cpp IndexSet.cpp \ 13 | Plaintext.cpp Util.cpp \ 14 | FHE-SI.cpp Ciphertext.cpp \ 15 | Serialization.cpp Matrix.cpp 16 | 17 | TESTPROGS = Test_Regression_x Test_Statistics_x Test_General_x Test_AddMul_x 18 | 19 | OBJPATHS = $(patsubst %.cpp,$(BUILD)/%.o, $(SRC)) 20 | TESTPATHS = $(addprefix $(TESTS)/, $(TESTPROGS)) 21 | 22 | SUFFIXES += .d 23 | NODEPS = clean $(BUILD) $(TESTS) $(DISTR) obj 24 | DEPFILES = $(patsubst %_x,$(TESTS)/%.d,$(TESTPROGS)) $(patsubst %.cpp,$(BUILD)/%.d,$(SRC)) 25 | 26 | all: $(OBJPATHS) $(TESTPATHS) 27 | 28 | ifeq (0, $(words $(findstring $(MAKECMDGOALS), $(NODEPS)))) 29 | -include $(DEPFILES) 30 | endif 31 | 32 | obj: $(OBJPATHS) 33 | 34 | test: $(OBJPATHS) $(TESTPATHS) 35 | 36 | $(BUILD): 37 | mkdir -p $(BUILD) 38 | $(TESTS): 39 | mkdir -p $(TESTS) 40 | 41 | deps: $(DEPFILES) 42 | 43 | $(BUILD)/%.d: %.cpp | $(BUILD) 44 | $(CC) $(CFLAGS) -MM -MT '$(BUILD)/$*.o' $< $(LDPATH) $(LDLIBS) -MF $@ 45 | 46 | $(TESTS)/%.d: %.cpp | $(TESTS) 47 | $(CC) $(CFLAGS) -MM -MT '$(TESTS)/$*_x' $< $(LDPATH) $(LDLIBS) -MF $@ 48 | 49 | $(BUILD)/%.o: %.cpp | $(BUILD) 50 | $(CC) $(CFLAGS) -o $@ -c $< 51 | 52 | $(TESTS)/%_x: %.cpp $(OBJPATHS) $(TESTS)/%.d | $(TESTS) 53 | $(CC) $(CFLAGS) -o $@ $< $(LDPATH) $(OBJPATHS) $(LDLIBS) 54 | 55 | clean: 56 | rm -rf $(BUILD) $(TESTS)/*_x $(TESTS)/*.d *~ 57 | -------------------------------------------------------------------------------- /Matrix.cpp: -------------------------------------------------------------------------------- 1 | #ifndef _MATRIX_CPP 2 | #define _MATRIX_CPP 3 | 4 | #include "Matrix.h" 5 | 6 | template 7 | Matrix Matrix::operator+(const Matrix &other) const { 8 | Matrix newMatrix = *this; 9 | newMatrix += other; 10 | return newMatrix; 11 | } 12 | 13 | template 14 | Matrix &Matrix::operator+=(const Matrix &other) { 15 | for (unsigned i = 0; i < NumRows(); i++) { 16 | for (unsigned j = 0; j < NumCols(); j++) { 17 | ElemAt(i,j) += other(i,j); 18 | } 19 | } 20 | return *this; 21 | } 22 | 23 | template 24 | Matrix Matrix::operator-(const Matrix &other) const { 25 | Matrix newMatrix = *this; 26 | newMatrix -= other; 27 | return newMatrix; 28 | } 29 | 30 | template 31 | Matrix &Matrix::operator-=(const Matrix &other) { 32 | for (unsigned i = 0; i < NumRows(); i++) { 33 | for (unsigned j = 0; j < NumCols(); j++) { 34 | T tmp = other(i,j); 35 | tmp *= -1; 36 | ElemAt(i,j) += tmp; 37 | } 38 | } 39 | return *this; 40 | } 41 | 42 | template 43 | Matrix Matrix::operator*(Matrix &other) const { 44 | Matrix newMatrix = *this; 45 | newMatrix *= other; 46 | return newMatrix; 47 | } 48 | 49 | template 50 | Matrix Matrix::operator*(vector &other) const { 51 | Matrix newMatrix = *this; 52 | newMatrix *= other; 53 | return newMatrix; 54 | } 55 | 56 | template 57 | Matrix &Matrix::operator*=(Matrix &other) { 58 | if (mat.empty()) return *this; 59 | Matrix newMatrix(NumRows(), other.NumCols(), dummy); 60 | 61 | for (unsigned i = 0; i < NumRows(); i++) { 62 | for (unsigned j = 0; j < other.NumCols(); j++) { 63 | newMatrix(i,j) = ElemAt(i,0); 64 | newMatrix(i,j) *= other(0,j); 65 | 66 | for (unsigned k = 1; k < NumCols(); k++) { 67 | T tmp = ElemAt(i, k); 68 | tmp *= other(k, j); 69 | 70 | newMatrix(i,j) += tmp; 71 | } 72 | } 73 | } 74 | 75 | swap(newMatrix.mat, mat); 76 | transpose = false; 77 | return *this; 78 | } 79 | 80 | template 81 | Matrix &Matrix::operator*=(vector &other) { 82 | if (mat.empty()) return *this; 83 | Matrix newMatrix(NumRows(), 1, dummy); 84 | 85 | for (unsigned i = 0; i < NumRows(); i++) { 86 | ElemAt(i,0) *= other[0]; 87 | newMatrix(i,0) = ElemAt(i,0); 88 | for (unsigned j = 1; j < NumCols(); j++) { 89 | ElemAt(i,j) *= other[j]; 90 | newMatrix(i,0) += ElemAt(i,j); 91 | } 92 | } 93 | 94 | swap(mat, newMatrix.mat); 95 | transpose = false; 96 | return *this; 97 | } 98 | 99 | template 100 | Matrix &Matrix::operator*=(T &other) { 101 | if (mat.empty()) return *this; 102 | for (unsigned i = 0; i < NumRows(); i++) { 103 | for (unsigned j = 0; j < NumCols(); j++) { 104 | ElemAt(i,j) *= other; 105 | } 106 | } 107 | return *this; 108 | } 109 | 110 | template 111 | Matrix &Matrix::operator=(const Matrix &other) { 112 | mat = other.mat; 113 | transpose = other.transpose; 114 | dummy = other.dummy; 115 | 116 | return *this; 117 | } 118 | 119 | template 120 | T &Matrix::operator()(unsigned row, unsigned col) { 121 | return ElemAt(row, col); 122 | } 123 | 124 | template 125 | T const &Matrix::operator()(unsigned row, unsigned col) const { 126 | return ElemAt(row, col); 127 | } 128 | 129 | template 130 | vector &Matrix::operator[](unsigned row) { 131 | return mat[row]; 132 | } 133 | 134 | template 135 | vector const &Matrix::operator[](unsigned row) const { 136 | return mat[row]; 137 | } 138 | 139 | template 140 | T &Matrix::ElemAt(unsigned row, unsigned col) { 141 | return transpose ? mat[col][row] : mat[row][col]; 142 | } 143 | 144 | template 145 | T const &Matrix::ElemAt(unsigned row, unsigned col) const { 146 | return transpose ? mat[col][row] : mat[row][col]; 147 | } 148 | 149 | template 150 | void Matrix::MultByTranspose() { 151 | if (mat.empty()) return; 152 | Matrix newMatrix(NumRows(), NumRows(), dummy); 153 | 154 | for (unsigned i = 0; i < NumRows(); i++) { 155 | for (unsigned j = i; j < NumRows(); j++) { 156 | newMatrix(i,j) = ElemAt(i,0); 157 | newMatrix(i,j) *= ElemAt(j,0); 158 | 159 | for (unsigned k = 1; k < NumCols(); k++) { 160 | T tmp = ElemAt(i, k); 161 | tmp *= ElemAt(j, k); 162 | 163 | newMatrix(i,j) += tmp; 164 | } 165 | 166 | if (i != j) { 167 | newMatrix(j,i) = newMatrix(i,j); 168 | } 169 | } 170 | } 171 | 172 | swap(newMatrix.mat, mat); 173 | transpose = false; 174 | } 175 | 176 | template 177 | void Matrix::Transpose() { 178 | transpose = !transpose; 179 | } 180 | 181 | template 182 | void Matrix::Invert(T& det, std::function reduce) { 183 | unsigned dim = NumRows(); 184 | 185 | Matrix adj(dim, dim, dummy); 186 | vector usedRows(dim), usedCols(dim); 187 | for (unsigned i = 0; i < dim; i++) { 188 | for (unsigned j = 0; j < dim; j++) { 189 | usedRows[i] = usedCols[j] = true; 190 | Determinant(adj(j,i), usedRows, usedCols, dim-1, reduce); 191 | usedRows[i] = usedCols[j] = false; 192 | if ((i+j) % 2 == 1) { 193 | adj(j,i) *= -1; 194 | } 195 | } 196 | } 197 | 198 | // Since we already computed the adjugate matrix, we can 199 | // reuse the results to compute the determinant 200 | det = ElemAt(0,0); 201 | det *= adj(0,0); 202 | for (unsigned i = 1; i < dim; i++) { 203 | T tmp = ElemAt(0,i); 204 | 205 | tmp *= adj(i,0); 206 | det += tmp; 207 | } 208 | if (reduce) { 209 | reduce(det); 210 | } 211 | 212 | swap(adj.mat, mat); 213 | transpose = false; 214 | } 215 | 216 | template 217 | void Matrix::Determinant(T &det, std::function reduce) const { 218 | unsigned dim = NumRows(); 219 | vector usedRows(dim), usedCols(dim); 220 | Determinant(det, usedRows, usedCols, dim, reduce); 221 | } 222 | 223 | template 224 | void Matrix::Determinant(T &det, vector &usedRows, 225 | vector &usedCols, unsigned dim, 226 | std::function reduce) const { 227 | unsigned matDim = NumRows(); 228 | unsigned row = 0; 229 | while (usedRows[row]) row++; 230 | 231 | bool negative = false; 232 | bool first = true; 233 | for (unsigned col = 0; col < matDim; col++) { 234 | if (usedCols[col]) continue; 235 | 236 | if (dim == 1) { 237 | det = ElemAt(row, col); 238 | return; 239 | } 240 | 241 | T tmp = ElemAt(row, col); 242 | if (negative) tmp *= -1; 243 | negative = !negative; 244 | 245 | usedRows[row] = usedCols[col] = true; 246 | T tmp2(dummy); 247 | Determinant(tmp2, usedRows, usedCols, dim-1, reduce); 248 | usedRows[row] = usedCols[col] = false; 249 | 250 | tmp *= tmp2; 251 | 252 | if (first) { 253 | det = tmp; 254 | first = false; 255 | } else { 256 | det += tmp; 257 | } 258 | } 259 | 260 | if (reduce) { 261 | reduce(det); 262 | } 263 | } 264 | 265 | template 266 | unsigned Matrix::NumRows() const { 267 | if (mat.empty()) return 0; 268 | return transpose ? mat[0].size() : mat.size(); 269 | } 270 | 271 | template 272 | unsigned Matrix::NumCols() const { 273 | if (mat.empty()) return 0; 274 | return transpose ? mat.size() : mat[0].size(); 275 | } 276 | 277 | template 278 | void Matrix::Resize(unsigned nRows, unsigned nCols) { 279 | Clear(); 280 | transpose = false; 281 | 282 | mat.resize(nRows); 283 | for (unsigned i = 0; i < nRows; i++) { 284 | mat[i].resize(nCols, dummy); 285 | } 286 | } 287 | 288 | template 289 | void Matrix::AddRow(vector &row) { 290 | if (transpose) return; // No support for adding to transposed matrix 291 | mat.push_back(row); 292 | } 293 | 294 | template 295 | void Matrix::Concatenate(Matrix &other) { 296 | if (transpose) return; // No support for concatenating to transposed matrix 297 | mat.insert(mat.end(), other.mat.begin(), other.mat.end()); 298 | } 299 | 300 | template 301 | void Matrix::Clear() { 302 | mat.clear(); 303 | } 304 | 305 | template 306 | void Matrix::MapAll(std::function func) { 307 | for (unsigned i = 0; i < mat.size(); i++) { 308 | for (unsigned j = 0; j < mat[i].size(); j++) { 309 | func(mat[i][j]); 310 | } 311 | } 312 | } 313 | 314 | template 315 | ostream &operator<<(ostream &os, const Matrix &m) { 316 | for (unsigned i = 0; i < m.NumRows(); i++) { 317 | for (unsigned j = 0; j < m.NumCols(); j++) { 318 | os << m(i,j) << " "; 319 | } 320 | os << endl; 321 | } 322 | return os; 323 | } 324 | 325 | #endif 326 | -------------------------------------------------------------------------------- /Matrix.h: -------------------------------------------------------------------------------- 1 | #ifndef _MATRIX_H 2 | #define _MATRIX_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | using namespace std; 9 | 10 | template 11 | class Matrix; 12 | 13 | template 14 | ostream& operator<<(ostream &, const Matrix &); 15 | 16 | template 17 | class Matrix { 18 | T dummy; 19 | public: 20 | Matrix() : dummy(T()) { 21 | transpose = false; 22 | } 23 | Matrix(const T &dummy) : dummy(dummy) { 24 | transpose = false; 25 | } 26 | 27 | Matrix(unsigned nRows, unsigned nCols, const T &dummy) : dummy(dummy) { 28 | Resize(nRows, nCols); 29 | } 30 | 31 | Matrix(unsigned nRows, unsigned nCols) : dummy(T()) { 32 | Resize(nRows, nCols); 33 | } 34 | 35 | Matrix operator+(const Matrix &other) const; 36 | Matrix &operator+=(const Matrix &other); 37 | 38 | Matrix operator-(const Matrix &other) const; 39 | Matrix &operator-=(const Matrix &other); 40 | 41 | Matrix operator*(Matrix &other) const; 42 | Matrix operator*(vector &other) const; 43 | Matrix &operator*=(Matrix &other); 44 | Matrix &operator*=(vector &other); 45 | Matrix &operator*=(T &other); 46 | 47 | Matrix &operator=(const Matrix &other); 48 | 49 | T &operator()(unsigned row, unsigned col); 50 | T const &operator()(unsigned row, unsigned col) const; 51 | 52 | vector &operator[](unsigned row); 53 | vector const &operator[](unsigned row) const; 54 | 55 | void MultByTranspose(); 56 | void Transpose(); 57 | void Invert(T& det, std::function reduce = NULL); 58 | 59 | void Determinant(T& det, std::function reduce = NULL) const; 60 | 61 | unsigned NumRows() const; 62 | unsigned NumCols() const; 63 | 64 | void Resize(unsigned nRows, unsigned nCols); 65 | 66 | void AddRow(vector &row); 67 | void Concatenate(Matrix &other); 68 | void Clear(); 69 | 70 | void MapAll(std::function func); 71 | 72 | friend ostream &operator<< <>(ostream &os, const Matrix &m); 73 | private: 74 | vector> mat; 75 | bool transpose; 76 | 77 | T &ElemAt(unsigned row, unsigned col); 78 | T const &ElemAt(unsigned row, unsigned col) const; 79 | 80 | void Determinant(T& det, vector &usedRows, 81 | vector &usedCols, unsigned dim, 82 | std::function reduce = NULL) const; 83 | }; 84 | 85 | #endif 86 | -------------------------------------------------------------------------------- /NumbTh.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012,2013 IBM Corp. 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | * See the GNU General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License along 13 | * with this program; if not, write to the Free Software Foundation, Inc., 14 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 15 | */ 16 | 17 | #include "NumbTh.h" 18 | 19 | #include 20 | #include 21 | 22 | using namespace std; 23 | 24 | // Factoring by trial division, only works for N<2^{60}. 25 | // Only the primes are recorded, not their multiplicity 26 | template static void factorT(vector &factors, const zz &N) 27 | { 28 | factors.resize(0); // reset the factors 29 | 30 | if (N<2) return; // sanity check 31 | 32 | PrimeSeq s; 33 | zz n = N; 34 | while (true) { 35 | if (ProbPrime(n)) { // we are left with just a single prime 36 | factors.push_back(n); 37 | return; 38 | } 39 | // if n is a composite, check if the next prime divides it 40 | long p = s.next(); 41 | if ((n%p)==0) { 42 | zz pp; 43 | conv(pp,p); 44 | factors.push_back(pp); 45 | do { n /= p; } while ((n%p)==0); 46 | } 47 | if (n==1) return; 48 | } 49 | } 50 | void factorize(vector &factors, long N) { factorT(factors, N);} 51 | void factorize(vector &factors, const ZZ& N) {factorT(factors, N);} 52 | 53 | template static void phiNT(zz &phin, vector &facts, const zz &N) 54 | { 55 | if (facts.size()==0) factorize(facts,N); 56 | 57 | zz n = N; 58 | conv(phin,1); // initialize phiN=1 59 | for (unsigned i=0; i &fs, long N) { phiNT(pN,fs,N); } 67 | void phiN(ZZ &pN, vector &fs, const ZZ &N) { phiNT(pN,fs,N); } 68 | 69 | /* Compute Phi(N) */ 70 | int phi_N(int N) 71 | { 72 | int phiN=1,p,e; 73 | PrimeSeq s; 74 | while (N!=1) 75 | { p=s.next(); 76 | e=0; 77 | while ((N%p)==0) { N=N/p; e++; } 78 | if (e!=0) 79 | { phiN=phiN*(p-1)*power_long(p,e-1); } 80 | } 81 | return phiN; 82 | } 83 | 84 | // finding e-th root of unity modulo the current modulus 85 | template void FindPrimRootT(zp &root, unsigned e) 86 | { 87 | zz phiQ; // phi(q) 88 | { vector facts; // just so we can call the PhiN routine 89 | zz q = zp::modulus(); 90 | phiN(phiQ, facts, q); 91 | } 92 | if ((phiQ%e)!=0) { // error, no e'th roots of unity exist 93 | conv(root,0); // set root = 0 and return 94 | return; 95 | } 96 | vector facts; 97 | factorize(facts,e); // factorization of e 98 | zz exp = phiQ/e; 99 | 100 | // generate random s^exp, then check if they have order e 101 | for (int i=0; i<1000; i++) { // similar to while(true) { ... } 102 | zp s; 103 | random(s); // s = random in Z_p 104 | power(root, s, exp); // try root = s^{phi(q)/e} mod p 105 | power(s, root, e); // this should be 1, if we chose (s,Q)==1 106 | if (s != 1) continue; 107 | 108 | // check that s^{e/p} != 1 for any prime divisor p of e 109 | for (unsigned i=0; i(r,e);} 121 | void FindPrimitiveRoot(ZZ_p &r, unsigned e) {FindPrimRootT(r,e);} 122 | 123 | /* Compute mobius function (naive method as n is small) */ 124 | int mobius(int n) 125 | { 126 | int p,e,arity=0; 127 | PrimeSeq s; 128 | while (n!=1) 129 | { p=s.next(); 130 | e=0; 131 | while ((n%p==0)) { n=n/p; e++; } 132 | if (e>1) { return 0; } 133 | if (e!=0) { arity^=1; } 134 | } 135 | if (arity==0) { return 1; } 136 | return -1; 137 | } 138 | 139 | 140 | 141 | /* Compute cyclotomic polynomial */ 142 | ZZX Cyclotomic(int N) 143 | { 144 | ZZX Num,Den,G,F; 145 | set(Num); set(Den); 146 | int m,d; 147 | for (d=1; d<=N; d++) 148 | { if ((N%d)==0) 149 | { clear(G); 150 | SetCoeff(G,N/d,1); SetCoeff(G,0,-1); 151 | m=mobius(d); 152 | if (m==1) { Num*=G; } 153 | else if (m==-1) { Den*=G; } 154 | } 155 | } 156 | F=Num/Den; 157 | return F; 158 | } 159 | 160 | 161 | 162 | /* Find a primitive root modulo N */ 163 | int primroot(int N,int phiN) 164 | { 165 | int g=2,p; 166 | PrimeSeq s; 167 | bool flag=false; 168 | 169 | while (flag==false) 170 | { flag=true; 171 | s.reset(1); 172 | do 173 | { p=s.next(); 174 | if ((phiN%p)==0) 175 | { if (PowerMod(g,phiN/p,N)==1) 176 | { flag=false; } 177 | } 178 | } 179 | while (p>1; 203 | for (int i=0; ideg(in)) trunc(out,out,deg(in)+1); // remove high degrees 215 | 216 | ZZ q2; q2=q>>1; 217 | for (int i=0; i<=deg(in); i++) 218 | { ZZ c=coeff(in,i); 219 | c %= q; 220 | if (abs) { 221 | if (c<0) c += q; 222 | } 223 | else if (q!=2) { 224 | if (c>q2) { c=c-q; } 225 | else if (c<-q2) { c=c+q; } 226 | } 227 | else // q=2 228 | { if (sign(coeff(in,i))!=sign(c)) 229 | { c=-c; } 230 | } 231 | SetCoeff(out,i,c); 232 | } 233 | } 234 | 235 | 236 | void PolyRed(ZZX& out, const ZZX& in, int q, bool abs) 237 | { 238 | // ensure that out has the same degree as in 239 | out.SetMaxLength(deg(in)+1); // allocate space if needed 240 | if (deg(out)>deg(in)) trunc(out,out,deg(in)+1); // remove high degrees 241 | 242 | int q2; q2=q>>1; 243 | for (int i=0; i<=deg(in); i++) 244 | { int c=coeff(in,i)%q; 245 | if (abs) 246 | { if (c<0) { c=c+q; } } 247 | else if (q==2) 248 | { if (coeff(in,i)<0) { c=-c; } } 249 | else 250 | { if (c>q2) { c=c-q; } 251 | else if (c<-q2) { c=c+q; } 252 | } 253 | SetCoeff(out,i,c); 254 | } 255 | } 256 | 257 | 258 | 259 | GF2X to_GF2X(const ZZX& a) 260 | { 261 | GF2X ans; 262 | if (!IsZero(a)) 263 | { ans.SetMaxLength(deg(a)); 264 | for (int i=0; i<=deg(a); i++) 265 | { SetCoeff(ans,i,coeff(a,i)%2); } 266 | } 267 | return ans; 268 | } 269 | 270 | 271 | 272 | ZZX to_ZZX(const GF2X& a) 273 | { 274 | ZZX ans; 275 | if (!IsZero(a)) 276 | { ans.SetMaxLength(deg(a)); 277 | for (int i=0; i<=deg(a); i++) 278 | { SetCoeff(ans,i,rep(coeff(a,i))); } 279 | } 280 | return ans; 281 | } 282 | 283 | 284 | 285 | int is_in(int x,int* X,int sz) 286 | { 287 | for (int i=0; i0,q>0 with q odd, 293 | * and such that all the entries in vp are in [-p/2,p/2) and all entries in 294 | * vq are in [0,q-1). Returns in vp the CRT of vp mod p and vq mod q, as 295 | * integers in [-pq/2, pq/2). Uses the formula: 296 | * 297 | * CRT(vp,p,vq,q) = vp + p*[ (vq-vp)*p^{-1} ]_q 298 | * 299 | * where [...]_q means reduction to the interval [-q/2,q/2). As q is odd then 300 | * this is the same as reducing to [-(q-1)/2,(q-1)/2], hence [...]_q * p is 301 | * in [-p(q-1)/2, p(q-1)/2], and since vp is in [-p/2,p/2) then the sum is 302 | * indeed in [-pq/2,pq/2). 303 | * 304 | * Returns true if both vectors are of the same length, false otherwise 305 | */ 306 | template 307 | bool intVecCRT(vec_ZZ& vp, const ZZ& p, const zzvec& vq, long q) 308 | { 309 | long pInv = InvMod(rem(p,q), q); // p^{-1} mod q 310 | long n = min(vp.length(),vq.length()); 311 | long q_over_2 = q/2; 312 | ZZ tmp; 313 | long vqi; 314 | for (long i=0; i q_over_2) delta_times_pInv -= q; 320 | 321 | mul(tmp, delta_times_pInv, p); // tmp = [(vq_i-vp_i)*p^{-1}]_q * p 322 | vp[i] += tmp; 323 | } 324 | // other entries (if any) are 0 mod q 325 | for (long i=vq.length(); i q_over_2) delta_times_pInv -= q; 330 | 331 | mul(tmp, delta_times_pInv, p); // tmp = [(vq_i-vp_i)*p^{-1}]_q * p 332 | vp[i] += tmp; 333 | } 334 | return (vp.length()==vq.length()); 335 | } 336 | // specific instantiations: vq can be vec_long or vec_ZZ 337 | template bool intVecCRT(vec_ZZ&, const ZZ&, const vec_ZZ&, long); 338 | template bool intVecCRT(vec_ZZ&, const ZZ&, const vec_long&, long); 339 | 340 | void sampleHWt(ZZX &poly, long Hwt, long n) 341 | { 342 | if (n<=0) n=deg(poly)+1; if (n<=0) return; 343 | clear(poly); // initialize to zero 344 | poly.SetMaxLength(n); // allocate space for degree-(n-1) polynomial 345 | 346 | long b,u,i=0; 347 | if (Hwt>n) Hwt=n; 348 | while (i= 0; i--) 416 | r = (r*hh + coeff(g, i)) % f; 417 | 418 | res = r; 419 | } 420 | 421 | ZZ largestCoeff(const ZZX& f) 422 | { 423 | ZZ mx = ZZ::zero(); 424 | for (long i=0; i<=deg(f); i++) { 425 | if (mx < abs(coeff(f,i))) 426 | mx = abs(coeff(f,i)); 427 | } 428 | return mx; 429 | } 430 | -------------------------------------------------------------------------------- /NumbTh.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012,2013 IBM Corp. 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | * See the GNU General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License along 13 | * with this program; if not, write to the Free Software Foundation, Inc., 14 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 15 | */ 16 | 17 | #ifndef _NumbTh 18 | #define _NumbTh 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | NTL_CLIENT 30 | 31 | // MinGW hack 32 | #ifndef lrand48 33 | #define drand48() (((double)rand()) / RAND_MAX) 34 | #define lrand48() rand() 35 | #define srand48(X) srand(X) 36 | #endif 37 | 38 | inline double log2(const xdouble& x){ return log(x) * 1.442695040889; } 39 | inline double log2(const double x){ return log(x) * 1.442695040889; } 40 | 41 | // Factoring by trial division, only works for N<2^{60}, only the primes 42 | // are recorded, not their multiplicity. class zz can be long or ZZ 43 | void factorize(vector &factors, long N); 44 | void factorize(vector &factors, const ZZ& N); 45 | 46 | /* Compute Phi(N) and also factorize N */ 47 | void phiN(long &phiN, vector &facts, long N); 48 | void phiN(ZZ &phiN, vector &facts, const ZZ &N); 49 | 50 | /* Compute Phi(N) */ 51 | int phi_N(int N); 52 | 53 | // Find e-th root of unity modulo the current modulus 54 | void FindPrimitiveRoot(zz_p &r, unsigned e); 55 | void FindPrimitiveRoot(ZZ_p &r, unsigned e); 56 | 57 | /* Compute mobius function (naive method as n is small) */ 58 | int mobius(int n); 59 | 60 | /* Compute cyclotomic polynomial */ 61 | ZZX Cyclotomic(int N); 62 | 63 | /* Find a primitive root modulo N */ 64 | int primroot(int N,int phiN); 65 | 66 | int ord(int N,int p); 67 | 68 | 69 | // Rand mod p poly of degree < n 70 | ZZX RandPoly(int n,const ZZ& p); 71 | 72 | 73 | /* When abs=false reduce to interval (-q/2,...,q/2), when abs=true reduce 74 | * to [0,q). When abs=false and q=2, maintains the same sign as the input. 75 | */ 76 | void PolyRed(ZZX& out, const ZZX& in, int q, bool abs=false); 77 | void PolyRed(ZZX& out, const ZZX& in, const ZZ& q, bool abs=false); 78 | inline void PolyRed(ZZX& F, int q, bool abs=false) { PolyRed(F,F,q,abs); } 79 | inline void PolyRed(ZZX& F, const ZZ& q, bool abs=false) 80 | { PolyRed(F,F,q,abs); } 81 | 82 | GF2X to_GF2X(const ZZX& a); 83 | ZZX to_ZZX(const GF2X& a); 84 | 85 | 86 | 87 | /* Finds whether x is an element of the set X 88 | of size sz, returns -1 it not and the location if true 89 | */ 90 | int is_in(int x,int* X,int sz); 91 | 92 | /* Incremental integer CRT for vectors. Expects co-primes p,q with q odd, 93 | * and such that all the entries in v1 are in [-p/2,p/2). Returns in v1 the 94 | * CRT of vp mod p and vq mod q, as integers in [-pq/2, pq/2). Uses the 95 | * formula: 96 | * CRT(vp,p,vq,q) = vp + [(vq-vp)*p^{-1}]_q * p, 97 | * 98 | * where [...]_q means reduction to the interval [-q/2,q/2). Notice that if q 99 | * is odd then this is the same as reducing to [-(q-1)/2,(q-1)/2], which means 100 | * that [...]_q * p is in [-p(q-1)/2, p(q-1)/2], and since vp is in [-p/2,p/2) 101 | * then the sum is indeed in [-pq/2,pq/2). 102 | * 103 | * return true is both vectors are of the same length, false otherwise 104 | */ 105 | template // zzvec can be vec_ZZ or vec_long 106 | bool intVecCRT(vec_ZZ& vp, const ZZ& p, const zzvec& vq, long q); 107 | 108 | // argmax(v)/argmin(v) finds the index of the (first) largest/smallest 109 | // element in the vector v. They are roughly just simpler variants of 110 | // std::max_element and std::mim_element. 111 | // argmin/argmax are implemented as a template, so the code must be placed 112 | // in the header file for the comiler to find it. The class T must have an 113 | // implementation of operator> and operator< for this template to work. 114 | 115 | template 116 | long argminmax(vector& v) 117 | { 118 | if (v.size()<1) return -1; // error: this is an empty array 119 | unsigned idx = 0; 120 | T target = v[0]; 121 | for (unsigned i=1; i target) { target = v[i]; idx = i;} } 123 | else { if (v[i] < target) { target = v[i]; idx = i;} } 124 | return (long) idx; 125 | } 126 | 127 | template long argmax(vector& v) 128 | { return argminmax(v); } 129 | 130 | template long argmin(vector& v) 131 | { return argminmax(v); } 132 | 133 | 134 | // Sample polynomials with entries {-1,0,1}. These functions are similar to 135 | // the SampleSmall class from v1, but without a class around it. 136 | 137 | // In sampleSmall, each coeff is 0 w/ prob. 1/2 and +-1 w/ prob. 1/4. In 138 | // sampleHWt, min(Hwt,n) random coefficients are chosen at random in {-1,+1} 139 | // and the others are set to zero. If n=0 then n=poly.deg()+1 is used. 140 | 141 | void sampleSmall(ZZX &poly, long n=0); 142 | void sampleHWt(ZZX &poly, long Hwt, long n=0); 143 | 144 | // Sample polynomials with Gaussian coefficients. This function is similar 145 | // to the MultivariateGauss class from v1, but without a class around it. 146 | 147 | void sampleGaussian(ZZX &poly, long n=0, double stdev=1.0); 148 | 149 | 150 | // NTL's random number generation faciliity is pretty limited, 151 | // and does not provide a way to save/restore the state of a 152 | // pseudo-random stream. The following gives us that ability. 153 | 154 | class RandomState { 155 | private: 156 | ZZ state; 157 | bool restored; 158 | 159 | public: 160 | RandomState() { 161 | RandomBits(state, 512); 162 | restored = false; 163 | } 164 | 165 | void restore() { 166 | if (!restored) { 167 | SetSeed(state); 168 | restored = true; 169 | } 170 | } 171 | 172 | ~RandomState() { 173 | restore(); 174 | } 175 | 176 | private: 177 | RandomState(const RandomState&); // disable copy constructor 178 | RandomState& operator=(const RandomState&); // disable assignment 179 | }; 180 | 181 | // usage: 182 | // { RandomState state; ... } 183 | // 184 | // The destructor will restore state at end of scope, 185 | // but this can also be done explicitly via the restore method. 186 | 187 | 188 | // An experimental facility...it is annoying that vector::size() 189 | // is an unsigned quantity...this leads to all kinds of annoying 190 | // warning messages... 191 | 192 | template 193 | inline long lsize(const vector& v) { 194 | return (long) v.size(); 195 | } 196 | 197 | 198 | // modular composition: res = g(h) mod f 199 | void ModComp(ZZX& res, const ZZX& g, const ZZX& h, const ZZX& f); 200 | 201 | // returns the largest absolute value coefficient 202 | ZZ largestCoeff(const ZZX& f); 203 | 204 | #endif 205 | -------------------------------------------------------------------------------- /PAlgebra.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012,2013 IBM Corp. 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | * See the GNU General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License along 13 | * with this program; if not, write to the Free Software Foundation, Inc., 14 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 15 | */ 16 | 17 | /* 18 | * PAlgebra.cpp - Implementation of the class PAlgebra 19 | * 20 | * The class PAlgebra is the base class containing the structure of (Z/mZ)^*, 21 | * which is isomorphic to the Galois group over A = Z[X]/Phi_m(X)). 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | NTL_CLIENT 29 | 30 | #include // defines INT_MAX 31 | #include 32 | #include // defines count(...), min(...) 33 | #include 34 | 35 | #include "NumbTh.h" // defines argmax(...) 36 | #include "PAlgebra.h" 37 | 38 | 39 | // Generate the representation of Z_m^* for a given odd integer m 40 | void PAlgebra::init(unsigned mm, unsigned g) { 41 | if (m==mm) return; // nothing to do 42 | 43 | if (mm>NTL_SP_BOUND) return; //(mm&1)==0 || 44 | 45 | m = mm; 46 | this->g = g; 47 | zz_p::init(mm); 48 | 49 | long idx; 50 | 51 | zmsIdx.assign(m,-1); // allocate m slots, initialize them to -1 52 | for (unsigned i=idx=0; iphim = idx; 54 | 55 | Phi_mX = Cyclotomic(m); // compute and store Phi_m(X) 56 | } 57 | -------------------------------------------------------------------------------- /PAlgebra.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012,2013 IBM Corp. 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | * See the GNU General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License along 13 | * with this program; if not, write to the Free Software Foundation, Inc., 14 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 15 | */ 16 | 17 | #ifndef _PAlgebra_H_ 18 | #define _PAlgebra_H_ 19 | /* PAlgebra.h - Declatations of the classes PAlgebra, PAlgebra2, PAlgebra2r 20 | * 21 | * The class PAlgebra is the base class containing the structure of (Z/mZ)^*, 22 | * which is isomorphic to the Galois group over A = Z[X]/Phi_m(X)). The 23 | * derived classes PAlgebra2 and PAlgebra2r contain also the structure of 24 | * the plaintext spaces A_2 = A/2A and A_2r = A/(2^r A), namely polynomials 25 | * over Z/2Z and polynomials over Z/2^r Z. 26 | * 27 | * We representat (Z/mZ)^* as (Z/mZ)^* = <2> x x 28 | * where the group generated by g1,g2,... consists of the elements that 29 | * have the same order in (Z/mZ)^* as in (Z/mZ)^* /<2>, and h1,h2,... 30 | * generate the remaining quotient group (Z/mZ)^* /<2,g1,g2,...>. 31 | * 32 | * We let T \subset (Z/mZ)^* be a set of representatives for the quotient 33 | * group (Z/mZ)^* /<2>, defined as T={ \prod_i gi^{ei}\cdot\prod_j hj^{ej} } 34 | * where the ei's range over 0,1,...,ord(gi)-1 and the ej's range over 35 | * 0,1,...ord(hj)-1 (these last orders are in (Z/mZ)^* /<2,g1,g2,...>). 36 | * 37 | * Phi_m(X) is factored as Phi_m(X)=\prod_{t\in T} F_t(X) mod 2 (or mod 2^r), 38 | * where the F_t's are irreducible modulo 2 (or 2^r). An arbitrarily factor 39 | * is chosen as F_1, then for each t \in T we associate with the index t the 40 | * factor F_t(X) = GCD(F_1(X^t), Phi_m(X)). 41 | * 42 | * Note that fixing a representation of the field R=(Z/2Z)[X]/F_1(X) (or 43 | * ring R=(Z/2^rZ)[X]/F_1(X)) and letting z be a root of F_1 in R (which 44 | * is a primitive m-th root of unity in R), we get that F_t is the minimal 45 | * polynomial of z^{1/t}. 46 | */ 47 | #include 48 | #include 49 | #include 50 | #include 51 | 52 | NTL_CLIENT 53 | class PAlgebra { 54 | unsigned m; // the integer m defines (Z/mZ)^*, Phi_m(X), etc. 55 | unsigned g; // generator for the cyclic group (Z/mZ)^* 56 | unsigned phim; // phi(m) 57 | 58 | ZZX Phi_mX; // Holds the integer polynomial Phi_m(X) 59 | double cM; // the ring constant c_m for Z[X]/Phi_m(X) 60 | 61 | vector zmsIdx; // if t is the i'th element in (Z/mZ)* then zmsIdx[t]=i 62 | // zmsIdx[t]==-1 if t\notin (Z/mZ)* 63 | 64 | public: 65 | void init(unsigned mm, unsigned g); // compute the structure of (Z/mZ)^* 66 | 67 | // Constructors 68 | PAlgebra(unsigned mm=0,unsigned g=0) : m(0) { init(mm,g); } 69 | 70 | // I/O methods 71 | 72 | void printout() const; // prints the structure in a readable form 73 | 74 | // Access methods 75 | 76 | unsigned M() const { return m; } 77 | unsigned G() const { return g; } 78 | unsigned phiM() const { return phim; } 79 | const ZZX& PhimX() const { return Phi_mX; } 80 | 81 | 82 | int indexInZmstar(unsigned t) const // returns the index of t in (Z/mZ)* 83 | { return (t>0 && t0 && t-1); } 87 | 88 | }; 89 | 90 | #endif // #ifdef _PAlgebra_H_ 91 | -------------------------------------------------------------------------------- /Plaintext.cpp: -------------------------------------------------------------------------------- 1 | #include "Plaintext.h" 2 | #include "NumbTh.h" 3 | #include 4 | #include "assert.h" 5 | 6 | void Plaintext::Init(const ZZ_pX &msg) { 7 | message = msg; 8 | } 9 | 10 | void Plaintext::Init(const vector &msgs) { 11 | EmbedInSlots(msgs); 12 | } 13 | 14 | Plaintext &Plaintext::operator=(const Plaintext &other) { 15 | assert(&context == &other.context); 16 | message = other.message; 17 | 18 | return *this; 19 | } 20 | 21 | bool Plaintext::operator==(const Plaintext &other) const { 22 | return ((&context == &other.context) && 23 | (message == other.message)); 24 | } 25 | 26 | void Plaintext::EmbedInSlots(const vector &msgs, bool onlyUsable) { 27 | context.GetPlaintextSpace().EmbedInSlots(message, msgs, onlyUsable); 28 | } 29 | 30 | void Plaintext::DecodeSlots(vector &msgBatch, bool onlyUsable) { 31 | context.GetPlaintextSpace().DecodeSlots(msgBatch, message, onlyUsable); 32 | } 33 | 34 | void Plaintext::DecodeSlot(ZZ_pX &val, unsigned slot) { 35 | context.GetPlaintextSpace().DecodeSlot(val, message, slot); 36 | } 37 | 38 | ostream &operator<<(ostream &os, const Plaintext &ptxt) { 39 | return (os << ptxt.message); 40 | } -------------------------------------------------------------------------------- /Plaintext.h: -------------------------------------------------------------------------------- 1 | #ifndef _PLAINTEXT_H 2 | #define _PLAINTEXT_H 3 | 4 | #include "NTL/ZZX.h" 5 | #include 6 | #include 7 | #include 8 | #include "FHEContext.h" 9 | 10 | class Plaintext { 11 | public: 12 | Plaintext() : context(*activeContext) {} 13 | Plaintext(const FHEcontext &context) : context(context) {} 14 | 15 | Plaintext(const ZZ_pX &msg) : context(*activeContext) { Init(msg); } 16 | Plaintext(const FHEcontext &context, const ZZ_pX &msg) : context(context) { Init(msg); } 17 | 18 | template 19 | Plaintext(const T &msg) : context(*activeContext) { Init(to_ZZ_pX(msg)); } 20 | 21 | template 22 | Plaintext(const FHEcontext &context, const T &msg) : context(context) { Init(to_ZZ_pX(msg)); } 23 | 24 | Plaintext(const vector &msgs) : context(*activeContext) { Init(msgs); } 25 | Plaintext(const FHEcontext &context, const vector &msgs) : context(context) { Init(msgs); } 26 | 27 | template 28 | Plaintext(const vector &msgs) : context(*activeContext) { Init(msgs); } 29 | 30 | template 31 | Plaintext(const FHEcontext &context, const vector &msgs) : context(context) { Init(msgs); } 32 | 33 | void Init(); 34 | void Init(const ZZ_pX &msg); 35 | 36 | void Init(const vector &msgs); 37 | 38 | template 39 | void Init(const vector &msgs) { 40 | vector newMsgs(msgs.size()); 41 | for (unsigned i = 0; i < msgs.size(); i++) { 42 | newMsgs[i] = to_ZZ_pX(msgs[i]); 43 | } 44 | 45 | EmbedInSlots(newMsgs); 46 | } 47 | 48 | void EmbedInSlots(const vector &msgs, bool onlyUsable = true); 49 | void DecodeSlots(vector &msgBatch, bool onlyUsable = true); 50 | void DecodeSlot(ZZ_pX &val, unsigned slot); 51 | 52 | Plaintext &operator=(const Plaintext &other); 53 | 54 | bool operator==(const Plaintext &other) const; 55 | friend ostream &operator<<(ostream &os, const Plaintext &ptxt); 56 | 57 | ZZ_pX message; 58 | 59 | /*DEBUG functions*/ 60 | 61 | void Randomize() { 62 | random(message, deg(context.zMstar.PhimX())-1); 63 | } 64 | static Plaintext Random(const FHEcontext &context) { 65 | Plaintext ret(context); 66 | ret.Randomize(); 67 | return ret; 68 | } 69 | 70 | Plaintext &operator+=(const Plaintext &other) { 71 | assert(&context == &other.context); 72 | message += other.message; 73 | return *this; 74 | } 75 | 76 | Plaintext &operator-=(const Plaintext &other) { 77 | assert(&context == &other.context); 78 | message -= other.message; 79 | return *this; 80 | } 81 | 82 | Plaintext &operator*=(const Plaintext &other) { 83 | assert(&context == &other.context); 84 | MulMod(message, message, other.message, to_ZZ_pX(context.zMstar.PhimX())); 85 | return *this; 86 | } 87 | 88 | Plaintext &operator>>=(long k) { 89 | vector plaintextArray; 90 | DecodeSlots(plaintextArray, false); 91 | vector rotatedArray = plaintextArray; 92 | for (unsigned i = 0; i < plaintextArray.size(); i++) { 93 | rotatedArray[(i + plaintextArray.size() - k) % plaintextArray.size()] = plaintextArray[i]; 94 | } 95 | EmbedInSlots(rotatedArray, false); 96 | return *this; 97 | } 98 | 99 | Plaintext operator+(const Plaintext &other) { 100 | assert(&context == &other.context); 101 | return Plaintext(context, message+other.message); 102 | } 103 | 104 | Plaintext operator*(const Plaintext &other) { 105 | assert(&context == &other.context); 106 | return Plaintext(context, MulMod(message, other.message, to_ZZ_pX(context.zMstar.PhimX()))); 107 | } 108 | 109 | private: 110 | const FHEcontext &context; 111 | }; 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /PlaintextSpace.cpp: -------------------------------------------------------------------------------- 1 | #ifndef _PLAINTEXT_SPACE_H_ 2 | #define _PLAINTEXT_SPACE_H_ 3 | 4 | #include "PlaintextSpace.h" 5 | #include "Util.h" 6 | #include 7 | #include "assert.h" 8 | 9 | static inline unsigned ZZToLong(const ZZ &num) { 10 | unsigned data; 11 | BytesFromZZ((unsigned char *)&data, num, sizeof(unsigned)); 12 | return data; 13 | } 14 | 15 | PlaintextSpace::~PlaintextSpace() { 16 | for (unsigned i = 0; i < factors.size(); i++) { 17 | delete factors[i]; 18 | delete crtCoeffs[i]; 19 | } 20 | } 21 | 22 | void PlaintextSpace::Init(const ZZX &PhiX, const ZZ &p) { 23 | ZZ_p::init(p); 24 | 25 | this->PhiX = to_ZZ_pX(PhiX); 26 | this->p = p; 27 | 28 | vec_ZZ_pX crtFactors; 29 | SFCanZass(crtFactors, this->PhiX); 30 | totalSlots = crtFactors.length(); 31 | 32 | factors.resize(totalSlots); 33 | for (unsigned i = 0; i < totalSlots; i++) { 34 | factors[i] = new ZZ_pX(crtFactors[i]); 35 | } 36 | 37 | usableSlots = 1; 38 | unsigned tmp = totalSlots; 39 | while (tmp > 1) { 40 | usableSlots <<= 1; 41 | tmp >>= 1; 42 | } 43 | 44 | crtCoeffs.resize(totalSlots); 45 | for (unsigned i = 0; i < totalSlots; i++) { 46 | crtCoeffs[i] = new ZZ_pX(); 47 | ZZ_pX te = this->PhiX / crtFactors[i]; 48 | te %= crtFactors[i]; 49 | InvMod(*crtCoeffs[i], te, crtFactors[i]); 50 | *crtCoeffs[i] *= (this->PhiX / crtFactors[i]); 51 | } 52 | 53 | FindSlots(totalSlots); 54 | } 55 | 56 | void PlaintextSpace::Init(const ZZX &PhiX, const ZZ &p, unsigned generator) { 57 | this->generator = generator; 58 | Init(PhiX, p); 59 | } 60 | 61 | unsigned PlaintextSpace::GetUsableSlots() const { 62 | return usableSlots; 63 | } 64 | 65 | unsigned PlaintextSpace::GetTotalSlots() const { 66 | return totalSlots; 67 | } 68 | 69 | void PlaintextSpace::FindSlots(unsigned nSlots) { 70 | vector crt(nSlots); 71 | for (unsigned i = 0; i < nSlots; i++) { 72 | crt[i] = to_ZZ_pX(i+1); 73 | } 74 | ZZ_pX permX; 75 | 76 | EmbedInSlots(permX, crt, false); 77 | FrobeniusMap(permX, generator); 78 | DecodeSlots(crt, permX, false); 79 | 80 | vector perm(nSlots); 81 | for (unsigned i = 0; i < nSlots; i++) { 82 | perm[i] = ZZToLong(rep(crt[i].rep[0])) - 1; 83 | } 84 | ReorderSlots(perm); 85 | } 86 | 87 | void PlaintextSpace::ReorderSlots(vector &perm) { 88 | // EDF doesn't always return the factors in a particular order, so 89 | // we place the 0 slot first to make it deterministic 90 | unsigned zeroInd = 0; 91 | for (; zeroInd < perm.size() && perm[zeroInd] != 0; zeroInd++); 92 | 93 | vector factorsP; 94 | vector crtCoeffsP; 95 | 96 | factorsP.push_back(factors[zeroInd]); 97 | crtCoeffsP.push_back(crtCoeffs[zeroInd]); 98 | for (unsigned i = perm[zeroInd]; i != zeroInd; i = perm[i]) { 99 | factorsP.push_back(factors[i]); 100 | crtCoeffsP.push_back(crtCoeffs[i]); 101 | } 102 | 103 | assert(factorsP.size() == perm.size()); 104 | 105 | swap(crtCoeffs, crtCoeffsP); 106 | swap(factors, factorsP); 107 | 108 | crtCoeffsP.clear(); 109 | factorsP.clear(); 110 | } 111 | 112 | void PlaintextSpace::EmbedInSlots(ZZ_pX &embedded, const vector &msgs, 113 | bool onlyUsable) const { 114 | embedded = ZZ_pX::zero(); 115 | unsigned msgInd = 0; 116 | for (unsigned i = 0; i < totalSlots && msgInd < msgs.size(); i++) { 117 | if (onlyUsable && i >= usableSlots) break; 118 | embedded += *crtCoeffs[i] * msgs[msgInd++]; 119 | } 120 | embedded %= PhiX; 121 | } 122 | 123 | void PlaintextSpace::DecodeSlots(vector &msgBatch, const ZZ_pX &msg, 124 | bool onlyUsable) const { 125 | msgBatch.resize(totalSlots); 126 | for (unsigned i = 0; i < totalSlots; i++) { 127 | if (onlyUsable && i >= usableSlots) break; 128 | DecodeSlot(msgBatch[i], msg, i); 129 | } 130 | } 131 | 132 | void PlaintextSpace::DecodeSlot(ZZ_pX &val, const ZZ_pX &msg, unsigned ind) const { 133 | rem(val, msg, *factors[ind]); 134 | } 135 | 136 | void PlaintextSpace::FrobeniusMap(ZZ_pX &poly, long k) { 137 | for (long i = deg(poly); i >= 0; i--) { 138 | SetCoeff(poly, k*i, poly.rep[i]); 139 | if (i > 0) { 140 | poly.rep[i] = 0; 141 | } 142 | } 143 | rem(poly, poly, PhiX); 144 | } 145 | 146 | #endif 147 | -------------------------------------------------------------------------------- /PlaintextSpace.h: -------------------------------------------------------------------------------- 1 | #ifndef _PLAINTEXT_SPACE_H 2 | #define _PLAINTEXT_SPACE_H 3 | 4 | #include "PAlgebra.h" 5 | #include 6 | #include 7 | #include 8 | 9 | class PlaintextSpace { 10 | public: 11 | PlaintextSpace() {}; 12 | ~PlaintextSpace(); 13 | 14 | void Init(const ZZX &PhiX, const ZZ &p); 15 | void Init(const ZZX &PhiX, const ZZ &p, unsigned generator); 16 | 17 | unsigned GetUsableSlots() const; 18 | unsigned GetTotalSlots() const; 19 | 20 | void EmbedInSlots(ZZ_pX &embedded, const vector &msgs, bool onlyUsable = true) const; 21 | void DecodeSlots(vector &msgBatch, const ZZ_pX &msg, bool onlyUsable = true) const; 22 | void DecodeSlot(ZZ_pX &val, const ZZ_pX &msg, unsigned ind) const; 23 | private: 24 | ZZ p; 25 | unsigned generator; 26 | 27 | unsigned totalSlots; 28 | unsigned usableSlots; 29 | 30 | ZZ_pX PhiX; 31 | 32 | vector factors; 33 | vector crtCoeffs; 34 | 35 | void FindSlots(unsigned nSlots); 36 | void ReorderSlots(vector &perm); 37 | 38 | void FrobeniusMap(ZZ_pX &poly, long k); 39 | 40 | friend class FHEcontext; 41 | }; 42 | 43 | #endif -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is an implementation of Brakerski's somewhat homomorphic encryption 2 | system (http://eprint.iacr.org/2012/078.pdf). Much of the algebraic and number 3 | theoretic facilities are taken from HElib (https://github.com/shaih/HElib). 4 | This library leverages the NTL library over GMP, and is distributed under the 5 | terms of the GNU General Public License (GPL). 6 | 7 | A more complete description of the implementation details is available here: 8 | http://cs.stanford.edu/~dwu4/FHE-SI_Report.pdf. 9 | 10 | =========== 11 | Basic Setup 12 | =========== 13 | 14 | To compile the system, simply run make in the main directory. Note that you 15 | will need to install the NTL library prior to compiling and running the 16 | system. NTL is available for download at http://www.shoup.net/ntl/. If 17 | compilation fails due to NTL (missing header files or linker errors), you will 18 | have to modify the Makefile accordingly. If the header files are not present 19 | in a default directory, adding -I/path/to/NTL/header/files to the CFLAGS 20 | should fix the problem. If there is a linker problem, add the path to the NTL 21 | library to LDPATH: -L/path/to/NTL/library. 22 | 23 | =================== 24 | Running Basic Tests 25 | =================== 26 | 27 | Several test programs are included to illustrate usage of the system. We describe them below. 28 | 29 | Test_AddMul: ./tests/TestAddMul_x logQ p generator [seed] 30 | --------------------------------------------------------- 31 | 32 | This test exercises the basic homomorphic operations of the Brakerski system. 33 | The first three parameters pertain to the SWHE system: 34 | logQ: the ciphertext modulus to use (q = 2^logQ) 35 | p: the plaintext modulus to use (should be a safe prime) 36 | generator: the generator for the plaintext space (a generator 37 | of the multiplicative group Z/(p-1)Z) 38 | 39 | If a seed is not provided, Test_AddMul will test the system against a series 40 | of randomly generated test cases. The test will report whether any of the test 41 | cases failed. This can be used to verify that the homomorphic operations of 42 | the system are properly behaving. If a seed is provided, the will just run the 43 | test using the specified seed for the random number generator. This can be 44 | used to debug specific test cases. 45 | 46 | Small parameters that demonstrate the basic functionality are: 47 | ./tests/TestAddMul_x 80 23 7 48 | Note that these parameters do not constitute a secure choice of parameters. More 49 | information on choosing parameters to guarantee a certain level of security may 50 | be found here: http://cs.stanford.edu/~dwu4/FHE-SI_Report.pdf. 51 | 52 | Test_General: ./tests/Test_General_x 53 | ----------------------------------- 54 | 55 | This is a test that is similar to the Test_General from HELib. Not all of the 56 | functionality supported by HELib is present in our implementation. It should, 57 | however, demonstrate some of the facilities that is supported by our 58 | implementation. 59 | 60 | Test_Regression: ./tests/Test_Regression_x datafile p generator 61 | --------------------------------------------------------------- 62 | 63 | This test showcases running linear regression on encrypted data. The datafile 64 | is the path to an input data file. The remaining parameters pertain to the 65 | SWHE. As in Test_AddMul, the parameter p is the plaintext modulus and the 66 | parameter generator is a generator for the plaintext space. Again, p should be 67 | a safe prime. 68 | 69 | The datafile consists of the data points that we regress over. The datafile 70 | format is specified as follows. The first line consists of two numbers, 71 | separated by a space. The first number is the dimension d and the second 72 | number specifies the number of points. What follows is a series of lines. Each 73 | of the subsequent lines contains d + 1 numbers. The first d values are the 74 | independent variables and the last value is the dependent variable. 75 | 76 | A Python script is provided to generate random data to run this test: 77 | 78 | ./scripts/generateRandomData.py filename d N [nFiles] 79 | 80 | The filename specifies the output datafile. The next parameter d specifies the 81 | data dimension and lastly, N specifies the number of data points to generate. 82 | The optional nFiles parameter allows you to split the data points into 83 | multiple files, for instance, to perform parallel execution of linear 84 | regression. 85 | 86 | More concrete details on regression analysis is provided in the report: 87 | http://cs.stanford.edu/~dwu4/FHE-SI_Report.pdf. 88 | 89 | Test_Statistics: ./tests/Test_Statistics_x datafile p generator 90 | --------------------------------------------------------------- 91 | 92 | This test demonstrates computing basic statistics on encrypted data. The 93 | parameters are the same as that of Test_Regression. More detailed information 94 | is available in the report. 95 | 96 | ============= 97 | Serialization 98 | ============= 99 | 100 | Public keys, secret keys, and ciphertexts are all serializable and can be 101 | saved to and loaded from files. Refer to "Serialization.h" to see how this can 102 | be done. 103 | -------------------------------------------------------------------------------- /Regression.h: -------------------------------------------------------------------------------- 1 | #ifndef _REGRESSION_H_ 2 | #define _REGRESSION_H_ 3 | 4 | #include "FHEContext.h" 5 | #include "Plaintext.h" 6 | #include "FHE-SI.h" 7 | #include "Ciphertext.h" 8 | #include 9 | #include 10 | 11 | #include "Matrix.h" 12 | #include "Matrix.cpp" 13 | 14 | bool LoadData(Matrix &rawData, vector &labels, 15 | unsigned &dim, const string &filename) { 16 | ifstream fin; 17 | fin.open(filename); 18 | if (!fin) { 19 | cout << "Unable to read data file." << endl; 20 | return false; 21 | } 22 | 23 | rawData.Clear(); 24 | labels.clear(); 25 | 26 | int label, n; 27 | 28 | fin >> dim >> n; 29 | vector data(dim); 30 | for (int i = 0; i < n; i++) { 31 | for (unsigned j = 0; j < dim; j++) { 32 | fin >> data[j]; 33 | } 34 | fin >> label; 35 | 36 | rawData.AddRow(data); 37 | labels.push_back(to_ZZ(label)); 38 | } 39 | 40 | return true; 41 | } 42 | 43 | double BatchData(vector> &ptxtData, vector &ptxtLabels, 44 | const Matrix<ZZ> &rawData, const vector<ZZ> &labels, const FHEcontext &context) { 45 | double start(clock()); 46 | 47 | ZZ p = to_ZZ(context.ModulusP()); 48 | ptxtData.clear(); 49 | ptxtLabels.clear(); 50 | unsigned batchSize = context.GetPlaintextSpace().GetUsableSlots(); 51 | for (unsigned i = 0; i < rawData.NumRows(); i += batchSize) { 52 | vector<Plaintext> row; 53 | vector<ZZ> curLabel; 54 | for (unsigned j = 0; j < rawData.NumCols(); j++) { 55 | vector<ZZ> columnBatch; 56 | for (unsigned k = i; ((k < i + batchSize) && (k < rawData.NumRows())); k++) { 57 | columnBatch.push_back(to_ZZ(rawData[k][j]) % p); 58 | if(j==0) curLabel.push_back(to_ZZ(labels[k]) % p); 59 | } 60 | row.push_back(Plaintext(context, columnBatch)); 61 | } 62 | ptxtData.push_back(row); 63 | ptxtLabels.push_back(Plaintext(context, curLabel)); 64 | } 65 | return (clock()-start)/CLOCKS_PER_SEC; 66 | } 67 | 68 | class Regression { 69 | public: 70 | Regression(const FHEcontext &context) : context(context), secretKey(context), publicKey(secretKey), keySwitch(secretKey), data(Ciphertext(publicKey)) { 71 | int k = context.Generator(); 72 | 73 | int nSlots = context.GetPlaintextSpace().GetUsableSlots(); 74 | while (nSlots > 1) { 75 | autoKeySwitch.push_back(KeySwitchSI(secretKey, k)); 76 | 77 | nSlots >>= 1; 78 | k *= k; 79 | k %= context.zMstar.M(); 80 | } 81 | } 82 | 83 | void AddData(const vector<vector<Plaintext>> &ptxtData, const vector<Plaintext> &ptxtLabels) { 84 | for (unsigned i = 0; i < ptxtData.size(); i++) { 85 | vector<Ciphertext> encExample(ptxtData[i].size(), Ciphertext(publicKey)); 86 | for (unsigned j = 0; j < ptxtData[i].size(); j++) { 87 | publicKey.Encrypt(encExample[j], ptxtData[i][j]); 88 | } 89 | Ciphertext encLabel(publicKey); 90 | publicKey.Encrypt(encLabel, ptxtLabels[i]); 91 | 92 | data.AddRow(encExample); 93 | labels.push_back(encLabel); 94 | } 95 | } 96 | 97 | void Clear() { 98 | data.Clear(); 99 | labels.clear(); 100 | } 101 | 102 | void Regress(vector<Ciphertext> &theta, Ciphertext &det) const { 103 | Matrix<Ciphertext> dataCopy = data; 104 | vector<Ciphertext> labels = this->labels; 105 | dataCopy.Transpose(); 106 | 107 | Matrix<Ciphertext> last = dataCopy*labels; 108 | dataCopy.MultByTranspose(); 109 | 110 | auto processFunc = [this](Ciphertext &ctxt) { 111 | this->keySwitch.ApplyKeySwitch(ctxt); 112 | this->SumBatchedData(ctxt); 113 | }; 114 | last.MapAll(processFunc); 115 | dataCopy.MapAll(processFunc); 116 | 117 | // Hack for when dimension is 1 (so we don't need to provide 118 | // an encryption of 1) 119 | if (data.NumCols() == 1) { 120 | det = dataCopy(0,0); 121 | theta.assign(1, last(0,0)); 122 | 123 | return; 124 | } 125 | 126 | dataCopy.Invert(det, [this](Ciphertext &ctxt) { 127 | this->keySwitch.ApplyKeySwitch(ctxt); 128 | }); 129 | 130 | dataCopy *= last; 131 | dataCopy.MapAll([this](Ciphertext &ctxt) { 132 | this->keySwitch.ApplyKeySwitch(ctxt); 133 | }); 134 | 135 | theta.resize(dataCopy.NumRows(), Ciphertext(publicKey)); 136 | for (unsigned i = 0; i < dataCopy.NumRows(); i++) { 137 | theta[i] = dataCopy(i,0); 138 | } 139 | 140 | // Add uniformly random values to all but the first slot 141 | Ciphertext noise(publicKey); 142 | for (unsigned i = 0; i < theta.size(); i++) { 143 | GenerateNoise(noise); 144 | theta[i] += noise; 145 | } 146 | 147 | GenerateNoise(noise); 148 | det += noise; 149 | } 150 | 151 | FHESIPubKey &GetPublicKey() { return publicKey; } 152 | FHESISecKey &GetSecretKey() { return secretKey; } 153 | 154 | vector<Ciphertext> labels; 155 | private: 156 | const FHEcontext &context; 157 | 158 | FHESISecKey secretKey; 159 | FHESIPubKey publicKey; 160 | 161 | KeySwitchSI keySwitch; 162 | vector<KeySwitchSI> autoKeySwitch; 163 | 164 | Matrix<Ciphertext> data; 165 | 166 | void SumBatchedData(Ciphertext &batchedData) const { 167 | int k = context.Generator(); 168 | 169 | for (unsigned i = 0; i < autoKeySwitch.size(); i++) { 170 | Ciphertext tmp = batchedData; 171 | tmp >>= k; 172 | autoKeySwitch[i].ApplyKeySwitch(tmp); 173 | batchedData += tmp; 174 | 175 | k *= k; 176 | k %= context.zMstar.M(); 177 | } 178 | } 179 | 180 | void GenerateNoise(Ciphertext &noiseCtxt) const { 181 | vector<ZZ_pX> randVec(context.GetPlaintextSpace().GetTotalSlots()); 182 | randVec[0] = to_ZZ_pX(0); 183 | for (unsigned i = 1; i < randVec.size(); i++) { 184 | randVec[i] = to_ZZ_pX(random_ZZ_p()); 185 | } 186 | 187 | Plaintext ptxt(context); 188 | ptxt.EmbedInSlots(randVec, false); 189 | publicKey.Encrypt(noiseCtxt, ptxt); 190 | } 191 | }; 192 | 193 | void RegressPT(vector<ZZ> &theta, ZZ &det, Matrix<ZZ> &data, 194 | vector<ZZ> &labels) { 195 | Matrix<ZZ> A = data; 196 | A.Transpose(); 197 | 198 | Matrix<ZZ> tmp = A*labels; 199 | 200 | A.MultByTranspose(); 201 | if (data.NumCols() == 1) { 202 | det = A(0,0); 203 | theta.assign(1, tmp(0,0)); 204 | return; 205 | } 206 | 207 | A.Invert(det); 208 | A *= tmp; 209 | 210 | theta.resize(A.NumRows()); 211 | for (unsigned i = 0; i < A.NumRows(); i++) { 212 | theta[i] = A(i,0); 213 | } 214 | } 215 | 216 | #endif 217 | -------------------------------------------------------------------------------- /Serialization.cpp: -------------------------------------------------------------------------------- 1 | #include "Serialization.h" 2 | 3 | void Export(ofstream &out, const ZZ &val) { 4 | uint32_t nBytes = NumBytes(val); 5 | out.write((char *) &nBytes, sizeof(uint32_t)); 6 | 7 | bool neg = (val < 0); 8 | out.write((char *) &neg, sizeof(bool)); 9 | 10 | unsigned char data[nBytes]; 11 | BytesFromZZ(data, val, nBytes); 12 | out.write((char *) data, nBytes); 13 | } 14 | 15 | void Import(ifstream &in, ZZ &val) { 16 | uint32_t nBytes; 17 | in.read((char *) &nBytes, sizeof(uint32_t)); 18 | 19 | bool neg; 20 | in.read((char *) &neg, sizeof(bool)); 21 | 22 | unsigned char data[nBytes]; 23 | in.read((char *) data, nBytes); 24 | ZZFromBytes(val, data, nBytes); 25 | 26 | if (neg) val *= -1; 27 | } 28 | 29 | void Export(ofstream &out, const ZZX &poly) { 30 | int32_t degree = deg(poly); 31 | 32 | out.write((char *) &degree, sizeof(int32_t)); 33 | for (int i = 0; i <= degree; i++) { 34 | Export(out, poly.rep[i]); 35 | } 36 | } 37 | 38 | void Import(ifstream &in, ZZX &poly) { 39 | poly = ZZX::zero(); 40 | 41 | int32_t degree; 42 | 43 | in.read((char *) &degree, sizeof(int32_t)); 44 | if (degree == -1) { 45 | return; 46 | } 47 | 48 | poly.SetMaxLength(degree + 1); 49 | for (int i = 0; i <= degree; i++) { 50 | ZZ coeff; 51 | Import(in, coeff); 52 | SetCoeff(poly, i, coeff); 53 | } 54 | } 55 | 56 | void Export(ofstream &out, const DoubleCRT &poly) { 57 | IndexMap<vec_long> map = poly.getMap(); 58 | 59 | uint32_t size = map.getIndexSet().card(); 60 | Export(out, size); 61 | for (long i = map.first(); i <= map.last(); i = map.next(i)) { 62 | Export(out, i); 63 | Export(out, map[i]); 64 | } 65 | } 66 | 67 | void Import(ifstream &in, DoubleCRT &poly) { 68 | IndexMap<vec_long> map; 69 | 70 | uint32_t size; 71 | Import(in, size); 72 | 73 | for (unsigned i = 0; i < size; i++) { 74 | long key; 75 | Import(in, key); 76 | map.insert(key); 77 | Import(in, map[key]); 78 | } 79 | 80 | poly.setMap(map); 81 | } 82 | 83 | void Export(ofstream &out, const vec_long &vec) { 84 | uint32_t len = vec.length(); 85 | Export(out, len); 86 | for (int i = 0; i < vec.length(); i++) { 87 | Export(out, vec[i]); 88 | } 89 | } 90 | 91 | void Import(ifstream &in, vec_long &vec) { 92 | uint32_t size; 93 | Import(in, size); 94 | vec.SetLength(size); 95 | 96 | for (uint32_t i = 0; i < size; i++) { 97 | Import(in, vec[i]); 98 | } 99 | } 100 | 101 | void Export(ofstream &out, const CiphertextPart &part) { 102 | Export(out, part.poly); 103 | } 104 | 105 | void Import(ifstream &in, CiphertextPart &part) { 106 | Import(in, part.poly); 107 | } 108 | 109 | void Export(ofstream &out, const Ciphertext &ctxt) { 110 | Ciphertext copy = ctxt; 111 | 112 | copy.ScaleDown(); 113 | Export(out, copy.parts); 114 | } 115 | 116 | void Import(ifstream &in, Ciphertext &ctxt) { 117 | ctxt.Clear(); 118 | Import(in, ctxt.parts); 119 | } 120 | -------------------------------------------------------------------------------- /Serialization.h: -------------------------------------------------------------------------------- 1 | #ifndef _SERIALIZATION_H_ 2 | #define _SERIALIZATION_H_ 3 | 4 | #include <NTL/ZZ.h> 5 | #include <fstream> 6 | 7 | #include "DoubleCRT.h" 8 | #include "Ciphertext.h" 9 | #include "Matrix.h" 10 | 11 | void Export(ofstream &out, const ZZ &val); 12 | void Export(ofstream &out, const ZZX &val); 13 | void Export(ofstream &out, const DoubleCRT &val); 14 | void Export(ofstream &out, const vec_long &vec); 15 | 16 | void Import(ifstream &in, ZZ &val); 17 | void Import(ifstream &in, ZZX &val); 18 | void Import(ifstream &in, DoubleCRT &val); 19 | void Import(ifstream &in, vec_long &vec); 20 | 21 | void Export(ofstream &out, const CiphertextPart &part); 22 | void Export(ofstream &out, const Ciphertext &ctxt); 23 | 24 | void Import(ifstream &in, CiphertextPart &part); 25 | void Import(ifstream &in, Ciphertext &ctxt); 26 | 27 | template<typename T> 28 | void Export(ofstream &out, const T &val) { 29 | out.write((char *) &val, sizeof(T)); 30 | } 31 | 32 | template<typename T> 33 | void Import(ifstream &in, T &val) { 34 | in.read((char *) &val, sizeof(T)); 35 | } 36 | 37 | template<typename T> 38 | void Export(ofstream &out, const vector<T> &vec) { 39 | uint32_t size = vec.size(); 40 | Export(out, size); 41 | for (unsigned i = 0; i < vec.size(); i++) { 42 | Export(out, vec[i]); 43 | } 44 | } 45 | 46 | template<typename T> 47 | void Import(ifstream &in, vector<T> &vec) { 48 | uint32_t size; 49 | Import(in, size); 50 | 51 | vec.resize(size); 52 | for (unsigned i = 0; i < size; i++) { 53 | Import(in, vec[i]); 54 | } 55 | } 56 | 57 | template<typename T> 58 | void Export(ofstream &out, const Matrix<T> &mat) { 59 | Export(out, mat.NumRows()); 60 | Export(out, mat.NumCols()); 61 | 62 | for (unsigned i = 0; i < mat.NumRows(); i++) { 63 | for (unsigned j = 0; j < mat.NumCols(); j++) { 64 | Export(out, mat(i,j)); 65 | } 66 | } 67 | } 68 | 69 | template<typename T> 70 | void Import(ifstream &in, Matrix<T> &mat) { 71 | uint32_t nRows, nCols; 72 | Import(in, nRows); 73 | Import(in, nCols); 74 | 75 | mat.Resize(nRows, nCols); 76 | for (unsigned i = 0; i < nRows; i++) { 77 | for (unsigned j = 0; j < nCols; j++) { 78 | Import(in, mat(i,j)); 79 | } 80 | } 81 | } 82 | 83 | #endif -------------------------------------------------------------------------------- /SingleCRT.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012,2013 IBM Corp. 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | * See the GNU General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License along 13 | * with this program; if not, write to the Free Software Foundation, Inc., 14 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 15 | */ 16 | 17 | /* 18 | * 19 | * SingleCRT.cpp - This class hold integer polynomials modulo many small primes 20 | * 21 | * SingleCRT form is a vector of (ZZX) polynomials, where the i'th polynomial 22 | * contains the coefficients wrt the ith prime in the chain of moduli. The 23 | * polynomial thus represented is defined modulo the product of all the 24 | * primes in use. 25 | * 26 | * This is mostly a helper class for DoubleCRT, and all the rules that apply 27 | * to DoubleCRT wrt modulus chain and levels apply also to SingleCRT objects. 28 | * Although SingleCRT and DoubleCRT objects can interact in principle, 29 | * translation back and forth are expensive since they involve FFT/iFFT. 30 | * Hence support for interaction between them is limited to explicit 31 | * conversions. 32 | */ 33 | #include <NTL/ZZ.h> 34 | 35 | NTL_CLIENT 36 | 37 | #include "NumbTh.h" 38 | #include "SingleCRT.h" 39 | #include "DoubleCRT.h" 40 | 41 | // a "sanity check" function, verifies consistency of polys with current 42 | // moduli chain an error is raised if they are not consistent 43 | void SingleCRT::verify() 44 | { 45 | const IndexSet& s = map.getIndexSet(); 46 | if (s.last() >= context.numPrimes()) 47 | Error("SingleCRT object has too many rows"); 48 | 49 | // check that the content of i'th row is in [0,pi) for all i 50 | for (long i = s.first(); i < s.last(); i = s.next(i)) { 51 | long pi = context.ithPrime(i); // the i'th modulus 52 | vec_ZZ& vp = map[i].rep; 53 | for (long j=0; j<vp.length(); j++) 54 | if (vp[j]<0 || vp[j]>= pi) 55 | Error("SingleCRT object has inconsistent data"); 56 | } 57 | } 58 | 59 | // Generic operators, Fnc is either AddMod or SubMod 60 | // This should have been a template, but gcc refuses to cooperate 61 | SingleCRT& SingleCRT::Op(const SingleCRT &other, 62 | void (*Fnc)(ZZ&, const ZZ&, const ZZ&, const ZZ&), 63 | bool matchIndexSets) 64 | { 65 | if (&context != &other.context) 66 | Error("SingleCRT::Op: incomopatible objects"); 67 | 68 | // Match the index sets, if needed 69 | if (matchIndexSets && !(map.getIndexSet() >= other.map.getIndexSet())) 70 | addPrimes(other.map.getIndexSet() / map.getIndexSet()); // This is expensive 71 | 72 | // INVARIANT: map.getIndexSet() >= other.map.getIndexSet()) 73 | 74 | SingleCRT tmp(context); // a rare case where you want an empty SingleCRT object 75 | const IndexMap<ZZX>* other_map = &other.map; 76 | if (map.getIndexSet() > other.map.getIndexSet()) { // Even more expensive 77 | tmp = other; 78 | tmp.addPrimes(map.getIndexSet() / other.map.getIndexSet()); 79 | other_map = &tmp.map; 80 | } 81 | 82 | const IndexSet& s = map.getIndexSet(); 83 | 84 | // add/sub polynomial, modulo the respective primes 85 | 86 | for (long i = s.first(); i <= s.last(); i = s.next(i)) { 87 | ZZ pi = to_ZZ(context.ithPrime(i)); 88 | vec_ZZ& vp1 = map[i].rep; 89 | const vec_ZZ& vp2 = (*other_map)[i].rep; 90 | 91 | long len1 = vp1.length(); 92 | long len2 = vp2.length(); 93 | long maxlen = max(len1, len2); 94 | vp1.SetLength(maxlen); 95 | for (long j=len1; j < maxlen; j++) clear(vp1[j]); 96 | for (long j=0; j<len2; j++) 97 | Fnc(vp1[j], vp1[j], vp2[j], pi); 98 | map[i].normalize(); 99 | } 100 | return *this; 101 | } 102 | 103 | // Implementation of scrt += poly, scrt -= poly, or scrt *= poly. This 104 | // implementation is safe for "in place" operation, e.g., s += s.map[i] 105 | SingleCRT& SingleCRT::Op(const ZZX &poly, 106 | void (*Fnc)(ZZ&, const ZZ&, const ZZ&, const ZZ&)) 107 | { 108 | const IndexSet& s = map.getIndexSet(); 109 | 110 | ZZX poly1, poly2; 111 | 112 | poly1 = poly; 113 | 114 | for (long i = s.first(); i <= s.last(); i = s.next(i)) { 115 | ZZ pi = to_ZZ(context.ithPrime(i)); 116 | poly2 = poly1; 117 | PolyRed(poly2,pi,/*abs=*/true); // abs=true means reduce to [0,pi-1) 118 | 119 | vec_ZZ& vp1 = map[i].rep; 120 | vec_ZZ& vp2 = poly2.rep; 121 | 122 | long len1 = vp1.length(); 123 | long len2 = vp2.length(); 124 | long maxlen = max(len1, len2); 125 | vp1.SetLength(maxlen); 126 | for (long j=len1; j < maxlen; j++) clear(vp1[j]); 127 | for (long j=0; j<len2; j++) 128 | Fnc(vp1[j], vp1[j], vp2[j], pi); 129 | 130 | map[i].normalize(); 131 | } 132 | return *this; 133 | } 134 | 135 | // Here Fnc is either Add(ZZX,ZZX,ZZ), Sub(ZZX,ZZX,ZZ), or Mul(ZZX,ZZX,ZZ) 136 | // FIXME: this is not alias friendly 137 | SingleCRT& SingleCRT::Op(const ZZ &num, void (*Fnc)(ZZX&, const ZZX&, const ZZ&)) 138 | { 139 | const IndexSet& s = map.getIndexSet(); 140 | ZZ pi; 141 | ZZ n; 142 | ZZX poly1; 143 | 144 | for (long i = s.first(); i <= s.last(); i = s.next(i)) { 145 | conv(pi, context.ithPrime(i)); 146 | rem(n, num, pi); // n = num % pi 147 | poly1 = map[i]; 148 | Fnc(poly1,poly1,n); 149 | PolyRed(poly1,pi,/*abs=*/true); // abs=true means reduce to [0,pi-1) 150 | map[i] = poly1; 151 | } 152 | return *this; 153 | } 154 | 155 | // Constructors 156 | 157 | SingleCRT::SingleCRT(const ZZX&poly, const FHEcontext& _context, const IndexSet& s) 158 | : context(_context) 159 | { 160 | assert(s.last() < context.numPrimes()); 161 | 162 | map.insert(s); 163 | *this = poly; // convert polynomial to singleCRT representation 164 | } 165 | 166 | SingleCRT::SingleCRT(const ZZX&poly, const FHEcontext& _context) 167 | : context(_context) 168 | { 169 | IndexSet s = IndexSet(0, context.numPrimes()-1); 170 | // FIXME: maybe the default index set should be determined by context? 171 | 172 | map.insert(s); 173 | *this = poly; // convert polynomial to singleCRT representation 174 | } 175 | 176 | // Uses the "active context", run-time error if it is NULL 177 | SingleCRT::SingleCRT(const ZZX&poly) 178 | : context(*activeContext) 179 | { 180 | IndexSet s = IndexSet(0, context.numPrimes()-1); 181 | // FIXME: maybe the default index set should be determined by context? 182 | 183 | map.insert(s); 184 | *this = poly; // convert polynomial to singleCRT representation 185 | } 186 | 187 | 188 | 189 | // Without specifying a ZZX, we get the zero polynomial 190 | SingleCRT::SingleCRT(const FHEcontext &_context, const IndexSet& s) 191 | : context(_context) 192 | { 193 | assert(s.last() < context.numPrimes()); 194 | 195 | map.insert(s); 196 | // default constructor for ZZX creates the zero polynomial 197 | } 198 | 199 | SingleCRT::SingleCRT(const FHEcontext &_context) 200 | : context(_context) 201 | { 202 | IndexSet s = IndexSet(0, context.numPrimes()-1); 203 | // FIXME: maybe the default index set should be determined by context? 204 | 205 | map.insert(s); 206 | // default constructor for ZZX creates the zero polynomial 207 | } 208 | 209 | 210 | // Uses the "active context", run-time error if it is NULL 211 | SingleCRT::SingleCRT(): context(*activeContext) 212 | { 213 | IndexSet s = IndexSet(0, context.numPrimes()-1); 214 | // FIXME: maybe the default index set should be determined by context? 215 | 216 | map.insert(s); 217 | // default constructor for ZZX creates the zero polynomial 218 | } 219 | 220 | 221 | // Assignment operators 222 | 223 | SingleCRT& SingleCRT::operator=(const SingleCRT& other) 224 | { 225 | if (&context != &other.context) 226 | Error("SingleCRT assignment: context mismatch"); 227 | 228 | map = other.map; 229 | return *this; 230 | } 231 | 232 | 233 | SingleCRT& SingleCRT::operator=(const DoubleCRT& other) 234 | { 235 | other.toSingleCRT(*this); 236 | return *this; 237 | } 238 | 239 | SingleCRT& SingleCRT::operator=(const ZZX& poly) 240 | { 241 | const IndexSet& s = map.getIndexSet(); 242 | ZZX poly1; 243 | 244 | for (long i = s.first(); i <= s.last(); i = s.next(i)) { 245 | ZZ pi = to_ZZ(context.ithPrime(i)); 246 | poly1 = poly; 247 | PolyRed(poly1,pi,true); // the flag true means reduce to [0,pi-1) 248 | map[i] = poly1; 249 | } 250 | return *this; 251 | } 252 | 253 | // explicitly changing the number of levels, returns the previous number 254 | 255 | void SingleCRT::addPrimes(const IndexSet& s1) 256 | { 257 | assert(card(s1 & map.getIndexSet()) == 0); 258 | 259 | ZZX poly, poly1; 260 | toPoly(poly); // recover in coefficient representation 261 | 262 | map.insert(s1); // add new rows to the map 263 | 264 | // fill in new rows 265 | for (long i = s1.first(); i <= s1.last(); i = s1.next(i)) { 266 | ZZ pi = to_ZZ(context.ithPrime(i)); 267 | 268 | poly1 = poly; 269 | PolyRed(poly1,pi,true); // the flag true means reduce to [0,pi-1) 270 | map[i] = poly; 271 | } 272 | } 273 | 274 | 275 | // Division by constant 276 | // FIXME: this is not alias friendly 277 | SingleCRT& SingleCRT::operator/=(const ZZ &num) 278 | { 279 | const IndexSet& s = map.getIndexSet(); 280 | ZZ pi, n; 281 | 282 | for (long i = s.first(); i <= s.last(); i = s.next(i)) { 283 | pi = to_ZZ(context.ithPrime(i)); 284 | rem(n,num,pi); 285 | InvMod(n,n,pi); // n = num^{-1} mod pi 286 | 287 | vec_ZZ& vp = map[i].rep; 288 | for (long j=0; j<vp.length(); j++) MulMod(vp[j], vp[j], n, pi); 289 | map[i].normalize(); 290 | } 291 | return *this; 292 | } 293 | 294 | // Recovering the polynomial in coefficient representation. This yields an 295 | // integer polynomial with coefficients in [-P/2,P/2] (P is the product of 296 | // all moduli used). Using the optional fromLvl, numLvls params we compute the 297 | // polynomial reduced modulo the product of only the ptimes in these levels. 298 | 299 | void SingleCRT::toPoly(ZZX& poly, const IndexSet& s) const 300 | { 301 | IndexSet s1 = map.getIndexSet() & s; 302 | 303 | if (card(s1) == 0) { 304 | clear(poly); 305 | return; 306 | } 307 | 308 | ZZ p = to_ZZ(context.ithPrime(s1.first())); // the first modulus 309 | 310 | poly = map[s1.first()]; // Get poly modulo the first prime 311 | 312 | vec_ZZ& vp = poly.rep; 313 | 314 | // ensure that coeficient vector is of size phi(m) with entries in [-p/2,p/2] 315 | long phim = context.zMstar.phiM(); 316 | long vpLength = vp.length(); 317 | if (vpLength<phim) { // just in case of leading zeros in poly 318 | vp.SetLength(phim); 319 | for (long j=vpLength; j<phim; j++) vp[j]=0; 320 | } 321 | ZZ p_over_2 = p/2; 322 | for (long j=0; j<phim; j++) if (vp[j] > p_over_2) vp[j] -= p; 323 | 324 | // do incremental integer CRT for other levels 325 | for (long i = s1.next(s1.first()); i <= s1.last(); i = s1.next(i)) { 326 | long q = context.ithPrime(i); // the next modulus 327 | 328 | // CRT the coefficient vectors of poly and current 329 | intVecCRT(vp, p, map[i].rep, q); // defined in the module NumbTh 330 | p *= q; // update the modulus 331 | } 332 | poly.normalize(); // need to call this after we work on the coeffs 333 | } 334 | 335 | 336 | void SingleCRT::toPoly(ZZX& p) const { 337 | const IndexSet& s = map.getIndexSet(); 338 | toPoly(p, s); 339 | } 340 | 341 | void SingleCRT::SetIndex(long index, const ZZX &poly) { 342 | map[index] = poly; 343 | } 344 | 345 | -------------------------------------------------------------------------------- /SingleCRT.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012,2013 IBM Corp. 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | * See the GNU General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License along 13 | * with this program; if not, write to the Free Software Foundation, Inc., 14 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 15 | */ 16 | 17 | #ifndef _SingleCRT_H_ 18 | #define _SingleCRT_H_ 19 | /* SingleCRT.h - This class hold integer polynomials modulo many small primes 20 | * 21 | * SingleCRT form is a map from an index set to (ZZX) polynomials, where the 22 | * i'th polynomial contains the coefficients wrt the ith prime in the chain of 23 | * moduli. The polynomial thus represented is defined modulo the product of 24 | * all the primes in use. 25 | * 26 | * This is mostly a helper class for DoubleCRT, and all the rules that apply 27 | * to DoubleCRT wrt moduli sets apply also to SingleCRT objects. Although 28 | * SingleCRT and DoubleCRT objects can interact in principle, translation back 29 | * and forth are expensive since they involve FFT/iFFT. Hence support for 30 | * interaction between them is limited to explicit conversions. 31 | */ 32 | #include <vector> 33 | #include <iostream> 34 | #include <NTL/ZZX.h> 35 | 36 | #include "FHEContext.h" 37 | #include "IndexMap.h" 38 | 39 | class DoubleCRT; 40 | 41 | class SingleCRT { 42 | const FHEcontext& context; 43 | IndexMap<ZZX> map; // the SingleCRT data 44 | 45 | friend class DoubleCRT; 46 | 47 | // a "sanity check" function, verifies consistency of polys with current 48 | // moduli chain an error is raised if they are not consistent 49 | void verify(); 50 | 51 | // Generic operators, Fnc is either AddMod, SubMod, or MulMod. (This should 52 | // have been a template, but gcc refuses to cooperate.) 53 | 54 | // The behavior when the *this and other have different index sets depends 55 | // on the matchIndexSets flag. When it is set to true then the union of the 56 | // two index sets is used; if false, then the index set of *this is used. 57 | SingleCRT& Op(const SingleCRT &other, 58 | void (*Fnc)(ZZ&, const ZZ&, const ZZ&, const ZZ&), 59 | bool matchIndexSets=true); 60 | 61 | SingleCRT& Op(const ZZX &poly, 62 | void (*Fnc)(ZZ&, const ZZ&, const ZZ&, const ZZ&)); 63 | SingleCRT& Op(const ZZ &num, void (*Fnc)(ZZX&, const ZZX&, const ZZ&)); 64 | 65 | 66 | public: 67 | 68 | 69 | // Constructors and assignment operators 70 | 71 | // representing an integer polynomial as SingleCRT. If the index set 72 | // is not specified, then all the primes from the context are used. 73 | // If the coefficients of poly are larger than the product of 74 | // the used moduli, they are effectively reduced modulo that product 75 | 76 | SingleCRT(const ZZX& poly, const FHEcontext& _context, const IndexSet& s); 77 | SingleCRT(const ZZX& poly, const FHEcontext& _context); 78 | SingleCRT(const ZZX& poly); // uses active context 79 | 80 | 81 | // Without specifying a ZZX, we get the zero polynomial 82 | 83 | SingleCRT(const FHEcontext& _context, const IndexSet& s); 84 | SingleCRT(const FHEcontext& _context); 85 | 86 | SingleCRT(); // uses the "active context", run-time error if it is NULL 87 | 88 | // Assignment operators 89 | 90 | SingleCRT& operator=(const SingleCRT& other); 91 | SingleCRT& operator=(const DoubleCRT& dcrt); 92 | SingleCRT& operator=(const ZZX& poly); 93 | SingleCRT& operator=(const ZZ& num) { *this = to_ZZX(num); return *this; } 94 | SingleCRT& operator=(const long num) { *this = to_ZZX(num); return *this; } 95 | 96 | bool operator==(const SingleCRT& other) const { 97 | return &context == &other.context && map == other.map; 98 | } 99 | 100 | bool operator!=(const SingleCRT& other) const { 101 | return !(*this==other); 102 | } 103 | 104 | // Set to zero, one 105 | 106 | SingleCRT& setZero() { *this = ZZX::zero(); return *this; } 107 | SingleCRT& setOne() { *this = 1; return *this; } 108 | 109 | 110 | // expand the index set by s1. 111 | // it is assumed that s1 is disjoint from the current index set. 112 | void addPrimes(const IndexSet& s1); 113 | 114 | 115 | // remove s1 from the index set 116 | void removePrimes(const IndexSet& s1) { 117 | map.remove(s1); 118 | } 119 | 120 | // Arithmetic operations. Only the "destructive" versions are used, 121 | // i.e., a += b is implemented but not a + b. 122 | 123 | // Addition, negation, subtraction 124 | SingleCRT& operator+=(const SingleCRT &other){ return Op(other,NTL::AddMod);} 125 | SingleCRT& operator+=(const ZZX &poly) { return Op(poly, NTL::AddMod);} 126 | SingleCRT& operator+=(const ZZ &num) { return Op(num, NTL::add); } 127 | SingleCRT& operator+=(long num) { return Op(to_ZZ(num), NTL::add); } 128 | 129 | SingleCRT& operator-=(const SingleCRT &other){ return Op(other,NTL::SubMod);} 130 | SingleCRT& operator-=(const ZZX &poly) { return Op(poly, NTL::SubMod); } 131 | SingleCRT& operator-=(const ZZ &num) { return Op(num, NTL::sub); } 132 | SingleCRT& operator-=(long num) { return Op(to_ZZ(num), NTL::sub); } 133 | 134 | // Procedural equivalents, supporting also the matchIndexSets flag 135 | void Add(const SingleCRT &other, bool matchIndexSet=true) 136 | { Op(other, NTL::AddMod, matchIndexSet); } 137 | void Sub(const SingleCRT &other, bool matchIndexSet=true) 138 | { Op(other, NTL::SubMod, matchIndexSet); } 139 | 140 | // These are the prefix versions, ++dcrt and --dcrt. 141 | SingleCRT& operator++() { return (*this += 1); }; 142 | SingleCRT& operator--() { return (*this -= 1); }; 143 | 144 | // Postfix version...no return value...just for style 145 | void operator++(int) { *this += 1; }; 146 | void operator--(int) { *this -= 1; }; 147 | 148 | // Multiplication by constant 149 | SingleCRT& operator*=(const ZZ &num) { return Op(num,NTL::mul); } 150 | SingleCRT& operator*=(long num) { return Op(to_ZZ(num),NTL::mul); } 151 | 152 | // Division by constant 153 | SingleCRT& operator/=(const ZZ &num); 154 | SingleCRT& operator/=(long num) { return (*this /= to_ZZ(num)); } 155 | 156 | // Recovering the polynomial in coefficient representation. This yields an 157 | // integer polynomial with coefficients in [-P/2,P/2] (P is the product of 158 | // all moduli used). Using the given index set, the 159 | // polynomial is reduced modulo the product of only the indexed primes. 160 | 161 | void toPoly(ZZX& p, const IndexSet& s) const; 162 | void toPoly(ZZX& p) const; 163 | 164 | friend ostream& operator<<(ostream &os, const SingleCRT &scrt) { 165 | ZZX p; 166 | scrt.toPoly(p); 167 | return (os << p); 168 | } 169 | 170 | void SetIndex(long index, const ZZX &poly); 171 | 172 | // Access methods 173 | const IndexMap<ZZX>& getMap() const { return map; } 174 | const FHEcontext& getContext() const { return context; } 175 | }; 176 | inline void conv(SingleCRT &s, const ZZX &p) { s=p; } 177 | // Cannot implement to_SingleCRT(p), since modChain is not defined 178 | 179 | inline void conv(ZZX &p, const SingleCRT &s) { s.toPoly(p); } 180 | inline ZZX to_ZZX(const SingleCRT &s) { ZZX p; s.toPoly(p); return p; } 181 | 182 | inline void conv(SingleCRT &s, const DoubleCRT &d) { s=d; } 183 | #endif // #ifndef _SingleCRT_H_ 184 | -------------------------------------------------------------------------------- /Statistics.h: -------------------------------------------------------------------------------- 1 | #ifndef _STATISTICS_H_ 2 | #define _STATISTICS_H_ 3 | 4 | #include "FHEContext.h" 5 | #include <vector> 6 | 7 | #include "FHE-SI.h" 8 | #include "Ciphertext.h" 9 | #include "Matrix.h" 10 | #include "Matrix.cpp" 11 | 12 | class Statistics { 13 | public: 14 | Statistics(const FHEcontext &context) : context(context), secretKey(context), 15 | publicKey(secretKey), keySwitch(secretKey), 16 | data(Ciphertext(publicKey)) { 17 | int k = context.Generator(); 18 | 19 | int nSlots = context.GetPlaintextSpace().GetUsableSlots(); 20 | while (nSlots > 1) { 21 | autoKeySwitch.push_back(KeySwitchSI(secretKey, k)); 22 | 23 | nSlots >>= 1; 24 | k *= k; 25 | k %= context.zMstar.M(); 26 | } 27 | } 28 | 29 | void AddData(const Matrix<Plaintext> &blocks, const vector<Plaintext> &blockSizes) { 30 | for (unsigned i = 0; i < blocks.NumRows(); i++) { 31 | vector<Ciphertext> encExample(blocks[i].size(), Ciphertext(publicKey)); 32 | for (unsigned j = 0; j < blocks[i].size(); j++) { 33 | publicKey.Encrypt(encExample[j], blocks[i][j]); 34 | } 35 | Ciphertext encN(publicKey); 36 | publicKey.Encrypt(encN, blockSizes[i]); 37 | 38 | data.AddRow(encExample); 39 | nElems.push_back(encN); 40 | } 41 | } 42 | 43 | void Clear() { 44 | data.Clear(); 45 | nElems.clear(); 46 | } 47 | 48 | void ComputeNthMoment(vector<Ciphertext> &moment, Ciphertext &denom, unsigned n) { 49 | if (n < 1 || n > 2) { // Not supported currently 50 | return; 51 | } 52 | 53 | moment.resize(data.NumCols(), Ciphertext(publicKey)); 54 | denom = nElems[0]; 55 | for (unsigned j = 0; j < data.NumCols(); j++) { 56 | moment[j] = data(0, j); 57 | if (n == 2) { 58 | moment[j] *= moment[j]; 59 | } 60 | 61 | for (unsigned i = 1; i < data.NumRows(); i++) { 62 | if (j == 0) { 63 | denom += nElems[i]; 64 | } 65 | 66 | Ciphertext tmp = data(i, j); 67 | if (n == 2) { 68 | tmp *= tmp; 69 | } 70 | moment[j] += tmp; 71 | } 72 | 73 | if (n == 2) { 74 | keySwitch.ApplyKeySwitch(moment[j]); 75 | } 76 | SumBatchedData(moment[j]); 77 | } 78 | 79 | Ciphertext noise(publicKey); 80 | for (unsigned i = 0; i < moment.size(); i++) { 81 | GenerateNoise(noise); 82 | moment[i] += noise; 83 | } 84 | } 85 | 86 | void ComputeCovariance(Matrix<Ciphertext> &cov, vector<Ciphertext> &mu, Ciphertext &n, Ciphertext &n2) { 87 | ComputeNthMoment(mu, n, 1); 88 | 89 | Ciphertext dummy(publicKey); 90 | Matrix<Ciphertext> muMat(dummy); 91 | muMat.AddRow(mu); 92 | muMat.Transpose(); 93 | muMat.MultByTranspose(); 94 | 95 | // Symmetric matrix so just process half the entries 96 | for (unsigned i = 0; i < muMat.NumRows(); i++) { 97 | for (unsigned j = i; j < muMat.NumCols(); j++) { 98 | keySwitch.ApplyKeySwitch(muMat(i,j)); 99 | muMat(i,j) *= -1; 100 | } 101 | } 102 | 103 | cov = data; 104 | cov.Transpose(); 105 | cov.MultByTranspose(); 106 | 107 | Ciphertext noise(publicKey); 108 | // Again, symmetric matrix so we just process half the entries 109 | for (unsigned i = 0; i < cov.NumRows(); i++) { 110 | for (unsigned j = i; j < cov.NumCols(); j++) { 111 | keySwitch.ApplyKeySwitch(cov(i,j)); 112 | SumBatchedData(cov(i,j)); 113 | cov(i,j) *= n; 114 | keySwitch.ApplyKeySwitch(cov(i,j)); 115 | cov(i,j) += muMat(i,j); 116 | 117 | GenerateNoise(noise); 118 | cov(i,j) += noise; 119 | 120 | cov(j,i) = cov(i,j); 121 | } 122 | } 123 | 124 | n2 = n; 125 | n2 *= n2; 126 | 127 | keySwitch.ApplyKeySwitch(n2); 128 | } 129 | 130 | // For debugging purposes 131 | FHESISecKey &GetSecretKey() { return secretKey; } 132 | FHESIPubKey &GetPublicKey() { return publicKey; } 133 | 134 | private: 135 | const FHEcontext &context; 136 | 137 | FHESISecKey secretKey; 138 | FHESIPubKey publicKey; 139 | 140 | KeySwitchSI keySwitch; 141 | vector<KeySwitchSI> autoKeySwitch; 142 | 143 | Matrix<Ciphertext> data; 144 | vector<Ciphertext> nElems; 145 | 146 | void SumBatchedData(Ciphertext &batchedData) const { 147 | int k = context.Generator(); 148 | 149 | for (unsigned i = 0; i < autoKeySwitch.size(); i++) { 150 | Ciphertext tmp = batchedData; 151 | tmp >>= k; 152 | autoKeySwitch[i].ApplyKeySwitch(tmp); 153 | batchedData += tmp; 154 | 155 | k *= k; 156 | k %= context.zMstar.M(); 157 | } 158 | } 159 | 160 | void GenerateNoise(Ciphertext &noiseCtxt) const { 161 | vector<ZZ_pX> randVec(context.GetPlaintextSpace().GetTotalSlots()); 162 | randVec[0] = to_ZZ_pX(0); 163 | for (unsigned i = 1; i < randVec.size(); i++) { 164 | randVec[i] = to_ZZ_pX(random_ZZ_p()); 165 | } 166 | 167 | Plaintext ptxt(context); 168 | ptxt.EmbedInSlots(randVec, false); 169 | publicKey.Encrypt(noiseCtxt, ptxt); 170 | } 171 | }; 172 | 173 | void ComputeNthMomentPT(vector<ZZ> &moment, unsigned n, const Matrix<ZZ> &data) { 174 | moment.resize(data.NumCols()); 175 | 176 | for (unsigned j = 0; j < data.NumCols(); j++) { 177 | moment[j] = 0; 178 | ZZ val; 179 | for (unsigned i = 0; i < data.NumRows(); i++) { 180 | power(val, data(i,j), n); 181 | moment[j] += val; 182 | } 183 | } 184 | } 185 | 186 | void ComputeMomentsPT(vector<ZZ> &sum, vector<ZZ> &sqSum, const Matrix<ZZ> &data) { 187 | ComputeNthMomentPT(sum, 1, data); 188 | ComputeNthMomentPT(sqSum, 2, data); 189 | } 190 | 191 | void ComputeCovariancePT(Matrix<ZZ> &cov, const Matrix<ZZ> &data) { 192 | cov = data; 193 | cov.Transpose(); 194 | cov.MultByTranspose(); 195 | 196 | ZZ n = to_ZZ(data.NumRows()); 197 | cov *= n; 198 | 199 | vector<ZZ> muVec; 200 | ComputeNthMomentPT(muVec, 1, data); 201 | 202 | Matrix<ZZ> mu; 203 | mu.AddRow(muVec); 204 | mu.Transpose(); 205 | mu.MultByTranspose(); 206 | 207 | cov -= mu; 208 | } 209 | 210 | #endif 211 | -------------------------------------------------------------------------------- /Test_AddMul.cpp: -------------------------------------------------------------------------------- 1 | #define N_TESTS 5000 2 | 3 | #include "Plaintext.h" 4 | #include "DoubleCRT.h" 5 | #include "Ciphertext.h" 6 | #include "NTL/ZZ_pX.h" 7 | 8 | #include <time.h> 9 | #include "FHE-SI.h" 10 | 11 | bool runTest(bool disp, long long seed, unsigned p, FHEcontext &context) { 12 | ZZ seedZZ; 13 | seedZZ = seed; 14 | 15 | srand48(seed); 16 | SetSeed(seedZZ); 17 | 18 | FHESISecKey secretKey(context); 19 | const FHESIPubKey &publicKey(secretKey); 20 | 21 | long phim = context.zMstar.phiM(); 22 | 23 | ZZ_pX ptxt1Poly, ptxt2Poly, sum, sumMult, prod, prod2, sumQuad; 24 | Plaintext resSum, resSumMult, resProd, resProdSwitch, resProd2, resSumQuad; 25 | 26 | ptxt1Poly.rep.SetLength(phim); 27 | ptxt2Poly.rep.SetLength(phim); 28 | for (long i=0; i < phim; i++) { 29 | ptxt1Poly.rep[i] = RandomBnd(p); 30 | ptxt2Poly.rep[i] = RandomBnd(p); 31 | } 32 | 33 | ptxt1Poly.normalize(); 34 | ptxt2Poly.normalize(); 35 | 36 | sum = ptxt1Poly + ptxt2Poly; 37 | sumMult = ptxt2Poly * 7; 38 | prod = ptxt1Poly * ptxt2Poly; 39 | prod2 = prod * prod; 40 | sumQuad = prod2 * prod2 * 9; 41 | 42 | rem(prod, prod, to_ZZ_pX(context.zMstar.PhimX())); 43 | rem(prod2, prod2, to_ZZ_pX(context.zMstar.PhimX())); 44 | rem(sumQuad, sumQuad, to_ZZ_pX(context.zMstar.PhimX())); 45 | 46 | Plaintext ptxt1(context, ptxt1Poly), ptxt2(context, ptxt2Poly); 47 | Ciphertext ctxt1(publicKey), ctxt2(publicKey); 48 | publicKey.Encrypt(ctxt1, ptxt1); 49 | publicKey.Encrypt(ctxt2, ptxt2); 50 | 51 | Ciphertext cSum = ctxt1; 52 | cSum += ctxt2; 53 | 54 | Ciphertext cSumMult = ctxt2; 55 | for (int i = 1; i < 7; i++) { 56 | cSumMult += ctxt2; 57 | } 58 | 59 | Ciphertext cProd = ctxt1; 60 | cProd *= ctxt2; 61 | 62 | secretKey.Decrypt(resSum, cSum); 63 | secretKey.Decrypt(resSumMult, cSumMult); 64 | 65 | KeySwitchSI keySwitch(secretKey); 66 | keySwitch.ApplyKeySwitch(cProd); 67 | secretKey.Decrypt(resProd, cProd); 68 | 69 | cProd *= cProd; 70 | Ciphertext tmp = cProd; 71 | Ciphertext cSumQuad = cProd; 72 | 73 | keySwitch.ApplyKeySwitch(cProd); 74 | secretKey.Decrypt(resProd2, cProd); 75 | 76 | for (int i = 0; i < 8; i++) { 77 | cSumQuad += tmp; 78 | } 79 | keySwitch.ApplyKeySwitch(cSumQuad); 80 | cSumQuad *= cProd; 81 | keySwitch.ApplyKeySwitch(cSumQuad); 82 | secretKey.Decrypt(resSumQuad, cSumQuad); 83 | 84 | bool success = ((resSum.message == sum) && (resSumMult.message == sumMult) && 85 | (resProd.message == prod) && (resProd2.message == prod2) && 86 | (resSumQuad == sumQuad)); 87 | 88 | if (disp || !success) { 89 | cout << "Seed: " << seed << endl << endl; 90 | 91 | if (resSum.message != sum) { 92 | cout << "Add failed." << endl; 93 | } 94 | if (resSumMult.message != sumMult) { 95 | cout << "Adding multiple times failed." << endl; 96 | } 97 | if (resProd.message != prod) { 98 | cout << "Multiply failed." << endl; 99 | } 100 | if (resProd2.message != prod2) { 101 | cout << "Squaring failed." << endl; 102 | } 103 | if (resSumQuad.message != sumQuad) { 104 | cout << "Sum and quad failed." << endl; 105 | } 106 | } 107 | 108 | if (disp || !success) { 109 | cout << "Test " << (success ? "SUCCEEDED" : "FAILED") << endl; 110 | } 111 | 112 | return success; 113 | } 114 | 115 | int main(int argc, char *argv[]) { 116 | unsigned p, g, logQ; 117 | 118 | if (argc >= 4) { 119 | logQ = atoi(argv[1]); 120 | p = atoi(argv[2]); 121 | g = atoi(argv[3]); 122 | } else { 123 | cout << "usage: Test_AddMul_x logQ p generator [seed]" << endl; 124 | return 1; 125 | } 126 | 127 | cout << "==================================================" << endl 128 | << "Running add/multiply tests using Brakerski system." << endl 129 | << "==================================================" << endl; 130 | 131 | FHEcontext context(p-1, logQ, p, g, 3); 132 | activeContext = &context; 133 | 134 | context.SetUpSIContext(); 135 | 136 | cout << "Finished setting up context." << endl; 137 | 138 | if (argc >= 5) { 139 | long long seed = atoi(argv[4]); 140 | bool res = runTest(true, seed, p, context); 141 | 142 | if (!res) { 143 | cout << "Failed test with seed " << seed << endl; 144 | return 1; 145 | } 146 | return 0; 147 | } 148 | 149 | int testsFailed = 0; 150 | 151 | srand48(time(NULL)); 152 | long long start = lrand48(); 153 | for (int iter = 0; iter < N_TESTS; iter++) { 154 | if (!runTest(false, start+iter, p, context)) { 155 | testsFailed++; 156 | } 157 | 158 | if (iter % 100 == 0) { 159 | cout << "." << flush; 160 | } 161 | } 162 | cout << endl; 163 | 164 | if (testsFailed == 0) { 165 | cout << "All tests SUCCEEDED!" << endl; 166 | } else{ 167 | cout << testsFailed << " of " << N_TESTS << " failed." << endl; 168 | } 169 | 170 | return testsFailed; 171 | } 172 | -------------------------------------------------------------------------------- /Test_General.cpp: -------------------------------------------------------------------------------- 1 | #include "FHE-SI.h" 2 | #include "Ciphertext.h" 3 | #include "Plaintext.h" 4 | 5 | void randomizePlaintext(Plaintext &ptxt, unsigned deg, unsigned p) { 6 | ZZ_pX poly; 7 | poly.rep.SetLength(deg); 8 | for(unsigned i = 0; i < deg; i++) { 9 | poly.rep[i] = RandomBnd(p); 10 | } 11 | 12 | poly.normalize(); 13 | ptxt.Init(poly); 14 | } 15 | 16 | int main(int argc, char *argv[]) { 17 | long seed = time(NULL); 18 | 19 | srand48(seed); 20 | SetSeed(to_ZZ(seed)); 21 | 22 | unsigned p = 2027; 23 | unsigned g = 3; 24 | unsigned logQ = 120; 25 | 26 | FHEcontext context(p-1, logQ, p, g); 27 | activeContext = &context; 28 | context.SetUpSIContext(); 29 | 30 | FHESISecKey secretKey(context); 31 | const FHESIPubKey &publicKey(secretKey); 32 | KeySwitchSI keySwitch(secretKey); 33 | 34 | long phim = context.zMstar.phiM(); 35 | long numSlots = context.GetPlaintextSpace().GetTotalSlots(); 36 | 37 | long rotAmt = rand() % numSlots; 38 | long rotDeg = 1; 39 | for (int i = 0; i < rotAmt; i++) { 40 | rotDeg *= context.Generator(); 41 | rotDeg %= context.zMstar.M(); 42 | } 43 | KeySwitchSI automorphKeySwitch(secretKey, rotDeg); 44 | 45 | Plaintext p0, p1, p2, p3; 46 | randomizePlaintext(p0, phim, p); 47 | randomizePlaintext(p1, phim, p); 48 | randomizePlaintext(p2, phim, p); 49 | randomizePlaintext(p3, phim, p); 50 | 51 | Plaintext const1, const2; 52 | randomizePlaintext(const1, phim, p); 53 | randomizePlaintext(const2, phim, p); 54 | 55 | Ciphertext c0(publicKey); 56 | Ciphertext c1(publicKey); 57 | Ciphertext c2(publicKey); 58 | Ciphertext c3(publicKey); 59 | 60 | publicKey.Encrypt(c0, p0); 61 | publicKey.Encrypt(c1, p1); 62 | publicKey.Encrypt(c2, p2); 63 | publicKey.Encrypt(c3, p3); 64 | 65 | p1 *= p2; 66 | p0 += const1; 67 | p2 *= const2; 68 | p3 >>= rotAmt; 69 | p1 *= -1; 70 | p3 *= p2; 71 | p0 -= p3; 72 | 73 | c1 *= c2; 74 | keySwitch.ApplyKeySwitch(c1); 75 | c0 += const1.message; 76 | 77 | c2 *= const2.message; 78 | 79 | c3 >>= rotDeg; 80 | automorphKeySwitch.ApplyKeySwitch(c3); 81 | 82 | c1 *= -1; 83 | c3 *= c2; 84 | keySwitch.ApplyKeySwitch(c3); 85 | 86 | Ciphertext tmp(c3); 87 | tmp *= -1; 88 | c0 += tmp; 89 | 90 | Plaintext pp0, pp1, pp2, pp3; 91 | secretKey.Decrypt(pp0, c0); 92 | secretKey.Decrypt(pp1, c1); 93 | secretKey.Decrypt(pp2, c2); 94 | secretKey.Decrypt(pp3, c3); 95 | 96 | if (!(pp0 == p0)) cerr << "oops 0" << endl; 97 | if (!(pp1 == p1)) cerr << "oops 1" << endl; 98 | if (!(pp2 == p2)) cerr << "oops 2" << endl; 99 | if (!(pp3 == p3)) cerr << "oops 3" << endl; 100 | cout << "All tests finished." << endl; 101 | } -------------------------------------------------------------------------------- /Test_Regression.cpp: -------------------------------------------------------------------------------- 1 | #include "FHE-SI.h" 2 | #include "Ciphertext.h" 3 | #include "SingleCRT.h" 4 | #include "Regression.h" 5 | #include <time.h> 6 | #include <ctime> 7 | #include <fstream> 8 | #include "Util.h" 9 | 10 | static int RunRegressionTest(Matrix<ZZ> &rawData, vector<ZZ> &labels, 11 | const ZZ &p, const FHEcontext &context) { 12 | 13 | vector<ZZ> theta; 14 | ZZ det; 15 | RegressPT(theta, det, rawData, labels); 16 | 17 | cout << "Expected values: " << endl; 18 | for (unsigned i = 0; i < theta.size(); i++) { 19 | cout << " theta[" << i << "] = " << theta[i] % p << endl; 20 | } 21 | cout << " Determinant: " << det % p << endl; 22 | cout << endl << endl; 23 | 24 | double start(clock()); 25 | Regression regress(context); 26 | cout << "Setup time: " << (clock()-start)/CLOCKS_PER_SEC << endl; 27 | 28 | vector<vector<Plaintext>> ptxtData; 29 | vector<Plaintext> ptxtLabels; 30 | 31 | double batchTime = BatchData(ptxtData, ptxtLabels, rawData, labels, context); 32 | 33 | cout << "Batch time: " << batchTime << endl; 34 | 35 | double encStart(clock()); 36 | regress.AddData(ptxtData, ptxtLabels); 37 | cout << "Encryption time: " << (clock()-encStart)/CLOCKS_PER_SEC << endl; 38 | 39 | vector<Ciphertext> encTheta; 40 | Ciphertext encDet(regress.GetPublicKey()); 41 | 42 | double regressionStart(clock()); 43 | regress.Regress(encTheta, encDet); 44 | cout << "Regression time: " << (clock()-regressionStart)/CLOCKS_PER_SEC << endl; 45 | 46 | FHESISecKey secretKey = regress.GetSecretKey(); 47 | Plaintext tmp(context); 48 | vector<ZZ_pX> msgs; 49 | ZZ_pX msg; 50 | 51 | double decStart(clock()); 52 | cout << endl << "Computed values: " << endl; 53 | for (unsigned i = 0; i < encTheta.size(); i++) { 54 | secretKey.Decrypt(tmp, encTheta[i]); 55 | tmp.DecodeSlots(msgs); 56 | cout << " theta[" << i << "] = " << msgs[0] << endl; 57 | } 58 | 59 | secretKey.Decrypt(tmp, encDet); 60 | tmp.DecodeSlots(msgs); 61 | 62 | cout << " Determinant: " << msgs[0] << endl << endl; 63 | cout << "Decryption time: " << (clock() - decStart)/CLOCKS_PER_SEC << endl; 64 | cout << "Total time: " << (clock()-start)/CLOCKS_PER_SEC << endl; 65 | 66 | return 0; 67 | } 68 | 69 | int main(int argc, char *argv[]) { 70 | srand48(time(NULL)); 71 | SetSeed(to_ZZ(time(NULL))); 72 | 73 | unsigned p, g, logQ = 0; 74 | char *datafile; 75 | 76 | if (argc >= 4) { 77 | datafile = argv[1]; 78 | p = atoi(argv[2]); 79 | g = atoi(argv[3]); 80 | } else { 81 | cout << "usage: Test_Regression_x datafile p generator" << endl; 82 | return 1; 83 | } 84 | 85 | unsigned blockSize = 1; 86 | unsigned val = (p-1)/2-1; 87 | while (val > 1) { 88 | blockSize <<= 1; 89 | val >>= 1; 90 | } 91 | 92 | Matrix<ZZ> rawData; 93 | vector<ZZ> labels; 94 | unsigned dim; 95 | 96 | if (!LoadData(rawData, labels, dim, datafile)) { 97 | return 1; 98 | } 99 | 100 | unsigned n = (p-1)/2-1; 101 | unsigned nBlocks = labels.size() / blockSize; 102 | if (labels.size() % blockSize != 0) { 103 | nBlocks++; 104 | } 105 | unsigned xi = max(nBlocks, dim); 106 | 107 | double lgQ = 4.5*log(n)+max(1,(int) dim-1)*(log(1280)+2*log(n)+log(xi)); 108 | logQ = (unsigned) ceil(lgQ / log(2) + 24.7); 109 | 110 | cout << "================================================" << endl 111 | << "Running regression tests using Brakerski system." << endl 112 | << "================================================" << endl; 113 | 114 | cout << "Parameters: " << endl 115 | << " data file: " << datafile << endl 116 | << " logQ: " << logQ << endl 117 | << " p: " << p << endl 118 | << " generator: " << g << endl 119 | << " block size: " << blockSize << endl 120 | << " num blocks: " << nBlocks << endl; 121 | 122 | FHEcontext context(p-1, logQ, p, g, 3); 123 | activeContext = &context; 124 | 125 | cout << "Running " << dim << "-dimensional regression on " << rawData.NumRows() 126 | << " datapoints in " << (rawData.NumRows()+blockSize-1)/blockSize 127 | << " blocks, modulo prime " << p << endl; 128 | 129 | context.SetUpSIContext(xi); 130 | return RunRegressionTest(rawData, labels, to_ZZ(p), context); 131 | } 132 | -------------------------------------------------------------------------------- /Test_Statistics.cpp: -------------------------------------------------------------------------------- 1 | #include "FHE-SI.h" 2 | 3 | #include "Statistics.h" 4 | #include <time.h> 5 | #include <ctime> 6 | #include <fstream> 7 | #include "Util.h" 8 | 9 | static bool LoadData(Matrix<ZZ> &data, unsigned &dim, const string &filename) { 10 | ifstream fin; 11 | fin.open(filename); 12 | if (!fin) { 13 | cout << "Unable to read data file." << endl; 14 | return false; 15 | } 16 | 17 | data.Clear(); 18 | 19 | unsigned n, junk; 20 | fin >> dim >> n; 21 | vector<ZZ> vec(dim); 22 | 23 | for (unsigned i = 0; i < n; i++) { 24 | for (unsigned j = 0; j < dim; j++) { 25 | fin >> vec[j]; 26 | } 27 | fin >> junk; 28 | 29 | data.AddRow(vec); 30 | } 31 | 32 | return true; 33 | } 34 | 35 | double BatchData(Matrix<Plaintext> &ptxtBlocks, vector<Plaintext> &ptxtBlockSizes, 36 | const Matrix<ZZ> &rawData, const FHEcontext &context) { 37 | 38 | double start(clock()); 39 | 40 | ZZ p = to_ZZ(context.ModulusP()); 41 | unsigned batchSize = context.GetPlaintextSpace().GetUsableSlots(); 42 | 43 | ptxtBlocks.Clear(); 44 | ptxtBlockSizes.clear(); 45 | 46 | for (unsigned i = 0; i < rawData.NumRows(); i += batchSize) { 47 | vector<Plaintext> row; 48 | unsigned n = 0; 49 | 50 | for (unsigned j = 0; j < rawData.NumCols(); j++) { 51 | vector<ZZ> columnBatch; 52 | for (unsigned k = i; ((k < i + batchSize) && (k < rawData.NumRows())); k++) { 53 | columnBatch.push_back(to_ZZ(rawData[k][j]) % p); 54 | } 55 | n = columnBatch.size(); 56 | row.push_back(Plaintext(context, columnBatch)); 57 | } 58 | 59 | ptxtBlocks.AddRow(row); 60 | ptxtBlockSizes.push_back(Plaintext(context, n)); 61 | } 62 | 63 | return (clock()-start)/CLOCKS_PER_SEC; 64 | } 65 | 66 | static int RunStatisticsTest(Matrix<ZZ> &data, unsigned p, const FHEcontext &context) { 67 | vector<ZZ> mean; 68 | Matrix<ZZ> cov; 69 | 70 | ComputeNthMomentPT(mean, 1, data); 71 | ComputeCovariancePT(cov, data); 72 | 73 | cout << "True values: " << endl; 74 | cout << " Mean: "; 75 | for (unsigned i = 0; i < mean.size(); i++) { 76 | cout << mean[i] << ", "; 77 | } 78 | cout << endl; 79 | 80 | cout << " Covariance: " << endl; 81 | for (unsigned i = 0; i < cov.NumRows(); i++) { 82 | cout << " "; 83 | for (unsigned j = 0; j < cov.NumCols(); j++) { 84 | cout << cov(i,j) << " "; 85 | } 86 | cout << endl; 87 | } 88 | cout << endl; 89 | 90 | cout << "Expected values: " << endl; 91 | cout << " Mean: "; 92 | for (unsigned i = 0; i < mean.size(); i++) { 93 | cout << mean[i] % to_ZZ(p) << ", "; 94 | } 95 | cout << endl; 96 | 97 | cout << " N: " << data.NumRows() % p << endl << endl; 98 | cov.MapAll([p](ZZ &elem) { 99 | elem %= to_ZZ(p); 100 | }); 101 | cout << " Covariance: " << endl; 102 | for (unsigned i = 0; i < cov.NumRows(); i++) { 103 | cout << " "; 104 | for (unsigned j = 0; j < cov.NumCols(); j++) { 105 | cout << cov(i,j) % to_ZZ(p) << " "; 106 | } 107 | cout << endl; 108 | } 109 | cout << endl; 110 | cout << " N^2: " << ((data.NumRows() % p) * (data.NumRows() % p)) % p << endl << endl; 111 | 112 | double start(clock()); 113 | 114 | Statistics stats(context); 115 | cout << "Setup time: " << (clock()-start)/CLOCKS_PER_SEC << endl; 116 | 117 | Matrix<Plaintext> ptxtBlocks; 118 | vector<Plaintext> ptxtBlockSizes; 119 | 120 | double batchTime = BatchData(ptxtBlocks, ptxtBlockSizes, data, context); 121 | 122 | cout << "Batch time: " << batchTime << endl; 123 | 124 | double encryptionStart(clock()); 125 | stats.AddData(ptxtBlocks, ptxtBlockSizes); 126 | cout << "Encryption time: " << (clock()-encryptionStart)/CLOCKS_PER_SEC << endl; 127 | 128 | vector<Ciphertext> encMean; 129 | Matrix<Ciphertext> encCov(Ciphertext(stats.GetPublicKey())); 130 | Ciphertext encN(stats.GetPublicKey()); 131 | Ciphertext encN2(stats.GetPublicKey()); 132 | 133 | double computationStart(clock()); 134 | stats.ComputeCovariance(encCov, encMean, encN, encN2); 135 | 136 | cout << "Computation time: " << (clock()-computationStart)/CLOCKS_PER_SEC << endl; 137 | 138 | FHESISecKey secretKey = stats.GetSecretKey(); 139 | 140 | Plaintext tmp(context); 141 | vector<ZZ_pX> msgs; 142 | ZZ_pX msg; 143 | 144 | double decStart(clock()); 145 | cout << endl << "Computed values: " << endl; 146 | cout << " Mean: "; 147 | for (unsigned i = 0; i < encMean.size(); i++) { 148 | secretKey.Decrypt(tmp, encMean[i]); 149 | tmp.DecodeSlots(msgs); 150 | cout << msgs[0] << ", "; 151 | } 152 | cout << endl; 153 | 154 | secretKey.Decrypt(tmp, encN); 155 | cout << " N: " << tmp << endl << endl; 156 | 157 | cout << " Covariance: " << endl; 158 | Matrix<ZZ_pX> decCov(encCov.NumRows(), encCov.NumCols()); 159 | for (unsigned i = 0; i < encCov.NumRows(); i++) { 160 | for (unsigned j = i; j < encCov.NumCols(); j++) { 161 | secretKey.Decrypt(tmp, encCov(i,j)); 162 | tmp.DecodeSlots(msgs); 163 | decCov(i,j) = decCov(j,i) = msgs[0]; 164 | } 165 | } 166 | 167 | cout << decCov << endl; 168 | 169 | secretKey.Decrypt(tmp, encN2); 170 | cout << " N^2: " << tmp << endl << endl; 171 | 172 | cout << "Decryption time: " << (clock()-decStart)/CLOCKS_PER_SEC << endl; 173 | cout << "Total time: " << (clock()-start)/CLOCKS_PER_SEC << endl; 174 | 175 | return 0; 176 | } 177 | 178 | int main(int argc, char *argv[]) { 179 | srand48(time(NULL)); 180 | SetSeed(to_ZZ(time(NULL))); 181 | 182 | unsigned p, g, logQ = 0; 183 | char *datafile; 184 | 185 | if (argc >= 4) { 186 | datafile = argv[1]; 187 | p = atoi(argv[2]); 188 | g = atoi(argv[3]); 189 | } else { 190 | cout << "usage: Test_Statistics_x datafile p generator" << endl; 191 | return 1; 192 | } 193 | 194 | unsigned blockSize = 1; 195 | unsigned val = (p-1)/2; 196 | while (val > 1) { 197 | blockSize <<= 1; 198 | val >>= 1; 199 | } 200 | 201 | Matrix<ZZ> data; 202 | unsigned dim; 203 | 204 | if (!LoadData(data, dim, datafile)) { 205 | cout << "Failed to load data file." << endl; 206 | return 1; 207 | } 208 | 209 | unsigned n = (p-1)/2-1; 210 | unsigned nBlocks = data.NumRows() / blockSize; 211 | if (data.NumRows() % blockSize != 0) { 212 | nBlocks++; 213 | } 214 | unsigned xi = max(nBlocks, dim); 215 | 216 | double lgQ = 6.5*log(n)+log(xi); 217 | logQ = (unsigned) ceil(lgQ / log(2) + 36.1); 218 | 219 | cout << "================================================" << endl 220 | << "Running statistics test using Brakerski system." << endl 221 | << "================================================" << endl; 222 | 223 | cout << "Parameters: " << endl 224 | << " data file: " << datafile << endl 225 | << " logQ: " << logQ << endl 226 | << " p: " << p << endl 227 | << " generator: " << g << endl 228 | << " block size: " << blockSize << endl 229 | << " num blocks: " << nBlocks << endl; 230 | 231 | 232 | cout << "Performing statistical analysis on " << data.NumRows() 233 | << " values (dimension " << dim << ") in " << (data.NumRows()+blockSize-1)/blockSize 234 | << " blocks, modulo prime " << p << endl << endl; 235 | 236 | vector<ZZ> mean; 237 | Matrix<ZZ> cov; 238 | 239 | FHEcontext context(p-1, logQ, p, g, 3); 240 | activeContext = &context; 241 | 242 | context.SetUpSIContext(xi); 243 | return RunStatisticsTest(data, p, context); 244 | } 245 | -------------------------------------------------------------------------------- /Util.cpp: -------------------------------------------------------------------------------- 1 | #include "Util.h" 2 | 3 | void Reduce(ZZ &val, unsigned logQ, bool positive) { 4 | /* This can all be pre-computed, given constant q */ 5 | ZZ signMask, dataMask; 6 | signMask = 1; 7 | dataMask = 1; 8 | 9 | signMask <<= logQ - 1; 10 | dataMask <<= logQ; 11 | dataMask -= 1; 12 | /***********************************************/ 13 | //val += q; 14 | 15 | if(sign(val) == -1) { 16 | ZZ factor = val>>logQ; 17 | //cout << "val/q: " << factor << endl; 18 | val += (1-(val>>logQ)) << logQ; 19 | //cout << "New val: " << val << endl; 20 | } 21 | val &= dataMask; 22 | if(!positive) { 23 | val ^= signMask; 24 | val -= signMask; 25 | } 26 | } 27 | 28 | void ReduceCoefficients(ZZX &poly, unsigned logQ, bool positive) { 29 | for (long i = 0; i <= deg(poly); i++) { 30 | Reduce(poly.rep[i], logQ, positive); 31 | } 32 | poly.normalize(); 33 | } 34 | 35 | void ReduceCoefficientsSlow(ZZX &poly, const ZZ &modulus, bool positive) { 36 | for (long i = 0; i <= deg(poly); i++) { 37 | poly.rep[i] %= modulus; 38 | if (!positive && poly.rep[i] > modulus/2) { 39 | poly.rep[i] -= modulus; 40 | } 41 | } 42 | poly.normalize(); 43 | } 44 | 45 | void ReduceCoefficientsSlow(ZZX &poly, unsigned modulus, bool positive) { 46 | ReduceCoefficientsSlow(poly, to_ZZ(modulus), positive); 47 | } 48 | 49 | void SampleRandom(ZZX &poly, const ZZ &modulus, unsigned deg) { 50 | ZZ offset = modulus / 2; 51 | poly.SetMaxLength(deg); 52 | for (unsigned i = 0; i < deg; i++) { 53 | SetCoeff(poly, i, RandomBnd(modulus) - offset); 54 | } 55 | } 56 | 57 | void GetConstantTerm(ZZ &res, ZZ_pX &poly) { 58 | if (deg(poly) == -1) { 59 | res = 0; 60 | } else { 61 | res = rep(poly.rep[0]); 62 | } 63 | } 64 | 65 | -------------------------------------------------------------------------------- /Util.h: -------------------------------------------------------------------------------- 1 | #ifndef _UTIL_H_ 2 | #define _UTIL_H_ 3 | 4 | #include <NTL/ZZX.h> 5 | #include <NTL/ZZ.h> 6 | #include <NTL/ZZ_pX.h> 7 | #include <vector> 8 | #include <cstring> 9 | #include <iostream> 10 | 11 | using namespace NTL; 12 | using namespace std; 13 | 14 | void Reduce(ZZ &val, unsigned logQ, bool positive = false); 15 | void ReduceCoefficients(ZZX &poly, unsigned logQ, 16 | bool positive = false); 17 | void ReduceCoefficientsSlow(ZZX &poly, const ZZ &modulus, 18 | bool positive = false); 19 | void ReduceCoefficientsSlow(ZZX &poly, unsigned modulus, 20 | bool positive = false); 21 | 22 | 23 | /* 24 | Reduces val to integer congruent (mod q) in the range (-q/2,q/2] 25 | Assumes q is a power of two. 26 | */ 27 | void ReduceMod(ZZ &val, unsigned logQ); 28 | 29 | /* 30 | Base decomposition for bases of the form 2^(8*size) 31 | */ 32 | void ByteDecomp(vector<ZZX> &smallPolys, const ZZX &poly, 33 | unsigned logQ, unsigned size = 1); 34 | 35 | void ByteDecomp(vector<vector<ZZX>> &smallPolys, const vector<ZZX> &polys, 36 | unsigned logQ, unsigned size = 1); 37 | 38 | /* 39 | First n powers of 2^(8*i) times poly 40 | */ 41 | void Powers(vector<ZZX> &powers, const ZZX &poly, long n, unsigned size = 1); 42 | 43 | /* 44 | byteDecomps is the vector of Byte Decomps to match components size with. 45 | */ 46 | void Powers(vector<vector<ZZX>> &powers, const vector<ZZX> &polys, 47 | vector<vector<ZZX>> byteDecomps, unsigned size = 1); 48 | 49 | void SampleRandom(ZZX &poly, const ZZ &modulus, unsigned deg); 50 | 51 | void GetConstantTerm(ZZ &res, ZZ_pX &poly); 52 | 53 | template<typename T> 54 | void PrintVector(const vector<T> &vec, ostream &out = std::cout) { 55 | for (unsigned i = 0; i < vec.size(); i++) { 56 | out << vec[i] << " "; 57 | } 58 | } 59 | 60 | template<typename T> 61 | void PrintVector(const vector<vector<T>> &vec, ostream &out = std::cout) { 62 | for (unsigned i = 0; i < vec.size(); i++) { 63 | PrintVector(vec[i]); 64 | out << endl; 65 | } 66 | } 67 | 68 | template <typename T> 69 | unsigned ComputeLog(T val) { 70 | unsigned log = 0; 71 | while (val != 0) { 72 | val >>= 1; 73 | log++; 74 | } 75 | 76 | return log-1; 77 | } 78 | 79 | template<typename T> 80 | static void DotProduct(T &res, const vector<T> &v1, 81 | const vector<T> &v2) { 82 | #ifdef DEBUG 83 | assert(v1.size() == v2.size()); 84 | #endif 85 | 86 | if (v1.size() == 0) { 87 | return; 88 | } 89 | 90 | res = v1[0]; 91 | res *= v2[0]; 92 | 93 | for (unsigned i = 1; i < v1.size(); i++) { 94 | T val = v1[i]; 95 | val *= v2[i]; 96 | res += val; 97 | } 98 | } 99 | 100 | template<typename T> 101 | static void TensorProduct(vector<T> &res, const vector<T> &v1, const vector<T> &v2) { 102 | res.resize(v1.size()*v2.size()); 103 | 104 | unsigned ind = 0; 105 | for (unsigned i = 0; i < v1.size(); i++) { 106 | for (unsigned j = 0; j < v2.size(); j++) { 107 | res[ind] = v1[i]; 108 | res[ind++] *= v2[j]; 109 | } 110 | } 111 | } 112 | 113 | #endif 114 | -------------------------------------------------------------------------------- /bluestein.cpp: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012,2013 IBM Corp. 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | * See the GNU General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License along 13 | * with this program; if not, write to the Free Software Foundation, Inc., 14 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 15 | */ 16 | 17 | /* 18 | * bluestein.cpp - 19 | * An implementation of non-power-of-two FFT using Bluestein's trick 20 | * 21 | * Entry points: 22 | * 23 | * void DFT(ZZ_pX& x, const ZZ_pX& a, long n, const ZZ_p& root); 24 | * void DFT(zz_pX& x, const zz_pX& a, long n, const zz_p& root); 25 | * void BluesteinFFT(ZZ_pX& x, const ZZ_pX& a, long n, const ZZ_p& root, 26 | * ZZ_pX& powers, FFTRep& Rb); 27 | * void BluesteinFFT(zz_pX& x, const zz_pX& a, long n, const zz_p& root, 28 | * zz_pX& powers, fftRep& Rb); 29 | * 30 | */ 31 | #include <NTL/ZZX.h> 32 | #include <NTL/lzz_pX.h> 33 | #include <NTL/ZZ_pX.h> 34 | 35 | NTL_CLIENT 36 | 37 | /* This module builds on Shoup's NTL, and contains both a bigint version 38 | * with types ZZ_p and ZZ_pX and a smallint version with types zz_p and zz_pX. 39 | * 40 | * These two versions are otherwise identical, hence a function template 41 | * is used to describe both. (NTL itself usually uses macros rather than 42 | * templates for this purpose, but in 2011 compiler support for templates 43 | * is already stable enough to use.) We use the name tXYZ(...) for the 44 | * template and XYZ(...) for the instantiated function. 45 | */ 46 | 47 | 48 | // NTL uses different function names for conversion between standard and 49 | // FFT representations for ZZ_pX vs zz_pX. Hence we need a wrapper so that 50 | // we can use the same name in the template below. 51 | inline void ToFFTRep(fftRep& y, const zz_pX& x, long k) 52 | { TofftRep(y, x, k, 0, deg(x)); } 53 | inline void FromFFTRep(zz_pX& x, fftRep& y, long lo, long hi) 54 | { FromfftRep(x, y, lo, hi);} 55 | 56 | 57 | /* BluesteinFFT(x, a, n, root, powers, Rb): 58 | * ---------------------------------------- 59 | * Compute length-n FFT of the coefficient-vector of a and put the result in 60 | * x. If the degree of a is less than n then it treats the top coefficients 61 | * as 0, if the degree of a is more than n then the extra coefficients are 62 | * ignored. Similarly, if the top entries in x are zeros then x will have 63 | * degree smaller than n. The argument root is a 2n-th root of unity, namely 64 | * BluesteinFFT(...,root,...)=DFT(...,root^2,...). 65 | * 66 | * The inverse-FFT is obtained just by calling BluesteinFFT(... root^{-1}), 67 | * but this procedure is *NOT SCALED*, so BluesteinFFT(x,a,n,root,...) and 68 | * then BluesteinFFT(a2,x,n,root^{-1},...) will result in a2=a*n. 69 | * 70 | * In addition to the size-n FFT of a which is returned in x, this procedure 71 | * also returns the powers of root in the powers argument: 72 | * powers = [1, root, root^4, root^9, ..., root^{(n-1)^2}] 73 | * and in Rb it returns the size-N FFT representation of the negative 74 | * powers (with N>=2n-1, N a power of two): 75 | * b = [0,...,0, root^{-(n-1)^2},...,root^{-4}, root^{-1}, 1, 76 | * root^{-1},root^{-4},...,root^{-(n-1)^2}, 0...,0] 77 | * On subsequent calls with these 'powers' and 'Rb', these arrays are 78 | * not computed again but taken from these pre-comuted variables. 79 | * 80 | * If the powers and Rb arguments are initialized, then it is assumed that 81 | * they were computed correctly from root. The bahavior is undefined when 82 | * calling with initialized powers and Rb but a different root. In particular, 83 | * to compute the inverse-FFT (using root^{-1}), one must provide different 84 | * powers and Rb arguments than those that were given when computing in the 85 | * forward direction using root. To reset these arguments between calls 86 | * with different root values, use clear(powers); Rb.SetSize(0); 87 | * 88 | * This procedure assume that the global zp::modulus() was defined before 89 | * calling it. Also, this procedure *cannot* be used for in-place FFT, 90 | * calling BluesteinFFT(x,x,...) will just zero-out the polynomial x. 91 | */ 92 | template <class zp, class zpX, class fftrep> 93 | void tBluesteinFFT(zpX& x, const zpX& a, long n, const zp& root, 94 | zpX& powers, fftrep& Rb) 95 | { 96 | clear(x); 97 | if (IsZero(a) || n<=0) return; 98 | 99 | zp one; one=1; 100 | x.SetMaxLength(n); 101 | if (powers.rep.length()<n) powers.SetMaxLength(n); 102 | 103 | if (deg(powers)!=n) { // If the degree does not match, re-compute powers 104 | SetCoeff(powers,0,one); 105 | for (long i=1; i<n; i++) { 106 | long iSqr = MulMod(i, i, 2*n); // i^2 mod 2n 107 | SetCoeff(powers,i, power(root,iSqr)); // powers[i] = root^{i^2} 108 | } 109 | } // if deg(powers)==n, assume that it already includes powers of root 110 | 111 | for (long i=0; i<n; i++) { 112 | SetCoeff(x, i, coeff(a,i)*coeff(powers,i)); 113 | } 114 | // cout << " a.powers="<<x<<"\n"; 115 | 116 | long k = NextPowerOfTwo(2*n-1); 117 | long k2 = 1L << k; // k2 = 2^k 118 | fftrep Ra(INIT_SIZE, k); 119 | ToFFTRep(Ra, x, k); 120 | 121 | if (Rb.k!=k) { // If the transform size doesn't match, re-do the transform 122 | Rb.SetSize(k); 123 | zpX b(INIT_SIZE, k2); 124 | 125 | zp rInv = inv(root); 126 | SetCoeff(b,n-1,one); // b[n-1] = 1 127 | for (long i=1; i<n; i++) { 128 | long iSqr = MulMod(i, i, 2*n); // i^2 mod 2n 129 | zp bi = power(rInv,iSqr); 130 | SetCoeff(b,n-1+i, bi); // b[n-1+i] = b[n-1-i] = root^{-i^2} 131 | SetCoeff(b,n-1-i,bi); 132 | } 133 | // cout << " b="<<b<<"\n"; 134 | // cout << " (a.powers)*b="<<b*x<<"\n"; 135 | ToFFTRep(Rb, b, k); 136 | } // if Rb.k==k, assume that Rb already contains a transform of b 137 | 138 | mul(Ra,Ra,Rb); // multiply in FFT representation 139 | FromFFTRep(x, Ra, n-1, 2*(n-1)); // then convert back 140 | for (long i=0; i<n; i++) { 141 | SetCoeff(x, i, coeff(x,i)*coeff(powers,i)); 142 | } 143 | x.normalize(); 144 | } 145 | 146 | // For degugging purposes: a slow DFT procedure 147 | // x[k] = \sum_{i=0}^{n-1} a[i] root^{ki} 148 | template <class zp, class zpX> 149 | void tDFT(zpX& x, const zpX& a, long n, const zp& root) 150 | { 151 | clear(x); 152 | if (IsZero(a) || n<=0) return; 153 | 154 | x.SetMaxLength(n); 155 | 156 | zp sum = coeff(a,0); 157 | for (int i=1; i<n; i++) sum += coeff(a,i); 158 | SetCoeff(x,0,sum); 159 | 160 | zp term, base = root; // base = root^k 161 | for (long k=1; k<n; k++) { 162 | sum = coeff(a,0); 163 | term = base; // term = root^{ki} 164 | for (long i=1; i<n; i++) { 165 | sum += coeff(a,i) * term; // add a[i] * root^{ki} 166 | term *= base; // term = root^{k(i+1)} 167 | } 168 | SetCoeff(x, k, sum); 169 | base *= root; // base = root^{k+1} 170 | } 171 | x.normalize(); 172 | } 173 | 174 | // Instantiations of the templates above for ZZ_p/ZZ_pX/FFTRep 175 | // and for zz_p/zz_pX/fftrep 176 | 177 | void BluesteinFFT(ZZ_pX& x, const ZZ_pX& a, long n, const ZZ_p& root, 178 | ZZ_pX& powers, FFTRep& Rb) 179 | { tBluesteinFFT<ZZ_p,ZZ_pX,FFTRep>(x,a,n,root,powers,Rb); } 180 | 181 | void BluesteinFFT(zz_pX& x, const zz_pX& a, long n, const zz_p& root, 182 | zz_pX& powers, fftRep& Rb) 183 | { tBluesteinFFT<zz_p,zz_pX,fftRep>(x,a,n,root,powers,Rb); } 184 | 185 | void DFT(ZZ_pX& x, const ZZ_pX& a, long n, const ZZ_p& root) 186 | { tDFT<ZZ_p,ZZ_pX>(x,a,n,root); } 187 | 188 | void DFT(zz_pX& x, const zz_pX& a, long n, const zz_p& root) 189 | { tDFT<zz_p,zz_pX>(x,a,n,root); } 190 | -------------------------------------------------------------------------------- /bluestein.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 2012,2013 IBM Corp. 2 | * This program is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License as published by 4 | * the Free Software Foundation; either version 2 of the License, or 5 | * (at your option) any later version. 6 | * 7 | * This program is distributed in the hope that it will be useful, 8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 10 | * See the GNU General Public License for more details. 11 | * 12 | * You should have received a copy of the GNU General Public License along 13 | * with this program; if not, write to the Free Software Foundation, Inc., 14 | * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 15 | */ 16 | 17 | #ifndef _Bluestein 18 | #define _Bluestein 19 | /* bluestein.h - declaration of BluesteinFFT(x, a, n, root, powers, Rb): 20 | * --------------------------------------------------------------------- 21 | * Compute length-n FFT of the coefficient-vector of a, put the result in x 22 | * If the degree of a is less than n then it treats the top coefficients 23 | * as 0, if the degree of a is more than n then the extra coefficients are 24 | * ignored. Similarly, if the top entries in x are zeros then x will have 25 | * degree smaller than n. The argument root is a 2n-th root of unity, namely 26 | * BluesteinFFT(...,root,...)=DFT(...,root^2,...). 27 | * 28 | * The inverse-FFT is obtained just by calling BluesteinFFT(... root^{-1}), 29 | * but this procedure is *NOT SCALED*, so BluesteinFFT(x,a,n,root,...) and 30 | * then BluesteinFFT(a2,x,n,root^{-1},...) will result in a2=a*n. 31 | * 32 | * In addition to the size-n FFT of a which is returned in x, this procedure 33 | * also returns the powers of root in the powers argument: 34 | * powers = [1, root, root^4, root^9, ..., root^{(n-1)^2}] 35 | * and in Rb it returns the size-N FFT representation of the negative 36 | * powers (with N>=2n-1, N a power of two): 37 | * b = [0,...,0, root^{-(n-1)^2},...,root^{-4}, root^{-1}, 1, 38 | * root^{-1},root^{-4},...,root^{-(n-1)^2}, 0...,0] 39 | * On subsequent calls with these 'powers' and 'Rb', these arrays are 40 | * not computed again but taken from these pre-comuted variables. 41 | * 42 | * If the powers and Rb arguments are initialized, then it is assumed that 43 | * they were computed correctly from root. The bahavior is undefined when 44 | * calling with initialized powers and Rb but a different root. In particular, 45 | * to compute the inverse-FFT (using root^{-1}), one must provide different 46 | * powers and Rb arguments than those that were given when computing in the 47 | * forward direction using root. To reset these arguments between calls 48 | * with different root values, use clear(powers); Rb.SetSize(0); 49 | * 50 | * This procedure assume that the global zp::modulus() was defined before 51 | * calling it. Also, this procedure *cannot* be used for in-place FFT, 52 | * calling BluesteinFFT(x,x,...) will just zero-out the polynomial x. 53 | * 54 | * This module builds on Shoup's NTL, and contains both a bigint version 55 | * with types ZZ_p and ZZ_pX and a smallint version with types zz_p and zz_pX. 56 | */ 57 | #include <NTL/ZZX.h> 58 | #include <NTL/ZZ_pX.h> 59 | #include <NTL/lzz_pX.h> 60 | 61 | NTL_CLIENT 62 | 63 | void BluesteinFFT(ZZ_pX& x, const ZZ_pX& a, long n, 64 | const ZZ_p& root, ZZ_pX& powers, FFTRep& Rb); 65 | 66 | void BluesteinFFT(zz_pX& x, const zz_pX& a, long n, 67 | const zz_p& root, zz_pX& powers, fftRep& Rb); 68 | 69 | #endif 70 | 71 | -------------------------------------------------------------------------------- /scripts/generateRandomData.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import random, sys, math 4 | 5 | nFiles = 1 6 | if len(sys.argv) < 4: 7 | print 'usage: python generateRandomData.py filename d N [nFiles]' 8 | sys.exit(1) 9 | else: 10 | filename = sys.argv[1] 11 | d = int(sys.argv[2]) 12 | N = int(sys.argv[3]) 13 | if len(sys.argv) > 4: 14 | nFiles = int(sys.argv[4]) 15 | 16 | MIN = -100 17 | MAX = 100 18 | 19 | valuesPerFile = int(math.ceil(float(N) / nFiles)) 20 | 21 | coeff = [random.uniform(-10, 10) for i in xrange(d)] 22 | for n in xrange(nFiles): 23 | if nFiles > 1: 24 | name = '%s_%d.dat' % (filename, n) 25 | else: 26 | name = '%s.dat' % filename 27 | 28 | f = open(name, 'w') 29 | 30 | if nFiles == 1 or n < nFiles - 1 or N % valuesPerFile == 0: 31 | nValues = valuesPerFile 32 | else: 33 | nValues = N % valuesPerFile 34 | 35 | f.write('%d %d\n' % (d, nValues)) 36 | 37 | for i in xrange(nValues): 38 | val = [random.randint(MIN, MAX) for i in xrange(d)] 39 | label = sum([coeff[i]*val[i] for i in xrange(d)]) 40 | label += random.gauss(0,100); 41 | 42 | for j in xrange(d): 43 | f.write('%d ' % val[j]) 44 | f.write('%d\n' % label) 45 | f.close() 46 | --------------------------------------------------------------------------------