content: package dk.alexandra.fresco.suite.spdz2k;
  
  import dk.alexandra.fresco.framework.DRes;
  import dk.alexandra.fresco.framework.builder.numeric.BuilderFactoryNumeric;
  import dk.alexandra.fresco.framework.builder.numeric.Numeric;
  import dk.alexandra.fresco.framework.builder.numeric.ProtocolBuilderNumeric;
  import dk.alexandra.fresco.framework.value.SInt;
  import dk.alexandra.fresco.lib.field.integer.BasicNumericContext;
  import dk.alexandra.fresco.suite.spdz2k.datatypes.CompUInt;
  import dk.alexandra.fresco.suite.spdz2k.datatypes.CompUIntFactory;
  import dk.alexandra.fresco.suite.spdz2k.datatypes.Spdz2kSInt;
  import dk.alexandra.fresco.suite.spdz2k.protocols.computations.Spdz2kInputComputation;
  import dk.alexandra.fresco.suite.spdz2k.protocols.natives.Spdz2kAddKnownProtocol;
  import dk.alexandra.fresco.suite.spdz2k.protocols.natives.Spdz2kKnownSIntProtocol;
  import dk.alexandra.fresco.suite.spdz2k.protocols.natives.Spdz2kMultiplyProtocol;
  import dk.alexandra.fresco.suite.spdz2k.protocols.natives.Spdz2kOutputSinglePartyProtocol;
  import dk.alexandra.fresco.suite.spdz2k.protocols.natives.Spdz2kOutputToAllProtocol;
  import dk.alexandra.fresco.suite.spdz2k.protocols.natives.Spdz2kRandomBitProtocol;
  import dk.alexandra.fresco.suite.spdz2k.protocols.natives.Spdz2kRandomElementProtocol;
  import dk.alexandra.fresco.suite.spdz2k.protocols.natives.Spdz2kSubtractFromKnownProtocol;
  import java.math.BigInteger;
  import java.util.Objects;
  
  /**
   * Basic native builder for the SPDZ2k protocol suite.
   *
   * @param <PlainT> the type representing open values
   */
  public class Spdz2kBuilder<PlainT extends CompUInt<?, ?, PlainT>> implements
      BuilderFactoryNumeric {
  
    private final CompUIntFactory<PlainT> factory;
    private final BasicNumericContext numericContext;
  
    public Spdz2kBuilder(CompUIntFactory<PlainT> factory, BasicNumericContext numericContext) {
      this.factory = factory;
      this.numericContext = numericContext;
    }
  
    @Override
    public BasicNumericContext getBasicNumericContext() {
      return numericContext;
    }
  
    @Override
    public Numeric createNumeric(ProtocolBuilderNumeric builder) {
      return new Numeric() {
        @Override
        public DRes<SInt> add(DRes<SInt> a, DRes<SInt> b) {
          return () -> toSpdz2kSInt(a).add(toSpdz2kSInt(b));
        }
  
        @Override
        public DRes<SInt> add(BigInteger a, DRes<SInt> b) {
          return builder.append(new Spdz2kAddKnownProtocol<>(factory.createElement(a), b));
        }
  
        @Override
        public DRes<SInt> sub(DRes<SInt> a, DRes<SInt> b) {
          return () -> (toSpdz2kSInt(a)).subtract(toSpdz2kSInt(b));
        }
  
        @Override
        public DRes<SInt> sub(BigInteger a, DRes<SInt> b) {
          return builder.append(
              new Spdz2kSubtractFromKnownProtocol<>(factory.createElement(a), b));
        }
  
        @Override
        public DRes<SInt> sub(DRes<SInt> a, BigInteger b) {
          return builder.append(
              new Spdz2kAddKnownProtocol<>(factory.createElement(b).negateUInt(), a));
        }
  
        @Override
        public DRes<SInt> mult(DRes<SInt> a, DRes<SInt> b) {
          return builder.append(new Spdz2kMultiplyProtocol<>(a, b));
        }
  
        @Override
        public DRes<SInt> mult(BigInteger a, DRes<SInt> b) {
          return () -> toSpdz2kSInt(b).multiply(factory.createElement(a));
        }
  
        @Override
        public DRes<SInt> randomBit() {
          return builder.append(new Spdz2kRandomBitProtocol<>());
        }
  
        @Override
        public DRes<SInt> randomElement() {
          return builder.append(new Spdz2kRandomElementProtocol<>());
        }
  
        @Override
        public DRes<SInt> known(BigInteger value) {
          return builder.append(new Spdz2kKnownSIntProtocol<>(factory.createElement(value)));
        }
  
        @Override
        public DRes<SInt> input(BigInteger value, int inputParty) {
          return builder.seq(
              new Spdz2kInputComputation<>(factory.createElement(value), inputParty)
          );
        }
  
        @Override
        public DRes<BigInteger> open(DRes<SInt> secretShare) {
          return builder.append(new Spdz2kOutputToAllProtocol<>(secretShare));
        }
  
        @Override
        public DRes<BigInteger> open(DRes<SInt> secretShare, int outputParty) {
          return builder.append(new Spdz2kOutputSinglePartyProtocol<>(secretShare, outputParty));
        }
      };
    }
  
    /**
     * Get result from deferred and downcast result to {@link Spdz2kSInt<PlainT>}.
     */
    private Spdz2kSInt<PlainT> toSpdz2kSInt(DRes<SInt> value) {
      return Objects.requireNonNull((Spdz2kSInt<PlainT>) value.out());
    }
  }
  