changeset 1250:77f74e53f796

Merged with Martin.
author Marcel Keller <mkeller@cs.au.dk>
date Sat, 19 Sep 2009 15:34:01 +0200
parents 0f35ae3f503b 80125f56beaa
children 2bbab8b5597f
files viff/active.py viff/aes.py viff/passive.py viff/runtime.py
diffstat 11 files changed, 69 insertions(+), 222 deletions(-) [+]
line wrap: on
line diff
--- a/doc/program-counters.txt	Thu Sep 17 17:59:08 2009 +0200
+++ b/doc/program-counters.txt	Sat Sep 19 15:34:01 2009 +0200
@@ -88,63 +88,41 @@
 point in the execution tree. The execution tree is never explicitly
 constructed in VIFF, so a simple static numbering is not possible.
 
-Instead we mark methods that need to increment the program counter
-with the :func:`viff.runtime.increment_pc` decorator. The program
-counter starts at the value ``[0]`` and the decorated method will now
-begin by doing::
+The program counter starts at the value ``[0]``. It is changed in two
+cases:
 
-  self.program_counter[-1] += 1
-  self.program_counter.append(0)
+* when a callback is scheduled using
+  :meth:`viff.runtime.BasicRuntime.schedule_callback`, a new
+  sub-program counter is allocated. A sub-program counter is simply a
+  program counter with another digit. Because of the asynchronous
+  network, the callback will be invoked at an unknown later time. When
+  invoked, it sees the sub-program counter. This ensures that that the
+  parties agree on any network traffic produced in the callback.
 
-before it executes its body. When the body is finished, the method
-does::
+  When a piece of code like this::
 
-  self.program_counter.pop()
+    def cb(ignored):
+        print "callback:", self.program_counter
+    d = Deferred()
 
-before it returns. A method :meth:`foo` defined like this::
+    print "main:", self.program_counter
+    self.schedule_callback(d, cb)
+    print "main:", self.program_counter
 
-  @increment_pc
-  def foo(self):
-      print "foo:", self.program_counter
+    d.callback(None)
 
-is thus turned into this::
+  is executed, one will see output like this:
 
-  def foo(self):
-      self.program_counter[-1] += 1
-      self.program_counter.append(0)
-      print "foo:", self.program_counter
-      self.program_counter.pop()
+  .. code-block:: none
 
-and when executed starting from the initial program counter of ``[0]``
-we see that it prints ``foo: [1, 0]`` and leaves the program counter
-at ``[1]`` after it returns. It is very important that the program
-counter is left changed like this, for this means that the next call
-to :meth:`foo` will print ``foo: [2, 0]`` and increment the program
-counter to ``[2]``.
+     main: [0]
+     main: [1]
+     callback: [0, 0]
 
-If we have a method :meth:`bar` which calls :meth:`foo` several times::
+* some functions depend on a unique program counter. These functions
+  simply increase the last digit in the current program counter::
 
-  @increment_pc
-  def bar(self):
-      print "bar:", self.program_counter
-      self.foo()
-      print "bar:", self.program_counter
-      self.foo()
-      print "bar:", self.program_counter
-
-then the result of calling :meth:`bar` will be:
-
-.. code-block:: none
-
-   bar: [1, 0]
-   foo: [1, 1, 0]
-   bar: [1, 1]
-   foo: [1, 2, 0]
-   bar: [1, 2]
-
-Notice how each sub-call adds another digit to the counter and how it
-increments the counter used at the level of the caller. This system
-ensures that all program counters are unique.
+    self.program_counter[-1] += 1
 
 
 Alternatives
--- a/doc/runtime.txt	Thu Sep 17 17:59:08 2009 +0200
+++ b/doc/runtime.txt	Sat Sep 19 15:34:01 2009 +0200
@@ -31,8 +31,6 @@
       and other messages. They serve to distinguish messages sent with
       the same program counter from one another.
 
-   .. autofunction:: increment_pc
-
    .. autofunction:: preprocess
 
       See also :ref:`preprocessing` for more background information.
@@ -88,9 +86,7 @@
          different parts of the program execution never reuses the
          same program counter for different variables.
 
-         The :func:`increment_pc` decorator is responsible for
-         dynamically building the tree as the execution unfolds and
-         :meth:`schedule_callback` is responsible for scheduling
-         callbacks with the correct program counter.
+         The :meth:`schedule_callback` method is responsible for
+         scheduling callbacks with the correct program counter.
 
          See :ref:`program-counters` for more background information.
--- a/viff/active.py	Thu Sep 17 17:59:08 2009 +0200
+++ b/viff/active.py	Sat Sep 19 15:34:01 2009 +0200
@@ -27,7 +27,7 @@
 from viff.util import rand
 from viff.matrix import Matrix, hyper
 from viff.passive import PassiveRuntime
-from viff.runtime import Share, increment_pc, preprocess, gather_shares
+from viff.runtime import Share, preprocess, gather_shares
 from viff.runtime import ECHO, READY, SEND
 
 
@@ -37,7 +37,6 @@
     broadcast.
     """
 
-    @increment_pc
     def _broadcast(self, sender, message=None):
         """Perform a Bracha broadcast.
 
@@ -47,6 +46,8 @@
         protocol" by G. Bracha in Proc. 3rd ACM Symposium on
         Principles of Distributed Computing, 1984, pages 154-162.
         """
+        # We need a unique program counter for each call.
+        self.program_counter[-1] += 1
 
         result = Deferred()
         pc = tuple(self.program_counter)
@@ -141,7 +142,6 @@
 
         return result
 
-    @increment_pc
     def broadcast(self, senders, message=None):
         """Perform one or more Bracha broadcast(s).
 
@@ -186,7 +186,6 @@
     #: to :const:`None` here and update it as necessary.
     _hyper = None
 
-    @increment_pc
     def single_share_random(self, T, degree, field):
         """Share a random secret.
 
@@ -273,7 +272,6 @@
         self.schedule_callback(result, exchange)
         return result
 
-    @increment_pc
     def double_share_random(self, T, d1, d2, field):
         """Double-share a random secret using two polynomials.
 
@@ -376,7 +374,6 @@
         self.schedule_callback(result, exchange)
         return result
 
-    @increment_pc
     @preprocess("generate_triples")
     def get_triple(self, field):
         # This is a waste, but this function is only called if there
@@ -385,7 +382,6 @@
         result.addCallback(lambda triples: triples[0])
         return result
 
-    @increment_pc
     def generate_triples(self, field):
         """Generate multiplication triples.
 
@@ -425,14 +421,12 @@
 class TriplesPRSSMixin:
     """Mixin class for generating multiplication triples using PRSS."""
 
-    @increment_pc
     @preprocess("generate_triples")
     def get_triple(self, field):
         count, result = self.generate_triples(field, quantity=1)
         result.addCallback(lambda triples: triples[0])
         return result
 
-    @increment_pc
     def generate_triples(self, field, quantity=20):
         """Generate *quantity* multiplication triples using PRSS.
 
@@ -470,7 +464,6 @@
     :class:`ActiveRuntime` instead.
     """
 
-    @increment_pc
     def mul(self, share_x, share_y):
         """Multiplication of shares.
 
--- a/viff/aes.py	Thu Sep 17 17:59:08 2009 +0200
+++ b/viff/aes.py	Sat Sep 19 15:34:01 2009 +0200
@@ -24,7 +24,7 @@
 import operator
 
 from viff.field import GF256
-from viff.runtime import Share, gather_shares, increment_pc
+from viff.runtime import Share, gather_shares
 from viff.matrix import Matrix
 
 
@@ -360,7 +360,6 @@
                     "or of shares thereof."
             return input
 
-    @increment_pc
     def encrypt(self, cleartext, key, benchmark=False, prepare_at_once=False):
         """Rijndael encryption.
 
--- a/viff/comparison.py	Thu Sep 17 17:59:08 2009 +0200
+++ b/viff/comparison.py	Sat Sep 19 15:34:01 2009 +0200
@@ -25,7 +25,7 @@
 import math
 
 from viff.util import rand, profile
-from viff.runtime import Share, gather_shares, increment_pc
+from viff.runtime import Share, gather_shares
 from viff.passive import PassiveRuntime
 from viff.active import ActiveRuntime
 from viff.field import GF256, FieldElement
@@ -34,7 +34,6 @@
 class ComparisonToft05Mixin:
     """Comparison by Tomas Toft, 2005."""
 
-    @increment_pc
     def convert_bit_share(self, share, dst_field):
         """Convert a 0/1 share into dst_field."""
         bit = rand.randint(0, 1)
@@ -67,7 +66,6 @@
         return int_b, bit_bits
 
     @profile
-    @increment_pc
     def greater_than_equal(self, share_a, share_b):
         """Compute ``share_a >= share_b``.
 
@@ -100,7 +98,6 @@
         self.schedule_callback(result, self._finish_greater_than_equal, l)
         return result
 
-    @increment_pc
     def _finish_greater_than_equal(self, results, l):
         """Finish the calculation."""
         T = results[0]
@@ -128,7 +125,6 @@
 
         return GF256(T.bit(l)) ^ (bit_bits[l] ^ vec[0][1])
 
-    @increment_pc
     def _diamond(self, (top_a, bot_a), (top_b, bot_b)):
         """The "diamond-operator".
 
@@ -160,7 +156,6 @@
     elements and gives a secret result shared over Zp.
     """
 
-    @increment_pc
     def convert_bit_share(self, share, dst_field):
         """Convert a 0/1 share into *dst_field*."""
         l = self.options.security_parameter + math.log(dst_field.modulus, 2)
@@ -188,7 +183,6 @@
         return tmp - full_mask
 
     @profile
-    @increment_pc
     def greater_than_equal_preproc(self, field, smallField=None):
         """Preprocessing for :meth:`greater_than_equal`."""
         if smallField is None:
@@ -243,7 +237,6 @@
         ##################################################
 
     @profile
-    @increment_pc
     def greater_than_equal_online(self, share_a, share_b, preproc, field):
         """Compute ``share_a >= share_b``. Result is secret shared."""
         # increment l as a, b are increased
@@ -272,7 +265,6 @@
                                r_modl, r_bits, z)
         return c
 
-    @increment_pc
     def _finish_greater_than_equal(self, c, field, smallField, s_bit, s_sign,
                                mask, r_modl, r_bits, z):
         """Finish the calculation."""
@@ -316,7 +308,6 @@
         return (z - result) * ~field(2**l)
     # END _finish_greater_than
 
-    @increment_pc
     def greater_than_equal(self, share_a, share_b):
         """Compute ``share_a >= share_b``.
 
--- a/viff/equality.py	Thu Sep 17 17:59:08 2009 +0200
+++ b/viff/equality.py	Sat Sep 19 15:34:01 2009 +0200
@@ -20,13 +20,10 @@
 is mixed with.
 """
 
-from viff.runtime import increment_pc
-
 class ProbabilisticEqualityMixin:
     """This class implements probabilistic constant-round secure
     equality-testing of secret shared numbers."""
 
-    @increment_pc
     def equal(self, share_x, share_y):
         """Equality testing with secret shared result.
 
--- a/viff/paillier.py	Thu Sep 17 17:59:08 2009 +0200
+++ b/viff/paillier.py	Sat Sep 19 15:34:01 2009 +0200
@@ -27,7 +27,7 @@
 from twisted.internet.defer import Deferred, gatherResults
 import gmpy
 
-from viff.runtime import Runtime, increment_pc, Share, gather_shares
+from viff.runtime import Runtime, Share, gather_shares
 from viff.runtime import PAILLIER
 from viff.util import rand, find_random_prime
 
@@ -78,7 +78,6 @@
         else:
             self.peer = player
 
-    @increment_pc
     def prss_share_random(self, field):
         """Generate a share of a uniformly random element."""
         prfs = self.players[self.id].prfs(field.modulus)
@@ -94,7 +93,6 @@
         """
         return self.share(inputters, field, number)
 
-    @increment_pc
     def share(self, inputters, field, number=None):
         """Share *number* additively."""
         assert number is None or self.id in inputters
@@ -121,7 +119,6 @@
     def output(self, share, receivers=None):
         return self.open(share, receivers)
 
-    @increment_pc
     def open(self, share, receivers=None):
         """Open *share* to *receivers* (defaults to both players)."""
 
--- a/viff/passive.py	Thu Sep 17 17:59:08 2009 +0200
+++ b/viff/passive.py	Sat Sep 19 15:34:01 2009 +0200
@@ -22,8 +22,7 @@
 import operator
 
 from viff import shamir
-from viff.runtime import Runtime, increment_pc, Share, ShareList, \
-     gather_shares, preprocess
+from viff.runtime import Runtime, Share, ShareList, gather_shares, preprocess
 from viff.prss import prss, prss_lsb, prss_zero, prss_multi
 from viff.field import GF256, FieldElement
 from viff.util import rand, profile
@@ -57,7 +56,6 @@
     def output(self, share, receivers=None, threshold=None):
         return self.open(share, receivers, threshold)
 
-    @increment_pc
     def open(self, share, receivers=None, threshold=None):
         """Open a secret sharing.
 
@@ -175,7 +173,6 @@
         return result
 
     @profile
-    @increment_pc
     def mul(self, share_a, share_b):
         """Multiplication of shares.
 
@@ -232,7 +229,6 @@
         else:
             return share * (share ** (exponent-1))
 
-    @increment_pc
     def xor(self, share_a, share_b):
         field = share_a.field
         if not isinstance(share_b, Share):
@@ -245,7 +241,18 @@
         else:
             return share_a + share_b - 2 * share_a * share_b
 
-    @increment_pc
+    def prss_key(self):
+        """Create unique key for PRSS.
+
+        This increments the program counter and returns it as a tuple.
+        Each straight-line program (typically a callback attached to
+        some :class:`Deferred`) is executed in a context with unique
+        starting program counter. This ensures that consequetive calls
+        to PRSS-related methods will use unique program counters.
+        """
+        self.program_counter[-1] += 1
+        return tuple(self.program_counter)
+
     def prss_share(self, inputters, field, element=None):
         """Creates pseudo-random secret sharings.
 
@@ -273,7 +280,7 @@
         n = self.num_players
 
         # Key used for PRSS.
-        key = tuple(self.program_counter)
+        key = self.prss_key()
 
         # The shares for which we have all the keys.
         all_shares = []
@@ -314,7 +321,6 @@
         else:
             return result
 
-    @increment_pc
     def prss_share_random(self, field, binary=False):
         """Generate shares of a uniformly random element from the field given.
 
@@ -329,7 +335,7 @@
             modulus = field.modulus
 
         # Key used for PRSS.
-        prss_key = tuple(self.program_counter)
+        prss_key = self.prss_key()
         prfs = self.players[self.id].prfs(modulus)
         share = prss(self.num_players, self.id, field, prfs, prss_key)
 
@@ -354,7 +360,6 @@
         self.schedule_callback(result, finish, share, binary)
         return result
 
-    @increment_pc
     def prss_share_random_multi(self, field, quantity, binary=False):
         """Does the same as calling *quantity* times :meth:`prss_share_random`,
         but with less calls to the PRF. Sampling of a binary element is only
@@ -371,13 +376,12 @@
             modulus = field.modulus
 
         # Key used for PRSS.
-        prss_key = tuple(self.program_counter)
+        prss_key = self.prss_key()
         prfs = self.players[self.id].prfs(modulus ** quantity)
         shares = prss_multi(self.num_players, self.id, field, prfs, prss_key,
                             modulus, quantity)
         return [Share(self, field, share) for share in shares]
 
-    @increment_pc
     def prss_share_zero(self, field, quantity):
         """Generate *quantity* shares of the zero element from the
         field given.
@@ -385,13 +389,12 @@
         Communication cost: none.
         """
         # Key used for PRSS.
-        prss_key = tuple(self.program_counter)
+        prss_key = self.prss_key()
         prfs = self.players[self.id].prfs(field.modulus)
         zero_share = prss_zero(self.num_players, self.threshold, self.id,
                                field, prfs, prss_key, quantity)
         return [Share(self, field, zero_share[i]) for i in range(quantity)]
 
-    @increment_pc
     def prss_double_share(self, field, quantity):
         """Make *quantity* double-sharings using PRSS.
 
@@ -402,7 +405,6 @@
         z_2t = self.prss_share_zero(field, quantity)
         return (r_t, [r_t[i] + z_2t[i] for i in range(quantity)])
 
-    @increment_pc
     def prss_share_bit_double(self, field):
         """Share a random bit over *field* and GF256.
 
@@ -414,7 +416,7 @@
         n = self.num_players
         k = self.options.security_parameter
         prfs = self.players[self.id].prfs(2**k)
-        prss_key = tuple(self.program_counter)
+        prss_key = self.prss_key()
 
         b_p = self.prss_share_random(field, binary=True)
         r_p, r_lsb = prss_lsb(n, self.id, field, prfs, prss_key)
@@ -427,13 +429,12 @@
         # Use r_lsb to flip b as needed.
         return (b_p, b ^ r_lsb)
 
-    @increment_pc
     def prss_shamir_share_bit_double(self, field):
         """Shamir share a random bit over *field* and GF256."""
         n = self.num_players
         k = self.options.security_parameter
         prfs = self.players[self.id].prfs(2**k)
-        prss_key = tuple(self.program_counter)
+        prss_key = self.prss_key()
         inputters = range(1, self.num_players + 1)
 
         ri = rand.randint(0, 2**k - 1)
@@ -461,7 +462,6 @@
             result.append(share)
         return result
 
-    @increment_pc
     @preprocess("prss_powerchains")
     def prss_powerchain(self, max=7):
         """Generate a random secret share in GF256 and returns
@@ -483,7 +483,6 @@
         """
         return self.shamir_share(inputters, field, number, threshold)
 
-    @increment_pc
     def shamir_share(self, inputters, field, number=None, threshold=None):
         """Secret share *number* over *field* using Shamir's method.
 
--- a/viff/runtime.py	Thu Sep 17 17:59:08 2009 +0200
+++ b/viff/runtime.py	Sat Sep 19 15:34:01 2009 +0200
@@ -407,25 +407,6 @@
         reason.trap(ConnectionDone)
 
 
-def increment_pc(method):
-    """Make *method* automatically increment the program counter.
-
-    Adding this decorator to a :class:`Runtime` method will ensure
-    that the program counter is incremented correctly when entering
-    the method.
-    """
-
-    @wrapper(method)
-    def inc_pc_wrapper(self, *args, **kwargs):
-        try:
-            self.program_counter[-1] += 1
-            self.program_counter.append(0)
-            return method(self, *args, **kwargs)
-        finally:
-            self.program_counter.pop()
-    return inc_pc_wrapper
-
-
 def preprocess(generator):
     """Track calls to this method.
 
@@ -623,7 +604,6 @@
         dl = DeferredList(vars)
         self.schedule_callback(dl, lambda _: self.shutdown())
 
-    @increment_pc
     def schedule_callback(self, deferred, func, *args, **kwargs):
         """Schedule a callback on a deferred with the correct program
         counter.
@@ -637,7 +617,9 @@
         Any extra arguments are passed to the callback as with
         :meth:`addCallback`.
         """
+        self.program_counter[-1] += 1
         saved_pc = self.program_counter[:]
+        saved_pc.append(0)
 
         @wrapper(func)
         def callback_wrapper(*args, **kwargs):
@@ -673,7 +655,6 @@
         deferred.addCallback(queue_callback, self, fork)
         return self.schedule_callback(fork, func, *args, **kwargs)
 
-    @increment_pc
     def synchronize(self):
         """Introduce a synchronization point.
 
@@ -729,7 +710,6 @@
         self._expect_data(peer_id, SHARE, share)
         return share
 
-    @increment_pc
     def preprocess(self, program):
         """Generate preprocess material.
 
@@ -868,7 +848,7 @@
             self.depth_counter -= 1
             self.activation_counter = 0
 
-    def print_transferred_data():
+    def print_transferred_data(self):
         """Print the amount of transferred data for all connections."""
 
         for protocol in self.protocols.itervalues():
--- a/viff/test/test_basic_runtime.py	Thu Sep 17 17:59:08 2009 +0200
+++ b/viff/test/test_basic_runtime.py	Sat Sep 19 15:34:01 2009 +0200
@@ -18,7 +18,6 @@
 from twisted.internet.defer import Deferred, gatherResults
 
 from viff.test.util import RuntimeTestCase, protocol
-from viff.runtime import increment_pc
 
 
 class ProgramCounterTest(RuntimeTestCase):
@@ -32,26 +31,14 @@
     def test_simple_operation(self, runtime):
         """Test an operation which makes no further calls.
 
-        Each call should increment the program counter by one.
+        No callbacks are scheduled, and so the program counter is not
+        increased.
         """
+        self.assertEquals(runtime.program_counter, [0])
         runtime.synchronize()
-        self.assertEquals(runtime.program_counter, [1])
+        self.assertEquals(runtime.program_counter, [0])
         runtime.synchronize()
-        self.assertEquals(runtime.program_counter, [2])
-
-    @protocol
-    def test_complex_operation(self, runtime):
-        """Test an operation which makes nested calls.
-
-        This verifies that the program counter is only incremented by
-        one, even for a complex operation.
-        """
-        # Exclusive-or is calculated as x + y - 2 * x * y, so add,
-        # sub, and mul are called.
-        runtime.xor(self.Zp(0), self.Zp(1))
-        self.assertEquals(runtime.program_counter, [1])
-        runtime.xor(self.Zp(0), self.Zp(1))
-        self.assertEquals(runtime.program_counter, [2])
+        self.assertEquals(runtime.program_counter, [0])
 
     @protocol
     def test_callback(self, runtime):
@@ -62,62 +49,32 @@
         """
 
         def verify_program_counter(_):
+            # The callback is run with its own sub-program counter.
             self.assertEquals(runtime.program_counter, [1, 0])
 
         d = Deferred()
+
+        self.assertEquals(runtime.program_counter, [0])
+
+        # Scheduling a callback increases the program counter.
         runtime.schedule_callback(d, verify_program_counter)
-
-        runtime.synchronize()
-        self.assertEquals(runtime.program_counter, [2])
+        self.assertEquals(runtime.program_counter, [1])
 
         # Now trigger verify_program_counter.
         d.callback(None)
 
     @protocol
-    def test_nested_calls(self, runtime):
-        """Test Runtime methods that call other methods.
-
-        We create a couple of functions that are used as fake methods.
-        """
-
-        @increment_pc
-        def method_a(runtime):
-            # First top-level call, so first entry is 1. No calls to
-            # other methods decorated with increment_pc has been made,
-            # so the second entry is 0.
-            self.assertEquals(runtime.program_counter, [1, 0])
-            method_b(runtime, 1)
-
-            self.assertEquals(runtime.program_counter, [1, 1])
-            method_b(runtime, 2)
-
-            # At this point two sub-calls has been made:
-            self.assertEquals(runtime.program_counter, [1, 2])
-
-        @increment_pc
-        def method_b(runtime, count):
-            # This method is called twice from method_a:
-            self.assertEquals(runtime.program_counter, [1, count, 0])
-
-        # Zero top-level calls:
-        self.assertEquals(runtime.program_counter, [0])
-        method_a(runtime)
-
-        # One top-level call:
-        self.assertEquals(runtime.program_counter, [1])
-
-    @protocol
     def test_multiple_callbacks(self, runtime):
 
         d1 = Deferred()
         d2 = Deferred()
 
         def verify_program_counter(_, count):
-            self.assertEquals(runtime.program_counter, [1, count, 0])
+            self.assertEquals(runtime.program_counter, [count, 0])
 
-        @increment_pc
         def method_a(runtime):
-            self.assertEquals(runtime.program_counter, [1, 0])
+            # No calls to schedule_callback yet.
+            self.assertEquals(runtime.program_counter, [0])
 
             runtime.schedule_callback(d1, verify_program_counter, 1)
             runtime.schedule_callback(d2, verify_program_counter, 2)
--- a/viff/test/test_thresholds.py	Thu Sep 17 17:59:08 2009 +0200
+++ b/viff/test/test_thresholds.py	Sat Sep 19 15:34:01 2009 +0200
@@ -91,66 +91,26 @@
     num_players = 3
     threshold = 1
 
-
 class Players4Threshold1Test(Tests, RuntimeTestCase):
     num_players = 4
     threshold = 1
 
-
-class Players5Threshold1Test(Tests, RuntimeTestCase):
-    num_players = 5
-    threshold = 1
-
 class Players5Threshold2Test(Tests, RuntimeTestCase):
     num_players = 5
     threshold = 2
 
-
-class Players6Threshold1Test(Tests, RuntimeTestCase):
-    num_players = 6
-    threshold = 1
-
 class Players6Threshold2Test(Tests, RuntimeTestCase):
     num_players = 6
     threshold = 2
 
-
-class Players7Threshold1Test(Tests, RuntimeTestCase):
-    num_players = 7
-    threshold = 1
-
-class Players7Threshold2Test(Tests, RuntimeTestCase):
-    num_players = 7
-    threshold = 2
-
 class Players7Threshold3Test(Tests, RuntimeTestCase):
     num_players = 7
     threshold = 3
 
-class Players8Threshold1Test(Tests, RuntimeTestCase):
-    num_players = 8
-    threshold = 1
-
-class Players8Threshold2Test(Tests, RuntimeTestCase):
-    num_players = 8
-    threshold = 2
-
 class Players8Threshold3Test(Tests, RuntimeTestCase):
     num_players = 8
     threshold = 3
 
-class Players9Threshold1Test(Tests, RuntimeTestCase):
-    num_players = 9
-    threshold = 1
-
-class Players9Threshold2Test(Tests, RuntimeTestCase):
-    num_players = 9
-    threshold = 2
-
-class Players9Threshold3Test(Tests, RuntimeTestCase):
-    num_players = 9
-    threshold = 3
-
 class Players9Threshold4Test(Tests, RuntimeTestCase):
     num_players = 9
     threshold = 4