heap.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717
  1. #include <functional>
  2. #include "types.hpp"
  3. #include "duoram.hpp"
  4. #include "cell.hpp"
  5. #include "rdpf.hpp"
  6. #include "shapes.hpp"
  7. #include "heap.hpp"
  8. // The protocol begins by adding an empty node at the end of the heap array.
  9. // The key observation is that after \heapinsert is complete, the only entries
  10. // that might change are the ones on the path from the root to this new node.
  11. // Further, since the number of entries in the heap is public, \emph{which} entries in $\database$ form this path is also public.
  12. // We form the accessible set $\pdatabase$ of the nodes from the root to the newly added (empty) node.
  13. // The next observation is that this path (from root to leaf) starts off sorted, and will end up with the new element $\insertval$ inserted into the correct position so as to keep the path sorted.
  14. // The path is of length $\lg \heapsize$, so we use our binary search to find the appropriate insertion position with a single IDPF of height $\lg \lg \heapsize$.
  15. // The advice bits of that IDPF will then be bit shares of a vector $\flag = [0,0,1,0,\dots,0]$ with the $1$ indicating the position at which the new value must be inserted.
  16. // The shares of $\flag$ are (locally) converted to shares of $\u = [0,0,1,1,\dots,1]$ by taking running XORs.
  17. // The bits of $\flag$ and $\u$ are used in $2 \lg \heapsize$ parallel Flag-Word multiplications to shift the elements greater than $\insertval$ down one position, and to write $\insertval$ into the resulting hole, with a single message of communication.
  18. // This Protocol 4 from PRAC: Round-Efficient 3-Party MPC for Dynamic Data Structures
  19. int MinHeap::insert_optimized(MPCTIO tio, yield_t & yield, RegAS val) {
  20. auto HeapArray = oram.flat(tio, yield);
  21. num_items++;
  22. typename Duoram<RegAS>::Path old_P(HeapArray, tio, yield, num_items);
  23. const RegXS foundidx = old_P.binary_search(val);
  24. size_t childindex = num_items;
  25. uint64_t height = std::ceil(std::log2(num_items + 1)) + 1;
  26. RegAS zero;
  27. zero.ashare = 0;
  28. HeapArray[childindex] = zero;
  29. typename Duoram<RegAS>::Path P(HeapArray, tio, yield, num_items);
  30. #ifdef VERBOSE
  31. uint64_t val_reconstruction = mpc_reconstruct(tio, yield, val, 64);
  32. std::cout << "val_reconstruction = " << val_reconstruction << std::endl;
  33. #endif
  34. uint64_t logheight = std::ceil(double(std::log2(height))) + 1;
  35. std::vector<RegBS> flag(height+1);
  36. std::vector<RegBS> u(height+1);
  37. typename Duoram<RegAS>::template OblivIndex<RegXS,1> oidx(tio, yield, foundidx, logheight);
  38. u = oidx.unit_vector(tio, yield, 1 << logheight, foundidx);
  39. #ifdef VERBOSE
  40. uint64_t foundidx_reconstruction = mpc_reconstruct(tio, yield, foundidx);
  41. std::cout << "foundidx_reconstruction = " << foundidx_reconstruction << std::endl;
  42. std::cout << std::endl << " =============== " << std::endl;
  43. for (size_t j = 0; j < height; ++j) {
  44. uint64_t reconstruction = mpc_reconstruct(tio, yield, u[j]);
  45. std::cout << " --->> u[" << j << "] = " << reconstruction << std::endl;
  46. }
  47. #endif
  48. for (size_t j = 0; j < height; ++j) {
  49. if(tio.player() !=2) {
  50. flag[j] = u[j];
  51. if(j > 0) u[j] = u[j] ^ u[j-1];
  52. }
  53. }
  54. #ifdef VERBOSE
  55. for (size_t j = 0; j < height; ++j) {
  56. uint64_t reconstruction = mpc_reconstruct(tio, yield, u[j]);
  57. std::cout << " --->> [0000111111]][" << j << "] = " << reconstruction << std::endl;
  58. }
  59. #endif
  60. RegAS * path = new RegAS[height+1];
  61. RegAS * w = new RegAS[height+1];
  62. RegAS * v = new RegAS[height+1];
  63. for (size_t j = 0; j < height+1; ++j) path[j] = P[j];
  64. std::vector<coro_t> coroutines;
  65. for (size_t j = 1; j < height+1; ++j) {
  66. coroutines.emplace_back(
  67. [&tio, w, u, path, j](yield_t &yield) {
  68. mpc_flagmult(tio, yield, w[j], u[j-1], (path[j-1]-path[j]));
  69. }
  70. );
  71. coroutines.emplace_back(
  72. [&tio, v, flag, val, path, j](yield_t &yield) {
  73. mpc_flagmult(tio, yield, v[j-1], flag[j-1], (val - path[j-1]));
  74. }
  75. );
  76. }
  77. run_coroutines(tio, coroutines);
  78. for (size_t j = 0; j < height; ++j) P[j] += (w[j] + v[j]);
  79. #ifdef VERBOSE
  80. std::cout << "\n\n=================Before===========\n\n";
  81. for (size_t j = 0; j < height-1; ++j) {
  82. auto path_rec = mpc_reconstruct(tio, yield, P[j]);
  83. std::cout << j << " --->: " << path_rec << std::endl;
  84. }
  85. std::cout << "\n\n============================\n\n";
  86. std::cout << "\n\n=================Aftter===========\n\n";
  87. for (size_t j = 0; j < height-1; ++j) {
  88. auto path_rec = mpc_reconstruct(tio, yield, P[j]);
  89. std::cout << j << " --->: " << path_rec << std::endl;
  90. }
  91. std::cout << "\n\n============================\n\n";
  92. #endif
  93. delete[] path;
  94. delete[] w;
  95. delete[] v;
  96. return 1;
  97. }
  98. // The insert protocol works as follows:
  99. // It adds a new element in the last entry of the array
  100. // From the leaf (the element added), compare with its parent (1 oblivious compare)
  101. // This Protocol 3 from PRAC: Round-Efficient 3-Party MPC for Dynamic Data Structures
  102. int MinHeap::insert(MPCTIO tio, yield_t & yield, RegAS val) {
  103. auto HeapArray = oram.flat(tio, yield);
  104. num_items++;
  105. size_t childindex = num_items;
  106. size_t parentindex = childindex / 2;
  107. #ifdef VERBOSE
  108. std::cout << "childindex = " << childindex << std::endl;
  109. std::cout << "parentindex = " << parentindex << std::endl;
  110. #endif
  111. HeapArray[num_items] = val;
  112. typename Duoram<RegAS>::Path P(HeapArray, tio, yield, childindex);
  113. while (parentindex > 0) {
  114. RegAS sharechild = HeapArray[childindex];
  115. RegAS shareparent = HeapArray[parentindex];
  116. CDPF cdpf = tio.cdpf(yield);
  117. RegAS diff = sharechild - shareparent;
  118. auto[lt, eq, gt] = cdpf.compare(tio, yield, diff, tio.aes_ops());
  119. auto lteq = lt ^ eq;
  120. mpc_oswap(tio, yield, sharechild, shareparent, lteq, 64);
  121. HeapArray[childindex] = sharechild;
  122. HeapArray[parentindex] = shareparent;
  123. childindex = parentindex;
  124. parentindex = parentindex / 2;
  125. }
  126. return 1;
  127. }
  128. // This is only for debugging purposes
  129. // This function just verifies that the heap property is satisfied
  130. int MinHeap::verify_heap_property(MPCTIO tio, yield_t & yield) {
  131. #ifdef VERBOSE
  132. std::cout << std::endl << std::endl << "verify_heap_property is being called " << std::endl;
  133. #endif
  134. auto HeapArray = oram.flat(tio, yield);
  135. uint64_t * heapreconstruction = new uint64_t[num_items + 1];
  136. for (size_t j = 0; j < num_items; ++j) {
  137. heapreconstruction[j] = mpc_reconstruct(tio, yield, HeapArray[j]);
  138. //#ifdef VERBOSE
  139. if(tio.player() < 2) std::cout << j << " -----> heapreconstruction[" << j << "] = " << heapreconstruction[j] << std::endl;
  140. //#endif
  141. }
  142. for (size_t j = 1; j < num_items / 2; ++j) {
  143. if (heapreconstruction[j] > heapreconstruction[2 * j]) {
  144. std::cout << "heap property failure\n\n";
  145. std::cout << "j = " << j << std::endl;
  146. std::cout << heapreconstruction[j] << std::endl;
  147. std::cout << "2*j = " << 2 * j << std::endl;
  148. std::cout << heapreconstruction[2 * j] << std::endl;
  149. }
  150. if (heapreconstruction[j] > heapreconstruction[2 * j + 1]) {
  151. std::cout << "heap property failure\n\n";
  152. std::cout << "j = " << j << std::endl;
  153. std::cout << heapreconstruction[j] << std::endl;
  154. std::cout << "2*j + 1 = " << 2 * j + 1<< std::endl;
  155. std::cout << heapreconstruction[2 * j + 1] << std::endl;
  156. }
  157. assert(heapreconstruction[j] <= heapreconstruction[2 * j]);
  158. assert(heapreconstruction[j] <= heapreconstruction[2 * j + 1]);
  159. }
  160. delete [] heapreconstruction;
  161. return 1;
  162. }
  163. //This is only for debugging purposes
  164. void verify_parent_children_heaps(MPCTIO tio, yield_t & yield, RegAS parent, RegAS leftchild, RegAS rightchild) {
  165. uint64_t parent_reconstruction = mpc_reconstruct(tio, yield, parent);
  166. uint64_t leftchild_reconstruction = mpc_reconstruct(tio, yield, leftchild);
  167. uint64_t rightchild_reconstruction = mpc_reconstruct(tio, yield, rightchild);
  168. #ifdef VERBOSE
  169. std::cout << "parent_reconstruction = " << parent_reconstruction << std::endl;
  170. std::cout << "leftchild_reconstruction = " << leftchild_reconstruction << std::endl;
  171. std::cout << "rightchild_reconstruction = " << rightchild_reconstruction << std::endl << std::endl << std::endl;
  172. #endif
  173. assert(parent_reconstruction <= leftchild_reconstruction);
  174. assert(parent_reconstruction <= rightchild_reconstruction);
  175. }
  176. // This Protocol 6 from PRAC: Round-Efficient 3-Party MPC for Dynamic Data Structures
  177. RegXS MinHeap::restore_heap_property(MPCIO & mpcio, MPCTIO tio, yield_t & yield, RegXS index) {
  178. RegAS smallest;
  179. auto HeapArray = oram.flat(tio, yield);
  180. mpcio.reset_stats();
  181. tio.reset_lamport();
  182. RegXS leftchildindex = index;
  183. leftchildindex = index << 1;
  184. RegXS rightchildindex;
  185. rightchildindex.xshare = leftchildindex.xshare ^ (tio.player());
  186. RegAS parent;
  187. RegAS leftchild;
  188. RegAS rightchild;
  189. #ifdef VERBOSE
  190. auto index_reconstruction = mpc_reconstruct(tio, yield, index);
  191. auto leftchildindex_reconstruction = mpc_reconstruct(tio, yield, leftchildindex);
  192. auto rightchildindex_reconstruction = mpc_reconstruct(tio, yield, rightchildindex);
  193. std::cout << "index_reconstruction = " << index_reconstruction << std::endl;
  194. std::cout << "leftchildindex_reconstruction = " << leftchildindex_reconstruction << std::endl;
  195. std::cout << "rightchildindex_reconstruction = " << rightchildindex_reconstruction << std::endl;
  196. #endif
  197. std::vector<coro_t> coroutines_read;
  198. coroutines_read.emplace_back(
  199. [&tio, &parent, &HeapArray, index](yield_t &yield) {
  200. auto Acoro = HeapArray.context(yield);
  201. parent = Acoro[index];
  202. }
  203. );
  204. coroutines_read.emplace_back(
  205. [&tio, &HeapArray, &leftchild, leftchildindex](yield_t &yield) {
  206. auto Acoro = HeapArray.context(yield);
  207. leftchild = Acoro[leftchildindex];
  208. }
  209. );
  210. coroutines_read.emplace_back(
  211. [&tio, &rightchild, &HeapArray, rightchildindex](yield_t &yield) {
  212. auto Acoro = HeapArray.context(yield);
  213. rightchild = Acoro[rightchildindex];
  214. }
  215. );
  216. run_coroutines(tio, coroutines_read);
  217. CDPF cdpf = tio.cdpf(yield);
  218. auto[lt_c, eq_c, gt_c] = cdpf.compare(tio, yield, leftchild - rightchild, tio.aes_ops());
  219. auto lteq = lt_c ^ eq_c;
  220. RegXS smallerindex;
  221. RegAS smallerchild;
  222. run_coroutines(tio, [&tio, &smallerindex, lteq, rightchildindex, leftchildindex](yield_t &yield) {
  223. mpc_select(tio, yield, smallerindex, lteq, rightchildindex, leftchildindex, 64);
  224. }, [&tio, &smallerchild, lteq, rightchild, leftchild](yield_t &yield) {
  225. mpc_select(tio, yield, smallerchild, lteq, rightchild, leftchild, 64);
  226. }
  227. );
  228. CDPF cdpf0 = tio.cdpf(yield);
  229. auto[lt_p, eq_p, gt_p] = cdpf0.compare(tio, yield, smallerchild - parent, tio.aes_ops());
  230. auto lt_p_eq_p = lt_p ^ eq_p;
  231. RegBS ltlt1;
  232. mpc_and(tio, yield, ltlt1, lteq, lt_p_eq_p);
  233. RegAS update_index_by, update_leftindex_by;
  234. run_coroutines(tio, [&tio, &update_leftindex_by, ltlt1, parent, leftchild](yield_t &yield) {
  235. mpc_flagmult(tio, yield, update_leftindex_by, ltlt1, (parent - leftchild), 64);
  236. }, [&tio, &update_index_by, lt_p, parent, smallerchild](yield_t &yield) {
  237. mpc_flagmult(tio, yield, update_index_by, lt_p, smallerchild - parent, 64);
  238. }
  239. );
  240. std::vector<coro_t> coroutines;
  241. coroutines.emplace_back(
  242. [&tio, &HeapArray, index, update_index_by](yield_t &yield) {
  243. auto Acoro = HeapArray.context(yield);
  244. Acoro[index] += update_index_by;
  245. }
  246. );
  247. coroutines.emplace_back(
  248. [&tio, &HeapArray, leftchildindex, update_leftindex_by](yield_t &yield) {
  249. auto Acoro = HeapArray.context(yield);
  250. Acoro[leftchildindex] += update_leftindex_by;
  251. }
  252. );
  253. coroutines.emplace_back(
  254. [&tio, &HeapArray, rightchildindex, update_index_by, update_leftindex_by](yield_t &yield) {
  255. auto Acoro = HeapArray.context(yield);
  256. Acoro[rightchildindex] += -(update_index_by + update_leftindex_by);
  257. }
  258. );
  259. run_coroutines(tio, coroutines);
  260. #ifdef DEBUG
  261. verify_parent_children_heaps(tio, yield, HeapArray[index], HeapArray[leftchildindex] , HeapArray[rightchildindex]);
  262. #endif
  263. return smallerindex;
  264. }
  265. // This Protocol 7 from PRAC: Round-Efficient 3-Party MPC for Dynamic Data Structures
  266. auto MinHeap::restore_heap_property_optimized(MPCTIO tio, yield_t & yield, RegXS index, size_t layer, size_t depth, typename Duoram < RegAS > ::template OblivIndex < RegXS, 3 > (oidx)) {
  267. auto HeapArray = oram.flat(tio, yield);
  268. RegXS leftchildindex = index;
  269. leftchildindex = index << 1;
  270. RegXS rightchildindex;
  271. rightchildindex.xshare = leftchildindex.xshare ^ (tio.player());
  272. typename Duoram < RegAS > ::Flat P(HeapArray, tio, yield, 1 << layer, 1 << layer);
  273. typename Duoram < RegAS > ::Flat C(HeapArray, tio, yield, 2 << layer, 2 << layer);
  274. typename Duoram < RegAS > ::Stride L(C, tio, yield, 0, 2);
  275. typename Duoram < RegAS > ::Stride R(C, tio, yield, 1, 2);
  276. RegAS parent_tmp, leftchild_tmp, rightchild_tmp;
  277. std::vector<coro_t> coroutines_read;
  278. coroutines_read.emplace_back(
  279. [&tio, &parent_tmp, &P, &oidx](yield_t &yield) {
  280. auto Acoro = P.context(yield);
  281. parent_tmp = Acoro[oidx]; //inserted_val;
  282. });
  283. coroutines_read.emplace_back(
  284. [&tio, &L, &leftchild_tmp, &oidx](yield_t &yield) {
  285. auto Acoro = L.context(yield);
  286. leftchild_tmp = Acoro[oidx]; //inserted_val;
  287. });
  288. coroutines_read.emplace_back(
  289. [&tio, &R, &rightchild_tmp, &oidx](yield_t &yield) {
  290. auto Acoro = R.context(yield);
  291. rightchild_tmp = Acoro[oidx];
  292. });
  293. run_coroutines(tio, coroutines_read);
  294. CDPF cdpf = tio.cdpf(yield);
  295. auto[lt, eq, gt] = cdpf.compare(tio, yield, leftchild_tmp - rightchild_tmp, tio.aes_ops());
  296. auto lteq = lt ^ eq;
  297. RegXS smallerindex;
  298. RegAS smallerchild;
  299. run_coroutines(tio, [&tio, &smallerindex, lteq, rightchildindex, leftchildindex](yield_t &yield)
  300. { mpc_select(tio, yield, smallerindex, lteq, rightchildindex, leftchildindex, 64);;},
  301. [&tio, &smallerchild, lt, rightchild_tmp, leftchild_tmp](yield_t &yield)
  302. { mpc_select(tio, yield, smallerchild, lt, rightchild_tmp, leftchild_tmp, 64);;});
  303. CDPF cdpf0 = tio.cdpf(yield);
  304. auto[lt1, eq1, gt1] = cdpf0.compare(tio, yield, smallerchild - parent_tmp, tio.aes_ops());
  305. auto lt1eq1 = lt1 ^ eq1;
  306. RegBS ltlt1;
  307. mpc_and(tio, yield, ltlt1, lteq, lt1eq1);
  308. RegAS update_index_by, update_leftindex_by;
  309. run_coroutines(tio, [&tio, &update_leftindex_by, ltlt1, parent_tmp, leftchild_tmp](yield_t &yield)
  310. { mpc_flagmult(tio, yield, update_leftindex_by, ltlt1, (parent_tmp - leftchild_tmp), 64);},
  311. [&tio, &update_index_by, lt1eq1, parent_tmp, smallerchild](yield_t &yield)
  312. {mpc_flagmult(tio, yield, update_index_by, lt1eq1, smallerchild - parent_tmp, 64);}
  313. );
  314. std::vector<coro_t> coroutines;
  315. coroutines.emplace_back(
  316. [&tio, &P, &oidx, update_index_by](yield_t &yield) {
  317. auto Acoro = P.context(yield);
  318. Acoro[oidx] += update_index_by; //inserted_val;
  319. });
  320. coroutines.emplace_back(
  321. [&tio, &L, &oidx, update_leftindex_by](yield_t &yield) {
  322. auto Acoro = L.context(yield);
  323. Acoro[oidx] += update_leftindex_by; //inserted_val;
  324. });
  325. coroutines.emplace_back(
  326. [&tio, &R, &oidx, update_leftindex_by, update_index_by](yield_t &yield) {
  327. auto Acoro = R.context(yield);
  328. Acoro[oidx] += -(update_leftindex_by + update_index_by);
  329. });
  330. run_coroutines(tio, coroutines);
  331. return std::make_pair(smallerindex, gt);
  332. }
  333. void MinHeap::initialize(MPCTIO tio, yield_t & yield) {
  334. auto HeapArray = oram.flat(tio, yield);
  335. HeapArray.init(0x7fffffffffffff);
  336. }
  337. // This function simply initializes a heap with values 1,2,...,n
  338. // We use this function only to setup our heap
  339. // to do timing experiments on insert and extractmins
  340. void MinHeap::initialize_heap(MPCTIO tio, yield_t & yield) {
  341. auto HeapArray = oram.flat(tio, yield);
  342. std::vector<coro_t> coroutines;
  343. for (size_t j = 1; j <= num_items; ++j) {
  344. coroutines.emplace_back(
  345. [&tio, &HeapArray, j](yield_t &yield) {
  346. auto Acoro = HeapArray.context(yield);
  347. RegAS v;
  348. v.ashare = j * tio.player();
  349. Acoro[j] = v;
  350. }
  351. );
  352. }
  353. run_coroutines(tio, coroutines);
  354. }
  355. void MinHeap::print_heap(MPCTIO tio, yield_t & yield) {
  356. auto HeapArray = oram.flat(tio, yield);
  357. uint64_t * Pjreconstruction = new uint64_t[num_items + 1];
  358. for (size_t j = 0; j <= num_items; ++j) Pjreconstruction[j] = mpc_reconstruct(tio, yield, HeapArray[j]);
  359. for (size_t j = 0; j <= num_items; ++j) {
  360. if(2 * j < num_items) {
  361. std::cout << j << "-->> HeapArray[" << j << "] = " << std::dec << Pjreconstruction[j] << ", children are: " << Pjreconstruction[2 * j] << " and " << Pjreconstruction[2 * j + 1] << std::endl;
  362. } else {
  363. std::cout << j << "-->> HeapArray[" << j << "] = " << std::dec << Pjreconstruction[j] << " is a LEAF " << std::endl;
  364. }
  365. }
  366. delete[] Pjreconstruction;
  367. }
  368. auto MinHeap::restore_heap_property_at_root(MPCTIO tio, yield_t & yield, size_t index = 1) {
  369. auto HeapArray = oram.flat(tio, yield);
  370. RegAS parent = HeapArray[index];
  371. RegAS leftchild = HeapArray[2 * index];
  372. RegAS rightchild = HeapArray[2 * index + 1];
  373. CDPF cdpf = tio.cdpf(yield);
  374. auto[lt, eq, gt] = cdpf.compare(tio, yield, leftchild - rightchild, tio.aes_ops());
  375. auto lteq = lt ^ eq;
  376. RegAS smallerchild;
  377. mpc_select(tio, yield, smallerchild, lteq, rightchild, leftchild);
  378. RegXS smallerindex(lt);
  379. uint64_t leftchildindex = (2 * index);
  380. uint64_t rightchildindex = (2 * index) + 1;
  381. smallerindex = (RegXS(lteq) & leftchildindex) ^ (RegXS(gt) & rightchildindex);
  382. CDPF cdpf0 = tio.cdpf(yield);
  383. auto[lt1, eq1, gt1] = cdpf0.compare(tio, yield, smallerchild - parent, tio.aes_ops());
  384. auto lt1eq1 = lt1 ^ eq1;
  385. RegBS ltlt1;
  386. mpc_and(tio, yield, ltlt1, lteq, lt1eq1);
  387. RegAS update_index_by, update_leftindex_by;
  388. run_coroutines(tio, [&tio, &update_leftindex_by, ltlt1, parent, leftchild](yield_t &yield) {
  389. mpc_flagmult(tio, yield, update_leftindex_by, ltlt1, (parent - leftchild), 64);
  390. }, [&tio, &update_index_by, lt1eq1, parent, smallerchild](yield_t &yield) {
  391. mpc_flagmult(tio, yield, update_index_by, lt1eq1, smallerchild - parent, 64);
  392. }
  393. );
  394. std::vector<coro_t> coroutines;
  395. coroutines.emplace_back(
  396. [&tio, &HeapArray, index, update_index_by](yield_t &yield) {
  397. auto Acoro = HeapArray.context(yield);
  398. Acoro[index] += update_index_by;
  399. }
  400. );
  401. coroutines.emplace_back(
  402. [&tio, &HeapArray, leftchildindex, update_leftindex_by](yield_t &yield) {
  403. auto Acoro = HeapArray.context(yield);
  404. Acoro[leftchildindex] += update_leftindex_by;
  405. }
  406. );
  407. coroutines.emplace_back(
  408. [&tio, &HeapArray, rightchildindex, update_index_by, update_leftindex_by](yield_t &yield) {
  409. auto Acoro = HeapArray.context(yield);
  410. Acoro[rightchildindex] += -(update_index_by + update_leftindex_by);
  411. }
  412. );
  413. run_coroutines(tio, coroutines);
  414. #ifdef VERBOSE
  415. RegAS new_parent = HeapArray[index];
  416. RegAS new_left = HeapArray[leftchildindex];
  417. RegAS new_right = HeapArray[rightchildindex];
  418. uint64_t parent_R = mpc_reconstruct(tio, yield, new_parent);
  419. uint64_t left_R = mpc_reconstruct(tio, yield, new_left);
  420. uint64_t right_R = mpc_reconstruct(tio, yield, new_right);
  421. std::cout << "parent_R = " << parent_R << std::endl;
  422. std::cout << "left_R = " << left_R << std::endl;
  423. std::cout << "right_R = " << right_R << std::endl;
  424. #endif
  425. #ifdef DEBUG
  426. verify_parent_children_heaps(tio, yield, HeapArray[index], HeapArray[leftchildindex] , HeapArray[rightchildindex]);
  427. #endif
  428. return std::make_pair(smallerindex, gt);
  429. }
  430. // This Protocol 5 from PRAC: Round-Efficient 3-Party MPC for Dynamic Data Structures
  431. // Like in the paper, there is only version of extract_min
  432. // the optimized version calls the optimized restore_heap_property
  433. RegAS MinHeap::extract_min(MPCIO & mpcio, MPCTIO tio, yield_t & yield, int is_optimized) {
  434. size_t height = std::log2(num_items);
  435. RegAS minval;
  436. auto HeapArray = oram.flat(tio, yield);
  437. minval = HeapArray[1];
  438. HeapArray[1] = RegAS(HeapArray[num_items]);
  439. num_items--;
  440. auto outroot = restore_heap_property_at_root(tio, yield);
  441. RegXS smaller = outroot.first;
  442. if(is_optimized > 0) {
  443. typename Duoram < RegAS > ::template OblivIndex < RegXS, 3 > oidx(tio, yield, height);
  444. oidx.incr(outroot.second);
  445. for (size_t i = 0; i < height-1; ++i) {
  446. auto out = restore_heap_property_optimized(tio, yield, smaller, i + 1, height, typename Duoram < RegAS > ::template OblivIndex < RegXS, 3 > (oidx));
  447. smaller = out.first;
  448. oidx.incr(out.second);
  449. }
  450. }
  451. if(is_optimized == 0) {
  452. for (size_t i = 0; i < height - 1; ++i) {
  453. smaller = restore_heap_property(mpcio, tio, yield, smaller);
  454. }
  455. }
  456. return minval;
  457. }
  458. // This function is not used in the evaluation in PRAC: Round-Efficient 3-Party MPC for Dynamic Data Structures
  459. // This function is called by heapify which takes in a random array and turns it into a heap
  460. void MinHeap::heapify_at_level(MPCIO & mpcio, MPCTIO tio, yield_t & yield, size_t index = 1) {
  461. auto outroot = restore_heap_property_at_root(tio, yield, index);
  462. RegXS smaller = outroot.first;
  463. #ifdef VERBOSE
  464. uint64_t smaller_rec = mpc_reconstruct(tio, yield, smaller, 64);
  465. std::cout << "smaller_rec = " << smaller_rec << std::endl;
  466. std::cout << "num_items = " << num_items << std::endl;
  467. std::cout << "index = " << index << std::endl;
  468. #endif
  469. size_t height = std::log2(num_items) - std::floor(log2(index)) ;
  470. #ifdef VERBOSE
  471. std::cout << "height = " << height << std::endl << "===================" << std::endl;
  472. #endif
  473. for (size_t i = 0; i < height - 1; ++i) {
  474. #ifdef VERBOSE
  475. std::cout << "index = " << index << ", i = " << i << std::endl;
  476. uint64_t smaller_rec = mpc_reconstruct(tio, yield, smaller, 64);
  477. std::cout << "[inside loop] smaller_rec = " << smaller_rec << std::endl;
  478. #endif
  479. smaller = restore_heap_property(mpcio, tio, yield, smaller);
  480. }
  481. }
  482. // This function is not used in the evaluation in PRAC: Round-Efficient 3-Party MPC for Dynamic Data Structures
  483. // This function takes in a random array turns into a heap
  484. void MinHeap::heapify(MPCIO & mpcio, MPCTIO tio, yield_t & yield) {
  485. size_t startIdx = ((num_items + 1) / 2) - 1;
  486. for (size_t i = startIdx; i >= 1; i--) {
  487. heapify_at_level(mpcio, tio, yield, i);
  488. }
  489. }
  490. void Heap(MPCIO & mpcio,
  491. const PRACOptions & opts, char ** args) {
  492. // nbits_t depth = atoi(args[0]);
  493. // nbits_t heapdepth = atoi(args[1]);
  494. // size_t n_inserts = atoi(args[2]);
  495. // size_t n_extracts = atoi(args[3]);
  496. // int is_optimized = atoi(args[4]);
  497. // int run_sanity = atoi(args[5]);
  498. int argc = 12;
  499. int maxdepth = 0;
  500. int heapdepth = 0;
  501. size_t n_inserts = 0;
  502. size_t n_extracts = 0;
  503. int is_optimized = 0;
  504. int run_sanity = 0;
  505. // Process command line arguments
  506. for (int i = 0; i < argc; i += 2) {
  507. std::string option = args[i];
  508. if (option == "-m" && i + 1 < argc) {
  509. maxdepth = std::atoi(args[i + 1]);
  510. } else if (option == "-d" && i + 1 < argc) {
  511. heapdepth = std::atoi(args[i + 1]);
  512. } else if (option == "-i" && i + 1 < argc) {
  513. n_inserts = std::atoi(args[i + 1]);
  514. } else if (option == "-e" && i + 1 < argc) {
  515. n_extracts = std::atoi(args[i + 1]);
  516. } else if (option == "-opt" && i + 1 < argc) {
  517. is_optimized = std::atoi(args[i + 1]);
  518. } else if (option == "-s" && i + 1 < argc) {
  519. run_sanity = std::atoi(args[i + 1]);
  520. }
  521. }
  522. // Use the values
  523. std::cout << "maxdepth: " << maxdepth << std::endl;
  524. std::cout << "heapdepth: " << heapdepth << std::endl;
  525. std::cout << "n_inserts: " << n_inserts << std::endl;
  526. std::cout << "n_extracts: " << n_extracts << std::endl;
  527. std::cout << "is_optimized: " << is_optimized << std::endl;
  528. std::cout << "run_sanity: " << run_sanity << std::endl;
  529. // if ( * args) {
  530. // depth = atoi( * args);
  531. // ++args;
  532. // }
  533. // if ( * args) {
  534. // items = atoi( * args);
  535. // ++args;
  536. // }
  537. MPCTIO tio(mpcio, 0, opts.num_threads);
  538. run_coroutines(tio, [ & tio, maxdepth, heapdepth, n_inserts, n_extracts, is_optimized, run_sanity, &mpcio](yield_t & yield) {
  539. size_t size = size_t(1) << maxdepth;
  540. MinHeap tree(tio.player(), size);
  541. tree.initialize(tio, yield);
  542. tree.num_items = (size_t(1) << heapdepth) - 1;
  543. tree.initialize_heap(tio, yield);
  544. std::cout << "\n===== Init Stats =====\n";
  545. tio.sync_lamport();
  546. mpcio.dump_stats(std::cout);
  547. mpcio.reset_stats();
  548. tio.reset_lamport();
  549. for (size_t j = 0; j < n_inserts; ++j) {
  550. RegAS inserted_val;
  551. inserted_val.randomize(10);
  552. #ifdef VERBOSE
  553. inserted_val.ashare = inserted_val.ashare;
  554. uint64_t inserted_val_rec = mpc_reconstruct(tio, yield, inserted_val, 64);
  555. std::cout << "inserted_val_rec = " << inserted_val_rec << std::endl << std::endl;
  556. #endif
  557. if(is_optimized > 0) tree.insert_optimized(tio, yield, inserted_val);
  558. if(is_optimized == 0) tree.insert(tio, yield, inserted_val);
  559. }
  560. std::cout << "\n===== Insert Stats =====\n";
  561. tio.sync_lamport();
  562. mpcio.dump_stats(std::cout);
  563. if(run_sanity == 1 && n_inserts != 0) tree.verify_heap_property(tio, yield);
  564. mpcio.reset_stats();
  565. tio.reset_lamport();
  566. #ifdef VERBOSE
  567. tree.print_heap(tio, yield);
  568. #endif
  569. for (size_t j = 0; j < n_extracts; ++j) {
  570. tree.extract_min(mpcio, tio, yield, is_optimized);
  571. #ifdef VERBOSE
  572. RegAS minval = tree.extract_min(mpcio, tio, yield, is_optimized);
  573. uint64_t minval_reconstruction = mpc_reconstruct(tio, yield, minval, 64);
  574. std::cout << "minval_reconstruction = " << minval_reconstruction << std::endl;
  575. #endif
  576. #ifdef DEBUG
  577. tree.verify_heap_property(tio, yield);
  578. #endif
  579. #ifdef VERBOSE
  580. tree.print_heap(tio, yield);
  581. #endif
  582. }
  583. std::cout << "\n===== Extract Min Stats =====\n";
  584. tio.sync_lamport();
  585. mpcio.dump_stats(std::cout);
  586. #ifdef VERBOSE
  587. tree.print_heap(tio, yield);
  588. #endif
  589. if(run_sanity == 1 && n_extracts != 0) tree.verify_heap_property(tio, yield);
  590. }
  591. );
  592. }