<jupyter_start><jupyter_text>.. _modulus:Modulus=======In this notebook we will use the Cicada modulus operator to compute the element-wise remainder of division. Note that modulus is a `private-public` operation: the `dividend` is a secret-shared value, while the `divisor` is a public value known to all players.  The resulting `remainder` will be a secret-shared value, so that the privacy of the dividend is preserved.The implementation is dependent on our integer division which is approximate, so that this method is probabilistic.Importantly, the divisor is an **unencoded value**.In the example, we compute :math:`144409 \bmod 117 = 31`.<jupyter_code>import logging

import numpy

import cicada.additive
import cicada.communicator

logging.basicConfig(level=logging.INFO)

@cicada.communicator.NNGCommunicator.run(world_size=3)
def main(communicator):
    log = cicada.Logger(logging.getLogger(), communicator)
    protocol = cicada.additive.AdditiveProtocol(communicator)

    # Player 0 will provide the secret divisor.
    a = numpy.array(144409)
    # Every player knows the modulus.
    for i in range(10):
        b = numpy.array(117+i)

        log.info(f"Dividend: {a}", src=0)
        log.info(f"Modulus: {b}", src=1)

        a_share = protocol.share(src=0, secret=protocol.encoder.encode(a), shape=())
        remainder_share = protocol.private_public_mod(a_share, b)
        remainder = protocol.encoder.decode(protocol.reveal(remainder_share))

        log.info(f"Player {communicator.rank} remainder: {remainder} expected {a%b}", src=0)

main();<jupyter_output>INFO:cicada.communicator.nng:Player 0 rendezvous with tcp://127.0.0.1:61248 from tcp://127.0.0.1:61248.
INFO:cicada.communicator.nng:Player 1 rendezvous with tcp://127.0.0.1:61248 from tcp://127.0.0.1:61249.
INFO:cicada.communicator.nng:Player 2 rendezvous with tcp://127.0.0.1:61248 from tcp://127.0.0.1:61250.
INFO:cicada.communicator.nng:Comm 'world' player 1 communicator ready.
INFO:cicada.communicator.nng:Comm 'world' player 0 communicator ready.
INFO:cicada.communicator.nng:Comm 'world' player 2 communicator ready.
INFO:root:Dividend: 144409
INFO:root:Modulus: 117
INFO:root:Player 0 remainder: 35.256103515625 expected 31
INFO:root:Dividend: 144409
INFO:root:Modulus: 118
INFO:root:Player 0 remainder: 101.36129760742188 expected 95
INFO:root:Dividend: 144409
INFO:root:Modulus: 119
INFO:root:Player 0 remainder: 189.50155639648438 expected 62
INFO:root:Dividend: 144409
INFO:root:Modulus: 120
INFO:root:Player 0 remainder: 35.256103515625 expected 49
INFO:root:Dividend: 144409
INFO:root:[...]<jupyter_text>As you can see, the result revealed to the players is correct.<jupyter_code>.. seealso:: :ref:`division`<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11366959064327485, 'fraction_numerical': 0.07894736842105263, 'mean_word_length': 5.234624145785877, 'pattern_counts': {'":': 0, '<': 8, '<?xml version=': 0, '>': 8, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 6, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '471192', 'license': 'Apache-2.0', 'n_tokens_mistral': 1070, 'n_tokens_neox': 898, 'n_words': 242, 'path': 'docs/user-guide/modulus.ipynb', 'repo_name': 'cicada-mpc/cicada-mpc', 'size': '2736'}