Browse Source

Merge branch 'main' into avadapal/heaps

avadapal 2 years ago
parent
commit
87bac90199
10 changed files with 460 additions and 178 deletions
  1. 55 25
      mpcio.cpp
  2. 23 2
      mpcio.hpp
  3. 33 8
      mpcio.tcc
  4. 58 0
      mpcops.cpp
  5. 20 0
      mpcops.hpp
  6. 95 63
      online.cpp
  7. 35 18
      preproc.cpp
  8. 50 16
      rdpf.hpp
  9. 87 46
      rdpf.tcc
  10. 4 0
      types.hpp

+ 55 - 25
mpcio.cpp

@@ -234,12 +234,12 @@ void MPCIO::dump_stats(std::ostream &os)
 // TVA is a tuple of vectors of arrays of PreCompStorage
 template <nbits_t WIDTH, typename TVA>
 static void rdpfstorage_init(TVA &storage, unsigned player,
-    ProcessingMode mode, unsigned num_threads)
+    ProcessingMode mode, unsigned num_threads, bool incremental)
 {
     auto &VA = std::get<WIDTH-1>(storage);
     VA.resize(num_threads);
-    char prefix[11];
-    strcpy(prefix, "rdpf");
+    char prefix[12];
+    strcpy(prefix, incremental ? "irdpf" : "rdpf");
     if (WIDTH > 1) {
         sprintf(prefix+strlen(prefix), "%d_", WIDTH);
     }
@@ -253,13 +253,13 @@ static void rdpfstorage_init(TVA &storage, unsigned player,
 // TVA is a tuple of vectors of arrays of PreCompStorage
 template <nbits_t WIDTH, typename TVA>
 static void rdpfstorage_dumpstats(std::ostream &os, TVA &storage,
-    size_t thread_num)
+    size_t thread_num, bool incremental)
 {
     auto &VA = std::get<WIDTH-1>(storage);
     for (nbits_t depth=1; depth<=ADDRESS_MAX_BITS; ++depth) {
         size_t cnt = VA[thread_num][depth-1].get_stats();
         if (cnt > 0) {
-            os << " r" << int(depth);
+            os << (incremental ? " i" : " r") << int(depth);
             if (WIDTH > 1) {
                 os << "." << int(WIDTH);
             }
@@ -296,11 +296,16 @@ MPCPeerIO::MPCPeerIO(unsigned player, ProcessingMode mode,
     for (unsigned i=0; i<num_threads; ++i) {
         valselecttriples.emplace_back(player, mode, "selects", i);
     }
-    rdpfstorage_init<1>(rdpftriples, player, mode, num_threads);
-    rdpfstorage_init<2>(rdpftriples, player, mode, num_threads);
-    rdpfstorage_init<3>(rdpftriples, player, mode, num_threads);
-    rdpfstorage_init<4>(rdpftriples, player, mode, num_threads);
-    rdpfstorage_init<5>(rdpftriples, player, mode, num_threads);
+    rdpfstorage_init<1>(rdpftriples, player, mode, num_threads, false);
+    rdpfstorage_init<2>(rdpftriples, player, mode, num_threads, false);
+    rdpfstorage_init<3>(rdpftriples, player, mode, num_threads, false);
+    rdpfstorage_init<4>(rdpftriples, player, mode, num_threads, false);
+    rdpfstorage_init<5>(rdpftriples, player, mode, num_threads, false);
+    rdpfstorage_init<1>(irdpftriples, player, mode, num_threads, true);
+    rdpfstorage_init<2>(irdpftriples, player, mode, num_threads, true);
+    rdpfstorage_init<3>(irdpftriples, player, mode, num_threads, true);
+    rdpfstorage_init<4>(irdpftriples, player, mode, num_threads, true);
+    rdpfstorage_init<5>(irdpftriples, player, mode, num_threads, true);
     for (unsigned i=0; i<num_threads; ++i) {
         cdpfs.emplace_back(player, mode, "cdpf", i);
     }
@@ -336,11 +341,16 @@ void MPCPeerIO::dump_precomp_stats(std::ostream &os)
         if (cnt > 0) {
             os << " s:" << cnt;
         }
-        rdpfstorage_dumpstats<1>(os, rdpftriples, i);
-        rdpfstorage_dumpstats<2>(os, rdpftriples, i);
-        rdpfstorage_dumpstats<3>(os, rdpftriples, i);
-        rdpfstorage_dumpstats<4>(os, rdpftriples, i);
-        rdpfstorage_dumpstats<5>(os, rdpftriples, i);
+        rdpfstorage_dumpstats<1>(os, rdpftriples, i, false);
+        rdpfstorage_dumpstats<2>(os, rdpftriples, i, false);
+        rdpfstorage_dumpstats<3>(os, rdpftriples, i, false);
+        rdpfstorage_dumpstats<4>(os, rdpftriples, i, false);
+        rdpfstorage_dumpstats<5>(os, rdpftriples, i, false);
+        rdpfstorage_dumpstats<1>(os, irdpftriples, i, true);
+        rdpfstorage_dumpstats<2>(os, irdpftriples, i, true);
+        rdpfstorage_dumpstats<3>(os, irdpftriples, i, true);
+        rdpfstorage_dumpstats<4>(os, irdpftriples, i, true);
+        rdpfstorage_dumpstats<5>(os, irdpftriples, i, true);
         cnt = cdpfs[i].get_stats();
         if (cnt > 0) {
             os << " c:" << cnt;
@@ -361,6 +371,11 @@ void MPCPeerIO::reset_precomp_stats()
         rdpfstorage_resetstats<3>(rdpftriples, i);
         rdpfstorage_resetstats<4>(rdpftriples, i);
         rdpfstorage_resetstats<5>(rdpftriples, i);
+        rdpfstorage_resetstats<1>(irdpftriples, i);
+        rdpfstorage_resetstats<2>(irdpftriples, i);
+        rdpfstorage_resetstats<3>(irdpftriples, i);
+        rdpfstorage_resetstats<4>(irdpftriples, i);
+        rdpfstorage_resetstats<5>(irdpftriples, i);
     }
 }
 
@@ -376,11 +391,16 @@ MPCServerIO::MPCServerIO(ProcessingMode mode,
         std::deque<tcp::socket> &p1socks) :
     MPCIO(2, mode, p0socks.size())
 {
-    rdpfstorage_init<1>(rdpfpairs, player, mode, num_threads);
-    rdpfstorage_init<2>(rdpfpairs, player, mode, num_threads);
-    rdpfstorage_init<3>(rdpfpairs, player, mode, num_threads);
-    rdpfstorage_init<4>(rdpfpairs, player, mode, num_threads);
-    rdpfstorage_init<5>(rdpfpairs, player, mode, num_threads);
+    rdpfstorage_init<1>(rdpfpairs, player, mode, num_threads, false);
+    rdpfstorage_init<2>(rdpfpairs, player, mode, num_threads, false);
+    rdpfstorage_init<3>(rdpfpairs, player, mode, num_threads, false);
+    rdpfstorage_init<4>(rdpfpairs, player, mode, num_threads, false);
+    rdpfstorage_init<5>(rdpfpairs, player, mode, num_threads, false);
+    rdpfstorage_init<1>(irdpfpairs, player, mode, num_threads, true);
+    rdpfstorage_init<2>(irdpfpairs, player, mode, num_threads, true);
+    rdpfstorage_init<3>(irdpfpairs, player, mode, num_threads, true);
+    rdpfstorage_init<4>(irdpfpairs, player, mode, num_threads, true);
+    rdpfstorage_init<5>(irdpfpairs, player, mode, num_threads, true);
     for (unsigned i=0; i<num_threads; ++i) {
         p0ios.emplace_back(std::move(p0socks[i]), "p0", i);
     }
@@ -396,11 +416,16 @@ void MPCServerIO::dump_precomp_stats(std::ostream &os)
             os << " ";
         }
         os << "T" << i;
-        rdpfstorage_dumpstats<1>(os, rdpfpairs, i);
-        rdpfstorage_dumpstats<2>(os, rdpfpairs, i);
-        rdpfstorage_dumpstats<3>(os, rdpfpairs, i);
-        rdpfstorage_dumpstats<4>(os, rdpfpairs, i);
-        rdpfstorage_dumpstats<5>(os, rdpfpairs, i);
+        rdpfstorage_dumpstats<1>(os, rdpfpairs, i, false);
+        rdpfstorage_dumpstats<2>(os, rdpfpairs, i, false);
+        rdpfstorage_dumpstats<3>(os, rdpfpairs, i, false);
+        rdpfstorage_dumpstats<4>(os, rdpfpairs, i, false);
+        rdpfstorage_dumpstats<5>(os, rdpfpairs, i, false);
+        rdpfstorage_dumpstats<1>(os, irdpfpairs, i, true);
+        rdpfstorage_dumpstats<2>(os, irdpfpairs, i, true);
+        rdpfstorage_dumpstats<3>(os, irdpfpairs, i, true);
+        rdpfstorage_dumpstats<4>(os, irdpfpairs, i, true);
+        rdpfstorage_dumpstats<5>(os, irdpfpairs, i, true);
     }
     os << "\n";
 }
@@ -413,6 +438,11 @@ void MPCServerIO::reset_precomp_stats()
         rdpfstorage_resetstats<3>(rdpfpairs, i);
         rdpfstorage_resetstats<4>(rdpfpairs, i);
         rdpfstorage_resetstats<5>(rdpfpairs, i);
+        rdpfstorage_resetstats<1>(irdpfpairs, i);
+        rdpfstorage_resetstats<2>(irdpfpairs, i);
+        rdpfstorage_resetstats<3>(irdpfpairs, i);
+        rdpfstorage_resetstats<4>(irdpfpairs, i);
+        rdpfstorage_resetstats<5>(irdpfpairs, i);
     }
 }
 

+ 23 - 2
mpcio.hpp

@@ -223,12 +223,22 @@ struct MPCPeerIO : public MPCIO {
     using RDPFPrecomps =
         std::vector<std::array<
             PreCompStorage<RDPFTriple<WIDTH>, RDPFTripleName>,ADDRESS_MAX_BITS>>;
+    template <nbits_t WIDTH>
+    using IRDPFPrecomps =
+        std::vector<std::array<
+            PreCompStorage<RDPFTriple<WIDTH>, IRDPFTripleName>,ADDRESS_MAX_BITS>>;
     std::tuple<
         RDPFPrecomps<1>,
         RDPFPrecomps<2>,
         RDPFPrecomps<3>,
         RDPFPrecomps<4>,
         RDPFPrecomps<5>> rdpftriples;
+    std::tuple<
+        IRDPFPrecomps<1>,
+        IRDPFPrecomps<2>,
+        IRDPFPrecomps<3>,
+        IRDPFPrecomps<4>,
+        IRDPFPrecomps<5>> irdpftriples;
 
     MPCPeerIO(unsigned player, ProcessingMode mode,
             std::deque<tcp::socket> &peersocks,
@@ -255,12 +265,22 @@ struct MPCServerIO : public MPCIO {
     using RDPFPrecomps =
         std::vector<std::array<
             PreCompStorage<RDPFPair<WIDTH>, RDPFPairName>,ADDRESS_MAX_BITS>>;
+    template <nbits_t WIDTH>
+    using IRDPFPrecomps =
+        std::vector<std::array<
+            PreCompStorage<RDPFPair<WIDTH>, IRDPFPairName>,ADDRESS_MAX_BITS>>;
     std::tuple<
         RDPFPrecomps<1>,
         RDPFPrecomps<2>,
         RDPFPrecomps<3>,
         RDPFPrecomps<4>,
         RDPFPrecomps<5>> rdpfpairs;
+    std::tuple<
+        IRDPFPrecomps<1>,
+        IRDPFPrecomps<2>,
+        IRDPFPrecomps<3>,
+        IRDPFPrecomps<4>,
+        IRDPFPrecomps<5>> irdpfpairs;
 
     MPCServerIO(ProcessingMode mode,
             std::deque<tcp::socket> &p0socks,
@@ -413,10 +433,11 @@ public:
     // Computational peers call:
     template <nbits_t WIDTH = 1>
     RDPFTriple<WIDTH> rdpftriple(yield_t &yield, nbits_t depth,
-        bool keep_expansion = true);
+        bool incremental = false, bool keep_expansion = true);
     // The server calls:
     template <nbits_t WIDTH = 1>
-    RDPFPair<WIDTH> rdpfpair(yield_t &yield, nbits_t depth);
+    RDPFPair<WIDTH> rdpfpair(yield_t &yield, nbits_t depth,
+        bool incremental = false);
     // Anyone can call:
     CDPF cdpf(yield_t &yield);
 

+ 33 - 8
mpcio.tcc

@@ -55,20 +55,32 @@ void PreCompStorage<T,N>::get(T& nextval)
 // rdpfpair() at the same time
 template <nbits_t WIDTH>
 RDPFTriple<WIDTH> MPCTIO::rdpftriple(yield_t &yield, nbits_t depth,
-    bool keep_expansion)
+    bool incremental, bool keep_expansion)
 {
     assert(mpcio.player < 2);
     RDPFTriple<WIDTH> val;
 
     MPCPeerIO &mpcpio = static_cast<MPCPeerIO&>(mpcio);
     if (mpcio.mode == MODE_ONLINE) {
-        std::get<WIDTH-1>(mpcpio.rdpftriples)[thread_num][depth-1].get(val);
+        if (incremental) {
+            std::get<WIDTH-1>(mpcpio.irdpftriples)
+                [thread_num][depth-1].get(val);
+        } else {
+            std::get<WIDTH-1>(mpcpio.rdpftriples)
+                [thread_num][depth-1].get(val);
+        }
     } else {
         val = RDPFTriple<WIDTH>(*this, yield, depth,
-            keep_expansion);
+            incremental, keep_expansion);
         iostream_server() <<
             val.dpf[(mpcio.player == 0) ? 1 : 2];
-        std::get<WIDTH-1>(mpcpio.rdpftriples)[thread_num][depth-1].inc();
+        if (incremental) {
+            std::get<WIDTH-1>(mpcpio.irdpftriples)
+                [thread_num][depth-1].inc();
+        } else {
+            std::get<WIDTH-1>(mpcpio.rdpftriples)
+                [thread_num][depth-1].inc();
+        }
         yield();
     }
     return val;
@@ -77,20 +89,33 @@ RDPFTriple<WIDTH> MPCTIO::rdpftriple(yield_t &yield, nbits_t depth,
 // Only the server calls this; the computational peers should be calling
 // rdpftriple() at the same time
 template <nbits_t WIDTH>
-RDPFPair<WIDTH> MPCTIO::rdpfpair(yield_t &yield, nbits_t depth)
+RDPFPair<WIDTH> MPCTIO::rdpfpair(yield_t &yield, nbits_t depth,
+    bool incremental)
 {
     assert(mpcio.player == 2);
     RDPFPair<WIDTH> val;
 
     MPCServerIO &mpcsrvio = static_cast<MPCServerIO&>(mpcio);
     if (mpcio.mode == MODE_ONLINE) {
-        std::get<WIDTH-1>(mpcsrvio.rdpfpairs)[thread_num][depth-1].get(val);
+        if (incremental) {
+            std::get<WIDTH-1>(mpcsrvio.irdpfpairs)
+                [thread_num][depth-1].get(val);
+        } else {
+            std::get<WIDTH-1>(mpcsrvio.rdpfpairs)
+                [thread_num][depth-1].get(val);
+        }
     } else {
-        RDPFTriple<WIDTH> trip(*this, yield, depth, true);
+        RDPFTriple<WIDTH> trip(*this, yield, depth, incremental, true);
         yield();
         iostream_p0() >> val.dpf[0];
         iostream_p1() >> val.dpf[1];
-        std::get<WIDTH-1>(mpcsrvio.rdpfpairs)[thread_num][depth-1].inc();
+        if (incremental) {
+            std::get<WIDTH-1>(mpcsrvio.irdpfpairs)
+                [thread_num][depth-1].inc();
+        } else {
+            std::get<WIDTH-1>(mpcsrvio.rdpfpairs)
+                [thread_num][depth-1].inc();
+        }
     }
     return val;
 }

+ 58 - 0
mpcops.cpp

@@ -265,6 +265,64 @@ void mpc_xs_to_as(MPCTIO &tio, yield_t &yield,
     as_x.ashare = (xs_x.xshare - as_C) & mask;
 }
 
+// P0 and P1 hold XOR shares x0 and x1 of x. x is at most nbits bits
+// long. Return x to P0 and P1 (and 0 to P2).
+//
+// Cost: 1 word sent in 1 message
+value_t mpc_reconstruct(MPCTIO &tio, yield_t &yield,
+    RegXS x, nbits_t nbits)
+{
+    RegXS res;
+    size_t nbytes = BITBYTES(nbits);
+    if (tio.player() < 2) {
+        tio.queue_peer(&x, nbytes);
+        yield();
+        tio.recv_peer(&res, nbytes);
+        res ^= x;
+    } else {
+        yield();
+    }
+    return res.xshare;
+}
+
+// P0 and P1 hold additive shares x0 and x1 of x. x is at most nbits
+// bits long. Return x to P0 and P1 (and 0 to P2).
+//
+// Cost: 1 word sent in 1 message
+value_t mpc_reconstruct(MPCTIO &tio, yield_t &yield,
+    RegAS x, nbits_t nbits)
+{
+    RegAS res;
+    size_t nbytes = BITBYTES(nbits);
+    if (tio.player() < 2) {
+        tio.queue_peer(&x, nbytes);
+        yield();
+        tio.recv_peer(&res, nbytes);
+        res += x;
+    } else {
+        yield();
+    }
+    return res.ashare;
+}
+
+// P0 and P1 hold bit shares f0 and f1 of f. Return f to P0 and P1 (and
+// 0 to P2).
+//
+// Cost: 1 word sent in 1 message
+bool mpc_reconstruct(MPCTIO &tio, yield_t &yield, RegBS f)
+{
+    RegBS res;
+    if (tio.player() < 2) {
+        tio.queue_peer(&f, 1);
+        yield();
+        tio.recv_peer(&res, 1);
+        res ^= f;
+    } else {
+        yield();
+    }
+    return res.bshare;
+}
+
 // P0 and P1 hold bit shares of f, and DPFnode XOR shares x0,y0 and
 // x1,y1 of x and y.  Set z to x=x0^x1 if f=0 and to y=y0^y1 if f=1.
 //

+ 20 - 0
mpcops.hpp

@@ -111,6 +111,26 @@ void mpc_xs_to_as(MPCTIO &tio, yield_t &yield,
     RegAS &as_x, RegXS xs_x,
     nbits_t nbits = VALUE_BITS, bool tally = true);
 
+// P0 and P1 hold XOR shares x0 and x1 of x. x is at most nbits bits
+// long. Return x to P0 and P1 (and 0 to P2).
+//
+// Cost: 1 word sent in 1 message
+value_t mpc_reconstruct(MPCTIO &tio, yield_t &yield,
+    RegXS f, nbits_t nbits = VALUE_BITS);
+
+// P0 and P1 hold additive shares x0 and x1 of x. x is at most nbits
+// bits long. Return x to P0 and P1 (and 0 to P2).
+//
+// Cost: 1 word sent in 1 message
+value_t mpc_reconstruct(MPCTIO &tio, yield_t &yield,
+    RegAS x, nbits_t nbits = VALUE_BITS);
+
+// P0 and P1 hold bit shares f0 and f1 of f. Return f to P0 and P1 (and
+// 0 to P2).
+//
+// Cost: 1 word sent in 1 message
+bool mpc_reconstruct(MPCTIO &tio, yield_t &yield, RegBS x);
+
 // P0 and P1 hold bit shares of f, and DPFnode XOR shares x0,y0 and
 // x1,y1 of x and y.  Set z to x=x0^x1 if f=0 and to y=y0^y1 if f=1.
 //

+ 95 - 63
online.cpp

@@ -209,7 +209,7 @@ static void lamport_test(MPCIO &mpcio,
 
 template <nbits_t WIDTH>
 static void rdpf_test(MPCIO &mpcio,
-    const PRACOptions &opts, char **args)
+    const PRACOptions &opts, char **args, bool incremental)
 {
     nbits_t depth=6;
     size_t num_iters = 1;
@@ -224,84 +224,101 @@ static void rdpf_test(MPCIO &mpcio,
     }
 
     MPCTIO tio(mpcio, 0, opts.num_threads);
-    run_coroutines(tio, [&tio, depth, num_iters] (yield_t &yield) {
+    run_coroutines(tio, [&tio, depth, num_iters, incremental] (yield_t &yield) {
         size_t &aes_ops = tio.aes_ops();
+        nbits_t min_level = incremental ? 1 : depth;
         for (size_t iter=0; iter < num_iters; ++iter) {
             if (tio.player() == 2) {
-                RDPFPair<WIDTH> dp = tio.rdpfpair<WIDTH>(yield, depth);
+                RDPFPair<WIDTH> dp = tio.rdpfpair<WIDTH>(yield, depth,
+                    incremental);
                 for (int i=0;i<2;++i) {
-                    const RDPF<WIDTH> &dpf = dp.dpf[i];
-                    for (address_t x=0;x<(address_t(1)<<depth);++x) {
-                        typename RDPF<WIDTH>::LeafNode leaf = dpf.leaf(x, aes_ops);
-                        RegBS ub = dpf.unit_bs(leaf);
-                        RegAS ua = dpf.unit_as(leaf);
-                        typename RDPF<WIDTH>::RegXSW sx = dpf.scaled_xs(leaf);
-                        typename RDPF<WIDTH>::RegASW sa = dpf.scaled_as(leaf);
-                        printf("%04x %x %016lx", x, ub.bshare, ua.ashare);
-                        for (nbits_t j=0;j<WIDTH;++j) {
-                            printf(" %016lx %016lx", sx[j].xshare, sa[j].ashare);
+                    RDPF<WIDTH> &dpf = dp.dpf[i];
+                    for (nbits_t level=min_level; level<=depth; ++level) {
+                        if (incremental) {
+                            printf("Level = %u\n\n", level);
+                            dpf.depth(level);
+                        }
+                        for (address_t x=0;x<(address_t(1)<<level);++x) {
+                            typename RDPF<WIDTH>::LeafNode leaf = dpf.leaf(x, aes_ops);
+                            RegBS ub = dpf.unit_bs(leaf);
+                            RegAS ua = dpf.unit_as(leaf);
+                            typename RDPF<WIDTH>::RegXSW sx = dpf.scaled_xs(leaf);
+                            typename RDPF<WIDTH>::RegASW sa = dpf.scaled_as(leaf);
+                            printf("%04x %x %016lx", x, ub.bshare, ua.ashare);
+                            for (nbits_t j=0;j<WIDTH;++j) {
+                                printf(" %016lx %016lx", sx[j].xshare, sa[j].ashare);
+                            }
+                            printf("\n");
                         }
                         printf("\n");
                     }
-                    printf("\n");
                 }
             } else {
-                RDPFTriple<WIDTH> dt = tio.rdpftriple<WIDTH>(yield, depth);
+                RDPFTriple<WIDTH> dt = tio.rdpftriple<WIDTH>(yield,
+                    depth, incremental);
                 for (int i=0;i<3;++i) {
-                    const RDPF<WIDTH> &dpf = dt.dpf[i];
-                    typename RDPF<WIDTH>::RegXSW peer_scaled_xor;
-                    typename RDPF<WIDTH>::RegASW peer_scaled_sum;
-                    if (tio.player() == 1) {
-                        tio.iostream_peer() << dpf.li[0].scaled_xor << dpf.li[0].scaled_sum;
-                    } else {
-                        tio.iostream_peer() >> peer_scaled_xor >> peer_scaled_sum;
-                        peer_scaled_sum += dpf.li[0].scaled_sum;
-                        peer_scaled_xor ^= dpf.li[0].scaled_xor;
-                    }
-                    for (address_t x=0;x<(address_t(1)<<depth);++x) {
-                        typename RDPF<WIDTH>::LeafNode leaf = dpf.leaf(x, aes_ops);
-                        RegBS ub = dpf.unit_bs(leaf);
-                        RegAS ua = dpf.unit_as(leaf);
-                        typename RDPF<WIDTH>::RegXSW sx = dpf.scaled_xs(leaf);
-                        typename RDPF<WIDTH>::RegASW sa = dpf.scaled_as(leaf);
-                        printf("%04x %x %016lx", x, ub.bshare, ua.ashare);
-                        for (nbits_t j=0;j<WIDTH;++j) {
-                            printf(" %016lx %016lx", sx[j].xshare, sa[j].ashare);
+                    RDPF<WIDTH> &dpf = dt.dpf[i];
+                    for (nbits_t level=min_level; level<=depth; ++level) {
+                        if (incremental) {
+                            printf("Level = %u\n\n", level);
+                            dpf.depth(level);
                         }
-                        printf("\n");
+                        typename RDPF<WIDTH>::RegXSW peer_scaled_xor;
+                        typename RDPF<WIDTH>::RegASW peer_scaled_sum;
                         if (tio.player() == 1) {
-                            tio.iostream_peer() << ub << ua << sx << sa;
+                            tio.iostream_peer() <<
+                                dpf.li[depth-level].scaled_xor <<
+                                dpf.li[depth-level].scaled_sum;
                         } else {
-                            RegBS peer_ub;
-                            RegAS peer_ua;
-                            typename RDPF<WIDTH>::RegXSW peer_sx;
-                            typename RDPF<WIDTH>::RegASW peer_sa;
-                            tio.iostream_peer() >> peer_ub >> peer_ua >>
-                                peer_sx >> peer_sa;
-                            ub ^= peer_ub;
-                            ua += peer_ua;
-                            sx ^= peer_sx;
-                            sa += peer_sa;
-                            bool is_nonzero = ub.bshare || ua.ashare;
+                            tio.iostream_peer() >> peer_scaled_xor >> peer_scaled_sum;
+                            peer_scaled_sum += dpf.li[depth-level].scaled_sum;
+                            peer_scaled_xor ^= dpf.li[depth-level].scaled_xor;
+                        }
+                        for (address_t x=0;x<(address_t(1)<<level);++x) {
+                            typename RDPF<WIDTH>::LeafNode leaf = dpf.leaf(x, aes_ops);
+                            RegBS ub = dpf.unit_bs(leaf);
+                            RegAS ua = dpf.unit_as(leaf);
+                            typename RDPF<WIDTH>::RegXSW sx = dpf.scaled_xs(leaf);
+                            typename RDPF<WIDTH>::RegASW sa = dpf.scaled_as(leaf);
+                            printf("%04x %x %016lx", x, ub.bshare, ua.ashare);
                             for (nbits_t j=0;j<WIDTH;++j) {
-                                is_nonzero |= (sx[j].xshare || sa[j].ashare);
+                                printf(" %016lx %016lx", sx[j].xshare, sa[j].ashare);
                             }
-                            if (is_nonzero) {
-                                printf("**** %x %016lx", ub.bshare, ua.ashare);
+                            printf("\n");
+                            if (tio.player() == 1) {
+                                tio.iostream_peer() << ub << ua << sx << sa;
+                            } else {
+                                RegBS peer_ub;
+                                RegAS peer_ua;
+                                typename RDPF<WIDTH>::RegXSW peer_sx;
+                                typename RDPF<WIDTH>::RegASW peer_sa;
+                                tio.iostream_peer() >> peer_ub >> peer_ua >>
+                                    peer_sx >> peer_sa;
+                                ub ^= peer_ub;
+                                ua += peer_ua;
+                                sx ^= peer_sx;
+                                sa += peer_sa;
+                                bool is_nonzero = ub.bshare || ua.ashare;
                                 for (nbits_t j=0;j<WIDTH;++j) {
-                                    printf(" %016lx %016lx", sx[j].xshare, sa[j].ashare);
+                                    is_nonzero |= (sx[j].xshare || sa[j].ashare);
                                 }
-                                printf("\nSCALE                  ");
-                                for (nbits_t j=0;j<WIDTH;++j) {
-                                    printf(" %016lx %016lx",
-                                        peer_scaled_xor[j].xshare,
-                                        peer_scaled_sum[j].ashare);
+                                if (is_nonzero) {
+                                    printf("**** %x %016lx", ub.bshare, ua.ashare);
+                                    for (nbits_t j=0;j<WIDTH;++j) {
+                                        printf(" %016lx %016lx", sx[j].xshare, sa[j].ashare);
+                                    }
+                                    printf("\nSCALE                  ");
+                                    for (nbits_t j=0;j<WIDTH;++j) {
+                                        printf(" %016lx %016lx",
+                                            peer_scaled_xor[j].xshare,
+                                            peer_scaled_sum[j].ashare);
+                                    }
+                                    printf("\n");
                                 }
-                                printf("\n");
                             }
                         }
+                        printf("\n");
                     }
-                    printf("\n");
                 }
             }
         }
@@ -1191,19 +1208,34 @@ void online_main(MPCIO &mpcio, const PRACOptions &opts, char **args)
         lamport_test(mpcio, opts, args);
     } else if (!strcmp(*args, "rdpftest")) {
         ++args;
-        rdpf_test<1>(mpcio, opts, args);
+        rdpf_test<1>(mpcio, opts, args, false);
     } else if (!strcmp(*args, "rdpftest2")) {
         ++args;
-        rdpf_test<2>(mpcio, opts, args);
+        rdpf_test<2>(mpcio, opts, args, false);
     } else if (!strcmp(*args, "rdpftest3")) {
         ++args;
-        rdpf_test<3>(mpcio, opts, args);
+        rdpf_test<3>(mpcio, opts, args, false);
     } else if (!strcmp(*args, "rdpftest4")) {
         ++args;
-        rdpf_test<4>(mpcio, opts, args);
+        rdpf_test<4>(mpcio, opts, args, false);
     } else if (!strcmp(*args, "rdpftest5")) {
         ++args;
-        rdpf_test<5>(mpcio, opts, args);
+        rdpf_test<5>(mpcio, opts, args, false);
+    } else if (!strcmp(*args, "irdpftest")) {
+        ++args;
+        rdpf_test<1>(mpcio, opts, args, true);
+    } else if (!strcmp(*args, "irdpftest2")) {
+        ++args;
+        rdpf_test<2>(mpcio, opts, args, true);
+    } else if (!strcmp(*args, "irdpftest3")) {
+        ++args;
+        rdpf_test<3>(mpcio, opts, args, true);
+    } else if (!strcmp(*args, "irdpftest4")) {
+        ++args;
+        rdpf_test<4>(mpcio, opts, args, true);
+    } else if (!strcmp(*args, "irdpftest5")) {
+        ++args;
+        rdpf_test<5>(mpcio, opts, args, true);
     } else if (!strcmp(*args, "rdpftime")) {
         ++args;
         rdpf_timing(mpcio, opts, args);

+ 35 - 18
preproc.cpp

@@ -78,7 +78,8 @@ void Openfiles::closeall()
 //   0x00: End of preprocessing
 //
 // One byte: subtype (not sent for type == 0x00)
-//   For RAM DPFs, the subtype is the width (0x01 to 0x05)
+//   For RAM DPFs, the subtype is the width (0x01 to 0x05), OR'd with
+//       0x80 if it is an incremental RDPF
 //   Otherwise, it is 0x00
 //
 // Four bytes: number of objects of that type (not sent for type == 0x00)
@@ -159,9 +160,14 @@ void preprocessing_comp(MPCIO &mpcio, const PRACOptions &opts, char **args)
                     }
                 } else if (type >= 0x01 && type <= 0x30) {
                     // RAM DPFs
+                    bool incremental = false;
+                    if (subtype >= 0x80) {
+                        incremental = true;
+                        subtype -= 0x80;
+                    }
                     assert(subtype >= 0x01 && subtype <= 0x05);
-                    char prefix[11];
-                    strcpy(prefix, "rdpf");
+                    char prefix[12];
+                    strcpy(prefix, incremental ? "irdpf" : "rdpf");
                     if (subtype > 1) {
                         sprintf(prefix+strlen(prefix), "%d_", subtype);
                     }
@@ -169,36 +175,42 @@ void preprocessing_comp(MPCIO &mpcio, const PRACOptions &opts, char **args)
                         mpcio.player, thread_num, type);
                     for (unsigned int i=0; i<num; ++i) {
                         coroutines.emplace_back(
-                            [&tio, &opts, tripfile, type, subtype](yield_t &yield) {
+                            [&tio, &opts, incremental, tripfile, type,
+                                subtype](yield_t &yield) {
                                 yield();
                                 switch(subtype) {
                                 case 1: {
                                     RDPFTriple<1> rdpftrip =
-                                        tio.rdpftriple<1>(yield, type, opts.expand_rdpfs);
+                                        tio.rdpftriple<1>(yield, type,
+                                            incremental, opts.expand_rdpfs);
                                     tripfile.os() << rdpftrip;
                                     break;
                                 }
                                 case 2: {
                                     RDPFTriple<2> rdpftrip =
-                                        tio.rdpftriple<2>(yield, type, opts.expand_rdpfs);
+                                        tio.rdpftriple<2>(yield, type,
+                                            incremental, opts.expand_rdpfs);
                                     tripfile.os() << rdpftrip;
                                     break;
                                 }
                                 case 3: {
                                     RDPFTriple<3> rdpftrip =
-                                        tio.rdpftriple<3>(yield, type, opts.expand_rdpfs);
+                                        tio.rdpftriple<3>(yield, type,
+                                            incremental, opts.expand_rdpfs);
                                     tripfile.os() << rdpftrip;
                                     break;
                                 }
                                 case 4: {
                                     RDPFTriple<4> rdpftrip =
-                                        tio.rdpftriple<4>(yield, type, opts.expand_rdpfs);
+                                        tio.rdpftriple<4>(yield, type,
+                                            incremental, opts.expand_rdpfs);
                                     tripfile.os() << rdpftrip;
                                     break;
                                 }
                                 case 5: {
                                     RDPFTriple<5> rdpftrip =
-                                        tio.rdpftriple<5>(yield, type, opts.expand_rdpfs);
+                                        tio.rdpftriple<5>(yield, type,
+                                            incremental, opts.expand_rdpfs);
                                     tripfile.os() << rdpftrip;
                                     break;
                                 }
@@ -355,7 +367,8 @@ void preprocessing_server(MPCServerIO &mpcsrvio, const PRACOptions &opts, char *
                                 stio.valselecttriple(yield);
                             });
                     }
-                } else if (type[0] == 'r') {
+                } else if (type[0] == 'r' || type[0] == 'i') {
+                    bool incremental = (type[0] == 'i');
                     char *widthstr = strchr(type, '.');
                     unsigned char width = 1;
                     if (widthstr) {
@@ -369,6 +382,9 @@ void preprocessing_server(MPCServerIO &mpcsrvio, const PRACOptions &opts, char *
                     } else {
                         unsigned char typetag = depth;
                         unsigned char subtypetag = width;
+                        if (incremental) {
+                            subtypetag += 0x80;
+                        }
                         stio.queue_p0(&typetag, 1);
                         stio.queue_p0(&subtypetag, 1);
                         stio.queue_p0(&num, 4);
@@ -376,8 +392,8 @@ void preprocessing_server(MPCServerIO &mpcsrvio, const PRACOptions &opts, char *
                         stio.queue_p1(&subtypetag, 1);
                         stio.queue_p1(&num, 4);
 
-                        char prefix[11];
-                        strcpy(prefix, "rdpf");
+                        char prefix[12];
+                        strcpy(prefix, incremental ? "irdpf" : "rdpf");
                         if (width > 1) {
                             sprintf(prefix+strlen(prefix), "%d_", width);
                         }
@@ -385,12 +401,13 @@ void preprocessing_server(MPCServerIO &mpcsrvio, const PRACOptions &opts, char *
                             mpcsrvio.player, thread_num, depth);
                         for (unsigned int i=0; i<num; ++i) {
                             coroutines.emplace_back(
-                                [&stio, &opts, pairfile, depth, width](yield_t &yield) {
+                                [&stio, &opts, pairfile, depth,
+                                incremental, width](yield_t &yield) {
                                     yield();
                                     switch (width) {
                                     case 1: {
                                         RDPFPair<1> rdpfpair =
-                                            stio.rdpfpair<1>(yield, depth);
+                                            stio.rdpfpair<1>(yield, depth, incremental);
                                         if (opts.expand_rdpfs) {
                                             rdpfpair.dpf[0].expand(stio.aes_ops());
                                             rdpfpair.dpf[1].expand(stio.aes_ops());
@@ -400,7 +417,7 @@ void preprocessing_server(MPCServerIO &mpcsrvio, const PRACOptions &opts, char *
                                     }
                                     case 2: {
                                         RDPFPair<2> rdpfpair =
-                                            stio.rdpfpair<2>(yield, depth);
+                                            stio.rdpfpair<2>(yield, depth, incremental);
                                         if (opts.expand_rdpfs) {
                                             rdpfpair.dpf[0].expand(stio.aes_ops());
                                             rdpfpair.dpf[1].expand(stio.aes_ops());
@@ -410,7 +427,7 @@ void preprocessing_server(MPCServerIO &mpcsrvio, const PRACOptions &opts, char *
                                     }
                                     case 3: {
                                         RDPFPair<3> rdpfpair =
-                                            stio.rdpfpair<3>(yield, depth);
+                                            stio.rdpfpair<3>(yield, depth, incremental);
                                         if (opts.expand_rdpfs) {
                                             rdpfpair.dpf[0].expand(stio.aes_ops());
                                             rdpfpair.dpf[1].expand(stio.aes_ops());
@@ -420,7 +437,7 @@ void preprocessing_server(MPCServerIO &mpcsrvio, const PRACOptions &opts, char *
                                     }
                                     case 4: {
                                         RDPFPair<4> rdpfpair =
-                                            stio.rdpfpair<4>(yield, depth);
+                                            stio.rdpfpair<4>(yield, depth, incremental);
                                         if (opts.expand_rdpfs) {
                                             rdpfpair.dpf[0].expand(stio.aes_ops());
                                             rdpfpair.dpf[1].expand(stio.aes_ops());
@@ -430,7 +447,7 @@ void preprocessing_server(MPCServerIO &mpcsrvio, const PRACOptions &opts, char *
                                     }
                                     case 5: {
                                         RDPFPair<5> rdpfpair =
-                                            stio.rdpfpair<5>(yield, depth);
+                                            stio.rdpfpair<5>(yield, depth, incremental);
                                         if (opts.expand_rdpfs) {
                                             rdpfpair.dpf[0].expand(stio.aes_ops());
                                             rdpfpair.dpf[1].expand(stio.aes_ops());

+ 50 - 16
rdpf.hpp

@@ -57,6 +57,8 @@ struct RDPF : public DPF {
         // XOR share of the scaling values M_xs such that the high words
         // of the WIDTH leaf values for P0 and P1 XOR to M_xs * e_{target}
         std::array<RegXS,WIDTH> scaled_xor;
+        // If we're saving the expansion, put it here
+        std::vector<LeafNode> expansion;
 
         LeafInfo() : unit_sum_inverse(0) {}
     };
@@ -82,9 +84,6 @@ struct RDPF : public DPF {
     // but therefore only the low bit gets used.
     value_t leaf_cfbits;
 
-    // If we're saving the expansion, put it here
-    std::vector<LeafNode> expansion;
-
     RDPF() {}
 
     // Construct a DPF with the given (XOR-shared) target location, and
@@ -100,19 +99,32 @@ struct RDPF : public DPF {
     // 2^{depth+1}-2 local AES operations for P0,P1
     // 0 local AES operations for P2
     RDPF(MPCTIO &tio, yield_t &yield,
-        RegXS target, nbits_t depth, bool save_expansion = false);
+        RegXS target, nbits_t depth, bool incremental = false,
+        bool save_expansion = false);
 
     // Do we have a precomputed expansion?
-    inline bool has_expansion() const { return expansion.size() > 0; }
+    inline bool has_expansion() const {
+        return li[maxdepth-curdepth].expansion.size() > 0;
+    }
 
     // Get an element of the expansion
     inline LeafNode get_expansion(address_t index) const {
-        return expansion[index];
+        return li[maxdepth-curdepth].expansion[index];
     }
 
     // The depth
     inline nbits_t depth() const { return curdepth; }
 
+    // Set the current depth for an incremental RDPF; 0 means to use
+    // maxdepth
+    inline void depth(nbits_t newdepth) {
+        if (newdepth > 0 && newdepth < maxdepth) {
+            curdepth = newdepth;
+        } else {
+            curdepth = maxdepth;
+        }
+    }
+
     // Get the leaf node for the given input
     //
     // Cost: depth AES operations
@@ -143,7 +155,7 @@ struct RDPF : public DPF {
         if (whichhalf == 1) {
             lowword = -lowword;
         }
-        a.ashare = lowword * li[0].unit_sum_inverse;
+        a.ashare = lowword * li[maxdepth-curdepth].unit_sum_inverse;
         return a;
     }
 
@@ -194,6 +206,9 @@ struct RDPF : public DPF {
         return a;
     }
 
+private:
+    // Expand one leaf layer of the DPF if it's not already expanded
+    void expand_leaf_layer(nbits_t li_index, size_t &aes_ops);
 };
 
 // Computational peers will generate triples of RDPFs with the _same_
@@ -229,6 +244,14 @@ struct RDPFTriple {
     // The depth
     inline nbits_t depth() const { return dpf[0].depth(); }
 
+    // Set the current depth for an incremental RDPFTriple; 0 means to
+    // use maxdepth
+    inline void depth(nbits_t newdepth) {
+        dpf[0].depth(newdepth);
+        dpf[1].depth(newdepth);
+        dpf[2].depth(newdepth);
+    }
+
     // The seed
     inline node get_seed() const {
         return std::make_tuple(dpf[0].get_seed(), dpf[1].get_seed(),
@@ -237,7 +260,8 @@ struct RDPFTriple {
 
     // Do we have a precomputed expansion?
     inline bool has_expansion() const {
-        return dpf[0].expansion.size() > 0;
+        int li_index = dpf[0].maxdepth - dpf[0].curdepth;
+        return dpf[0].li[li_index].expansion.size() > 0;
     }
 
     // Get an element of the expansion
@@ -251,7 +275,7 @@ struct RDPFTriple {
     // Construct three RDPFs of the given depth all with the same
     // randomly generated target index.
     RDPFTriple(MPCTIO &tio, yield_t &yield,
-        nbits_t depth, bool save_expansion = false);
+        nbits_t depth, bool incremental = false, bool save_expansion = false);
 
     // Descend the three RDPFs in lock step
     node descend(const node &parent, nbits_t parentdepth,
@@ -271,17 +295,19 @@ struct RDPFTriple {
     // Additive share of the scaling value M_as such that the high words
     // of the leaf values for P0 and P1 add to M_as * e_{target}
     inline void scaled_value(RegASWT &v) const {
-        std::get<0>(v) = dpf[0].li[0].scaled_sum;
-        std::get<1>(v) = dpf[1].li[0].scaled_sum;
-        std::get<2>(v) = dpf[2].li[0].scaled_sum;
+        int li_index = dpf[0].maxdepth - dpf[0].curdepth;
+        std::get<0>(v) = dpf[0].li[li_index].scaled_sum;
+        std::get<1>(v) = dpf[1].li[li_index].scaled_sum;
+        std::get<2>(v) = dpf[2].li[li_index].scaled_sum;
     }
 
     // XOR share of the scaling value M_xs such that the high words
     // of the leaf values for P0 and P1 XOR to M_xs * e_{target}
     inline void scaled_value(RegXSWT &v) const {
-        std::get<0>(v) = dpf[0].li[0].scaled_xor;
-        std::get<1>(v) = dpf[1].li[0].scaled_xor;
-        std::get<2>(v) = dpf[2].li[0].scaled_xor;
+        int li_index = dpf[0].maxdepth - dpf[0].curdepth;
+        std::get<0>(v) = dpf[0].li[li_index].scaled_xor;
+        std::get<1>(v) = dpf[1].li[li_index].scaled_xor;
+        std::get<2>(v) = dpf[2].li[li_index].scaled_xor;
     }
 
     // Get the additive-shared unit vector entry from the leaf node
@@ -354,6 +380,13 @@ struct RDPFPair {
     // The depth
     inline nbits_t depth() const { return dpf[0].depth(); }
 
+    // Set the current depth for an incremental RDPFPair; 0 means to use
+    // maxdepth
+    inline void depth(nbits_t newdepth) {
+        dpf[0].depth(newdepth);
+        dpf[1].depth(newdepth);
+    }
+
     // The seed
     inline node get_seed() const {
         return std::make_tuple(dpf[0].get_seed(), dpf[1].get_seed());
@@ -361,7 +394,8 @@ struct RDPFPair {
 
     // Do we have a precomputed expansion?
     inline bool has_expansion() const {
-        return dpf[0].expansion.size() > 0;
+        int li_index = dpf[0].maxdepth - dpf[0].curdepth;
+        return dpf[0].li[li_index].expansion.size() > 0;
     }
 
     // Get an element of the expansion

+ 87 - 46
rdpf.tcc

@@ -193,7 +193,7 @@ inline typename RDPF<WIDTH>::LeafNode RDPF<WIDTH>::descend_to_leaf(
     bool flag = get_lsb(parent);
     prg(prgout, parent, whichchild, aes_ops);
     if (flag) {
-        LeafNode CW = li[0].leaf_cw;
+        LeafNode CW = li[maxdepth-parentdepth-1].leaf_cw;
         LeafNode CWR = CW;
         bit_t cfbit = !!(leaf_cfbits &
             (value_t(1)<<(maxdepth-parentdepth-1)));
@@ -211,9 +211,15 @@ T& operator>>(T &is, RDPF<WIDTH> &rdpf)
     is.read((char *)&rdpf.seed, sizeof(rdpf.seed));
     rdpf.whichhalf = get_lsb(rdpf.seed);
     uint8_t depth;
-    // Add 64 to depth to indicate an expanded RDPF
+    // Add 64 to depth to indicate an expanded RDPF, and add 128 to
+    // indicate an incremental RDPF
     is.read((char *)&depth, sizeof(depth));
     bool read_expanded = false;
+    bool read_incremental = false;
+    if (depth > 128) {
+        read_incremental = true;
+        depth -= 128;
+    }
     if (depth > 64) {
         read_expanded = true;
         depth -= 64;
@@ -227,19 +233,22 @@ T& operator>>(T &is, RDPF<WIDTH> &rdpf)
         is.read((char *)&cw, sizeof(cw));
         rdpf.cw.push_back(cw);
     }
+    nbits_t num_leaflevels = read_incremental ? depth : 1;
+    rdpf.li.resize(num_leaflevels);
     if (read_expanded) {
-        rdpf.expansion.resize(1<<depth);
-        is.read((char *)rdpf.expansion.data(),
-            sizeof(rdpf.expansion[0])<<depth);
+        for(nbits_t i=0; i<num_leaflevels; ++i) {
+            nbits_t level = depth-i;
+            rdpf.li[i].expansion.resize(1<<level);
+            is.read((char *)rdpf.li[i].expansion.data(),
+                sizeof(rdpf.li[i].expansion[0])<<level);
+        }
     }
     value_t cfbits = 0;
     is.read((char *)&cfbits, BITBYTES(depth-1));
     rdpf.cfbits = cfbits;
-    nbits_t num_leaflevels = 1;
     value_t leaf_cfbits = 0;
     is.read((char *)&leaf_cfbits, BITBYTES(num_leaflevels));
     rdpf.leaf_cfbits = leaf_cfbits;
-    rdpf.li.resize(num_leaflevels);
     for (nbits_t i=0; i<num_leaflevels; ++i) {
         is.read((char *)&rdpf.li[i].leaf_cw,
             sizeof(rdpf.li[i].leaf_cw));
@@ -267,20 +276,27 @@ T& write_maybe_expanded(T &os, const RDPF<WIDTH> &rdpf,
     // If we're writing an expansion, add 64 to depth
     uint8_t expanded_depth = depth;
     bool write_expansion = false;
-    if (expanded && rdpf.expansion.size() == (size_t(1)<<depth)) {
+    if (expanded && rdpf.li[0].expansion.size() == (size_t(1)<<depth)) {
         write_expansion = true;
         expanded_depth += 64;
     }
+    // If we're writing an incremental RDPF, add 128 to depth
+    if (rdpf.li.size() > 1) {
+        expanded_depth += 128;
+    }
     os.write((const char *)&expanded_depth, sizeof(expanded_depth));
     for (uint8_t i=0; i<depth-1; ++i) {
         os.write((const char *)&rdpf.cw[i], sizeof(rdpf.cw[i]));
     }
+    nbits_t num_leaflevels = rdpf.li.size();
     if (write_expansion) {
-        os.write((const char *)rdpf.expansion.data(),
-            sizeof(rdpf.expansion[0])<<depth);
+        for(nbits_t i=0; i<num_leaflevels; ++i) {
+            nbits_t level = depth-i;
+            os.write((const char *)rdpf.li[i].expansion.data(),
+                sizeof(rdpf.li[i].expansion[0])<<level);
+        }
     }
     os.write((const char *)&rdpf.cfbits, BITBYTES(depth-1));
-    nbits_t num_leaflevels = 1;
     os.write((const char *)&rdpf.leaf_cfbits, BITBYTES(num_leaflevels));
     for (nbits_t i=0; i<num_leaflevels; ++i) {
         os.write((const char *)&rdpf.li[i].leaf_cw,
@@ -802,7 +818,7 @@ static inline void create_level(MPCTIO &tio, yield_t &yield,
             assert(low_sum & 1);
             li.unit_sum_inverse = inverse_value_t(low_sum);
         }
-    } else if (level == depth-1) {
+    } else if constexpr (!std::is_same_v<NT, DPFnode>) {
         yield();
     }
 }
@@ -818,7 +834,7 @@ static inline void create_level(MPCTIO &tio, yield_t &yield,
 // small optimization noted below.
 template <nbits_t WIDTH>
 RDPF<WIDTH>::RDPF(MPCTIO &tio, yield_t &yield,
-    RegXS target, nbits_t depth, bool save_expansion)
+    RegXS target, nbits_t depth, bool incremental, bool save_expansion)
 {
     int player = tio.player();
     size_t &aes_ops = tio.aes_ops();
@@ -839,7 +855,7 @@ RDPF<WIDTH>::RDPF(MPCTIO &tio, yield_t &yield,
     DPFnode *nextlevel = new DPFnode[1];
     nextlevel[0] = seed;
 
-    li.resize(1);
+    li.resize(incremental ? depth : 1);
 
     // Construct each intermediate level
     while(level < depth) {
@@ -848,20 +864,21 @@ RDPF<WIDTH>::RDPF(MPCTIO &tio, yield_t &yield,
             delete[] curlevel;
             curlevel = nextlevel;
             nextlevel = NULL;
-            if (save_expansion && level == depth-1) {
-                expansion.resize(1<<depth);
-                leaflevel = expansion.data();
-            } else if (level == depth-1) {
-                leaflevel = new LeafNode[1<<depth];
-            } else {
+            if (save_expansion && (incremental || level == depth-1)) {
+                li[depth-1-level].expansion.resize(1<<(level+1));
+                leaflevel = li[depth-1-level].expansion.data();
+            } else if (incremental || level == depth-1) {
+                leaflevel = new LeafNode[1<<(level+1)];
+            }
+            if (level < depth-1) {
                 nextlevel = new DPFnode[1<<(level+1)];
             }
         }
-        // Invariant: curlevel has 2^level elements; nextlevel has
-        // 2^{level+1} DPFnode elements if we're not at the last level,
-        // and leaflevel has 2^{level+1} LeafNode elements if we are at
-        // a leaf level (the last level always, and all levels if we are
-        // making an incremental RDPF).
+        // Invariant: curlevel has 2^level DPFnode elements; nextlevel
+        // has 2^{level+1} DPFnode elements if we're not at the last
+        // level, and leaflevel has 2^{level+1} LeafNode elements if we
+        // are at a leaf level (the last level always, and all levels if
+        // we are making an incremental RDPF).
 
         // The bit-shared choice bit is bit (depth-level-1) of the
         // XOR-shared target index
@@ -870,20 +887,24 @@ RDPF<WIDTH>::RDPF(MPCTIO &tio, yield_t &yield,
 
         if (level < depth-1) {
             DPFnode CW;
+            // This field is ignored when we're not expanding to a leaf
+            // level, but it needs to be an lvalue reference.
+            int noleafinfo = 0;
             create_level(tio, yield, curlevel, nextlevel, player, level,
-                depth, bs_choice, CW, cfbit, save_expansion, li[0],
+                depth, bs_choice, CW, cfbit, save_expansion, noleafinfo,
                 aes_ops);
             cfbits |= (value_t(cfbit)<<level);
             if (player < 2) {
                 cw.push_back(CW);
             }
-        } else {
+        }
+        if (incremental || level == depth-1) {
             LeafNode CW;
             create_level(tio, yield, curlevel, leaflevel, player, level,
-                depth, bs_choice, CW, cfbit, save_expansion, li[0],
-                aes_ops);
+                depth, bs_choice, CW, cfbit, save_expansion,
+                li[depth-level-1], aes_ops);
             leaf_cfbits |= (value_t(cfbit)<<(depth-level-1));
-            li[0].leaf_cw = CW;
+            li[depth-level-1].leaf_cw = CW;
         }
 
         if (!save_expansion) {
@@ -902,8 +923,8 @@ typename RDPF<WIDTH>::LeafNode
     RDPF<WIDTH>::leaf(address_t input, size_t &aes_ops) const
 {
     // If we have a precomputed expansion, just use it
-    if (expansion.size()) {
-        return expansion[input];
+    if (li[maxdepth-curdepth].expansion.size()) {
+        return li[maxdepth-curdepth].expansion[input];
     }
 
     DPFnode node = seed;
@@ -915,17 +936,18 @@ typename RDPF<WIDTH>::LeafNode
     return descend_to_leaf(node, curdepth-1, dir, aes_ops);
 }
 
-// Expand the DPF if it's not already expanded
+// Expand one leaf layer of the DPF if it's not already expanded
 //
-// This routine is slightly more efficient than repeatedly calling
-// StreamEval::next(), but it uses a lot more memory.
+// This routine is slightly more efficient (except for incremental
+// RDPFs) than repeatedly calling StreamEval::next(), but it uses a lot
+// more memory.
 template <nbits_t WIDTH>
-void RDPF<WIDTH>::expand(size_t &aes_ops)
+void RDPF<WIDTH>::expand_leaf_layer(nbits_t li_index, size_t &aes_ops)
 {
-    nbits_t depth = this->depth();
+    nbits_t depth = maxdepth - li_index;
     size_t num_leaves = size_t(1)<<depth;
-    if (expansion.size() == num_leaves) return;
-    expansion.resize(num_leaves);
+    if (li[li_index].expansion.size() == num_leaves) return;
+    li[li_index].expansion.resize(num_leaves);
     address_t index = 0;
     address_t lastindex = 0;
     DPFnode *path = new DPFnode[depth];
@@ -933,8 +955,10 @@ void RDPF<WIDTH>::expand(size_t &aes_ops)
     for (nbits_t i=1;i<depth;++i) {
         path[i] = descend(path[i-1], i-1, 0, aes_ops);
     }
-    expansion[index++] = descend_to_leaf(path[depth-1], depth-1, 0, aes_ops);
-    expansion[index++] = descend_to_leaf(path[depth-1], depth-1, 1, aes_ops);
+    li[maxdepth-depth].expansion[index++] =
+        descend_to_leaf(path[depth-1], depth-1, 0, aes_ops);
+    li[maxdepth-depth].expansion[index++] =
+        descend_to_leaf(path[depth-1], depth-1, 1, aes_ops);
     while(index < num_leaves) {
         // Invariant: lastindex and index will both be even, and
         // index=lastindex+2
@@ -954,18 +978,34 @@ void RDPF<WIDTH>::expand(size_t &aes_ops)
             path[i+1] = descend(path[i], i, 0, aes_ops);
         }
         lastindex = index;
-        expansion[index++] = descend_to_leaf(path[depth-1], depth-1, 0, aes_ops);
-        expansion[index++] = descend_to_leaf(path[depth-1], depth-1, 1, aes_ops);
+        li[li_index].expansion[index++] =
+            descend_to_leaf(path[depth-1], depth-1, 0, aes_ops);
+        li[li_index].expansion[index++] =
+            descend_to_leaf(path[depth-1], depth-1, 1, aes_ops);
     }
 
     delete[] path;
 }
 
+// Expand the DPF if it's not already expanded
+//
+// This routine is slightly more efficient (except for incremental
+// RDPFs) than repeatedly calling StreamEval::next(), but it uses a lot
+// more memory.
+template <nbits_t WIDTH>
+void RDPF<WIDTH>::expand(size_t &aes_ops)
+{
+    nbits_t num_leaf_layers = li.size();
+    for (nbits_t li_index=0; li_index < num_leaf_layers; ++li_index) {
+        expand_leaf_layer(li_index, aes_ops);
+    }
+}
+
 // Construct three RDPFs of the given depth all with the same randomly
 // generated target index.
 template <nbits_t WIDTH>
 RDPFTriple<WIDTH>::RDPFTriple(MPCTIO &tio, yield_t &yield,
-    nbits_t depth, bool save_expansion)
+    nbits_t depth, bool incremental, bool save_expansion)
 {
     // Pick a random XOR share of the target
     xs_target.randomize(depth);
@@ -975,9 +1015,10 @@ RDPFTriple<WIDTH>::RDPFTriple(MPCTIO &tio, yield_t &yield,
     std::vector<coro_t> coroutines;
     for (int i=0;i<3;++i) {
         coroutines.emplace_back(
-            [this, &tio, depth, i, save_expansion](yield_t &yield) {
+            [this, &tio, depth, i, incremental,
+                save_expansion](yield_t &yield) {
                 dpf[i] = RDPF<WIDTH>(tio, yield, xs_target, depth,
-                    save_expansion);
+                    incremental, save_expansion);
             });
     }
     coroutines.emplace_back(

+ 4 - 0
types.hpp

@@ -748,8 +748,12 @@ struct ValSelectTripleName { static constexpr const char *name = "s"; };
 // header dependencies.
 template <nbits_t WIDTH> struct RDPFPair;
 struct RDPFPairName { static constexpr const char *name = "r"; };
+// Incremental RDPFs
+struct IRDPFPairName { static constexpr const char *name = "i"; };
 template <nbits_t WIDTH> struct RDPFTriple;
 struct RDPFTripleName { static constexpr const char *name = "r"; };
+// Incremental RDPFs
+struct IRDPFTripleName { static constexpr const char *name = "i"; };
 struct CDPF;
 struct CDPFName { static constexpr const char *name = "c"; };