content: package dk.alexandra.fresco.lib.common.compare;
  
  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.util.Pair;
  import dk.alexandra.fresco.framework.value.SInt;
  import dk.alexandra.fresco.lib.common.compare.lt.BitLessThanOpen;
  import dk.alexandra.fresco.lib.common.compare.lt.Carry;
  import dk.alexandra.fresco.lib.common.compare.lt.LessThanOrEquals;
  import dk.alexandra.fresco.lib.common.compare.lt.LessThanZero;
  import dk.alexandra.fresco.lib.common.compare.min.ArgMin;
  import dk.alexandra.fresco.lib.common.compare.zerotest.ZeroTestConstRounds;
  import dk.alexandra.fresco.lib.common.compare.zerotest.ZeroTestLogRounds;
  import dk.alexandra.fresco.lib.common.util.SIntPair;
  import java.math.BigInteger;
  import java.util.List;
  
  /**
   * Default way of producing the protocols within the interface. This default class can be
   * overwritten when implementing {@link BuilderFactoryNumeric} if the protocol suite has a better
   * and more efficient way of constructing the protocols.
   */
  public class DefaultComparison implements Comparison {
  
    // Security parameter used by protocols using rightshifts and/or additive masks.
    private final int magicSecureNumber = 60;
    private final int maxBitLength;
    private final ProtocolBuilderNumeric builder;
  
    DefaultComparison(ProtocolBuilderNumeric builder) {
      this.maxBitLength = builder.getBasicNumericContext().getMaxBitLength();
      this.builder = builder;
    }
  
    @Override
    public DRes<SInt> compareLEQLong(DRes<SInt> x, DRes<SInt> y) {
      int bitLength = maxBitLength * 2;
      LessThanOrEquals leqProtocol = new LessThanOrEquals(bitLength, magicSecureNumber, x, y);
      return builder.seq(leqProtocol);
    }
  
    @Override
    public DRes<SInt> equals(DRes<SInt> x, DRes<SInt> y, int bitlength, Algorithm algorithm) {
      DRes<SInt> diff = builder.numeric().sub(x, y);
      return compareZero(diff, bitlength, algorithm);
    }
  
    @Override
    public DRes<SInt> equals(DRes<SInt> x, DRes<SInt> y) {
      int maxBitLength = builder.getBasicNumericContext().getMaxBitLength();
      return equals(x, y, maxBitLength);
    }
  
    @Override
    public DRes<SInt> compareLEQ(DRes<SInt> x, DRes<SInt> y) {
      return compareLT(builder.numeric().sub(x, 1), y);
    }
  
    @Override
    public DRes<SInt> compareLT(DRes<SInt> x, DRes<SInt> y, int bitlength, Algorithm algorithm) {
      if (bitlength > builder.getBasicNumericContext().getMaxBitLength()) {
        throw new IllegalArgumentException("The bitlength is more than allowed for elements.");
      }
      if (algorithm == Algorithm.LOG_ROUNDS) {
        if (builder.getBasicNumericContext().getStatisticalSecurityParam() + bitlength
            > builder.getBasicNumericContext().getModulus().bitLength()) {
          throw new IllegalArgumentException(
              "The max bitlength plus the statistical security parameter overflows the size of the modulus.");
        }
        DRes<SInt> difference = builder.numeric().sub(x, y);
        return builder.seq(new LessThanZero(difference, bitlength));
      } else {
        throw new UnsupportedOperationException("Not implemented yet");
      }
    }
  
    @Override
    public DRes<SInt> compareLT(DRes<SInt> x, DRes<SInt> y) {
      int maxBitLength = builder.getBasicNumericContext().getMaxBitLength();
      return compareLT(x, y, maxBitLength);
    }
  
    @Override
    public DRes<SInt> compareLTBits(BigInteger openValue, DRes<List<DRes<SInt>>> secretBits) {
      return builder.seq(new BitLessThanOpen(openValue, secretBits));
    }
  
    @Override
    public DRes<SInt> sign(DRes<SInt> x) {
      Numeric input = builder.numeric();
      // TODO create a compareLeqOrEqZero on comparison builder
      DRes<SInt> compare = compareLEQ(input.known(BigInteger.ZERO), x);
      BigInteger oint = BigInteger.valueOf(2);
      Numeric numericBuilder = builder.numeric();
      DRes<SInt> twice = numericBuilder.mult(oint, compare);
      return numericBuilder.sub(twice, BigInteger.valueOf(1));
    }
  
    @Override
    public DRes<SInt> compareZero(DRes<SInt> x, int bitlength, Algorithm algorithm) {
      if (bitlength > builder.getBasicNumericContext().getMaxBitLength()) {
        throw new IllegalArgumentException("The bitlength is more than allowed for elements.");
      }
      if (builder.getBasicNumericContext().getStatisticalSecurityParam()
          + bitlength > builder.getBasicNumericContext().getModulus().bitLength()) {
        throw new IllegalArgumentException(
            "The max bitlength plus the statistical security parameter overflows the size of the modulus.");
      }
      if (algorithm == Algorithm.LOG_ROUNDS) {
        return builder.seq(new ZeroTestLogRounds(x, bitlength));
      } else {
        return builder.seq(new ZeroTestConstRounds(x, bitlength));
      }
    }
  
    @Override
    public DRes<Pair<List<DRes<SInt>>, SInt>> argMin(List<DRes<SInt>> xs) {
      return builder.seq(new ArgMin(xs));
    }
  
  }
  