Dining cryptographers


from random import randrange
from itertools import combinations

# Dining cryptographers


class Cryptographer(object):
    "Dining cryptographer"

    def __init__(self, name):
        self.name = name
        self.partners = dict()
        self.pubBit = 0

    def __repr__(self):
        return self.name

    def AddPartner(self, otherCryptographer):
        randBit = randrange(2)
        print(
            "Negotiating secret bit: "
            + self.name
            + "->"
            + otherCryptographer.name
        )
        print("  value:" + repr(randBit))
        otherCryptographer.partners.update(
            [[self.name, randBit]]
        )
        self.partners.update(
            [[otherCryptographer.name, randBit]]
        )

    def Prepare(self):
        self.pubBit = (sum(self.partners.values())) % 2

    def SendBit(self, bt):
        self.Prepare()
        self.pubBit = (self.pubBit + bt) % 2


class DiningCryptographers(object):
    "Dining Cryptographers 1-bit channel"

    def __init__(self, Cryptographers):
        if type(Cryptographers) == int:
            Cr = []
            for i in range(0, Cryptographers):
                Cr.append(
                    Cryptographer(
                        "Cryptographer #" + repr(i + 1)
                    )
                )
        Cryptographers = Cr
        self.Cryptographers = Cryptographers

    def __getitem__(self, i):
        return self.Cryptographers[i]

    def negotiate(self):
        for [A, B] in combinations(self.Cryptographers, 2):
            A.AddPartner(B)
        for A in self.Cryptographers:
            A.Prepare()

    def reveal(self):
        res = 0
        for A in self.Cryptographers:
            print(repr(A) + " sends:" + repr(A.pubBit))
            res = (res + A.pubBit) % 2
        return res


DiningCryptographers(13)