From 8be14b33eec5cd8799554b3d171ce685d36b6490 Mon Sep 17 00:00:00 2001 From: Jon Michael Aanes Date: Mon, 10 Jun 2024 12:45:05 +0200 Subject: [PATCH 1/4] Fallback for zero case --- .../common/math/integer/ProductSIntList.java | 64 +++++++++++-------- .../lib/common/math/integer/SumSIntList.java | 63 ++++++++++-------- 2 files changed, 75 insertions(+), 52 deletions(-) diff --git a/lib/common/src/main/java/dk/alexandra/fresco/lib/common/math/integer/ProductSIntList.java b/lib/common/src/main/java/dk/alexandra/fresco/lib/common/math/integer/ProductSIntList.java index 6860b90e5..92cb55975 100644 --- a/lib/common/src/main/java/dk/alexandra/fresco/lib/common/math/integer/ProductSIntList.java +++ b/lib/common/src/main/java/dk/alexandra/fresco/lib/common/math/integer/ProductSIntList.java @@ -7,46 +7,58 @@ import dk.alexandra.fresco.framework.value.SInt; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** - * ComputationBuilder for multiplying a list of SInts. + * Builder for a {@link Computation}s that computes the product of a list of {@link SInts} + * secret-shared values. + * + *

Empty lists are allowed, and will always produce one. */ public class ProductSIntList implements Computation { private final List> input; /** - * Creates a new ProductSIntList. + * Creates a new {@link ProductSIntList}. * - * @param list the list to sum + * @param list the list to sum. Not nullable. */ public ProductSIntList(List> list) { - input = list; + input = Objects.requireNonNull(list); } @Override public DRes buildComputation(ProtocolBuilderNumeric iterationBuilder) { - return iterationBuilder.seq(seq -> - () -> input - ).whileLoop( - (inputs) -> inputs.size() > 1, - (seq, inputs) -> seq.par(parallel -> { - List> out = new ArrayList<>(); - Numeric numericBuilder = parallel.numeric(); - DRes left = null; - for (DRes input1 : inputs) { - if (left == null) { - left = input1; - } else { - out.add(numericBuilder.mult(left, input1)); - left = null; - } - } - if (left != null) { - out.add(left); - } - return () -> out; - }) - ).seq((builder, currentInput) -> currentInput.get(0)); + // Fast case if there is nothing to compute. + if (input.isEmpty()) { + return iterationBuilder.seq(seq -> seq.numeric().known(1)); + } + + // Slow case when there are elements to compute on. + return iterationBuilder + .seq(seq -> () -> input) + .whileLoop( + (inputs) -> inputs.size() > 1, + (seq, inputs) -> + seq.par( + parallel -> { + List> out = new ArrayList<>(); + Numeric numericBuilder = parallel.numeric(); + DRes left = null; + for (DRes input1 : inputs) { + if (left == null) { + left = input1; + } else { + out.add(numericBuilder.mult(left, input1)); + left = null; + } + } + if (left != null) { + out.add(left); + } + return () -> out; + })) + .seq((builder, currentInput) -> currentInput.get(0)); } } diff --git a/lib/common/src/main/java/dk/alexandra/fresco/lib/common/math/integer/SumSIntList.java b/lib/common/src/main/java/dk/alexandra/fresco/lib/common/math/integer/SumSIntList.java index 9092d413d..f0947c089 100644 --- a/lib/common/src/main/java/dk/alexandra/fresco/lib/common/math/integer/SumSIntList.java +++ b/lib/common/src/main/java/dk/alexandra/fresco/lib/common/math/integer/SumSIntList.java @@ -7,46 +7,57 @@ import dk.alexandra.fresco.framework.value.SInt; import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** - * ComputationBuilder for summing a list of SInts. + * Builder for a {@link Computation}s that sum lists of {@link SInts} secret-shared values. + * + *

Empty lists are allowed, and will always produce zero. */ public class SumSIntList implements Computation { private final List> input; /** - * Creates a new SumSIntList. + * Creates a new {@link SumSIntList}. * - * @param list the list to sum + * @param list the list to sum. Not nullable. */ public SumSIntList(List> list) { - input = list; + input = Objects.requireNonNull(list); } @Override public DRes buildComputation(ProtocolBuilderNumeric iterationBuilder) { - return iterationBuilder.seq(seq -> - () -> input - ).whileLoop( - (inputs) -> inputs.size() > 1, - (seq, inputs) -> seq.par(parallel -> { - List> out = new ArrayList<>(); - Numeric numericBuilder = parallel.numeric(); - DRes left = null; - for (DRes input1 : inputs) { - if (left == null) { - left = input1; - } else { - out.add(numericBuilder.add(left, input1)); - left = null; - } - } - if (left != null) { - out.add(left); - } - return () -> out; - }) - ).seq((builder, currentInput) -> currentInput.get(0)); + // Fast case if there is nothing to sum. + if (input.isEmpty()) { + return iterationBuilder.seq(seq -> seq.numeric().known(0)); + } + + // Slow case when there are elements to sum. + return iterationBuilder + .seq(seq -> () -> input) + .whileLoop( + (inputs) -> inputs.size() > 1, + (seq, inputs) -> + seq.par( + parallel -> { + List> out = new ArrayList<>(); + Numeric numericBuilder = parallel.numeric(); + DRes left = null; + for (DRes input1 : inputs) { + if (left == null) { + left = input1; + } else { + out.add(numericBuilder.add(left, input1)); + left = null; + } + } + if (left != null) { + out.add(left); + } + return () -> out; + })) + .seq((builder, currentInput) -> currentInput.get(0)); } } From 3a15a8d83dd8360c2be1aed3c42bc9fa3fbd106d Mon Sep 17 00:00:00 2001 From: Jon Michael Aanes Date: Tue, 11 Jun 2024 16:23:18 +0200 Subject: [PATCH 2/4] More thorough testing of product and sum calls. --- .../common/math/integer/ProductSIntList.java | 2 +- .../lib/common/math/integer/SumSIntList.java | 2 +- .../math/integer/TestProductAndSum.java | 92 ++++++++++++------- 3 files changed, 63 insertions(+), 33 deletions(-) diff --git a/lib/common/src/main/java/dk/alexandra/fresco/lib/common/math/integer/ProductSIntList.java b/lib/common/src/main/java/dk/alexandra/fresco/lib/common/math/integer/ProductSIntList.java index 92cb55975..f88be1fc0 100644 --- a/lib/common/src/main/java/dk/alexandra/fresco/lib/common/math/integer/ProductSIntList.java +++ b/lib/common/src/main/java/dk/alexandra/fresco/lib/common/math/integer/ProductSIntList.java @@ -13,7 +13,7 @@ * Builder for a {@link Computation}s that computes the product of a list of {@link SInts} * secret-shared values. * - *

Empty lists are allowed, and will always produce one. + *

Empty lists are allowed, and will always produce a value of {@code 1}. */ public class ProductSIntList implements Computation { diff --git a/lib/common/src/main/java/dk/alexandra/fresco/lib/common/math/integer/SumSIntList.java b/lib/common/src/main/java/dk/alexandra/fresco/lib/common/math/integer/SumSIntList.java index f0947c089..b03b8f826 100644 --- a/lib/common/src/main/java/dk/alexandra/fresco/lib/common/math/integer/SumSIntList.java +++ b/lib/common/src/main/java/dk/alexandra/fresco/lib/common/math/integer/SumSIntList.java @@ -12,7 +12,7 @@ /** * Builder for a {@link Computation}s that sum lists of {@link SInts} secret-shared values. * - *

Empty lists are allowed, and will always produce zero. + *

Empty lists are allowed, and will always produce a value of {@code 0}. */ public class SumSIntList implements Computation { diff --git a/lib/common/src/test/java/dk/alexandra/fresco/lib/common/math/integer/TestProductAndSum.java b/lib/common/src/test/java/dk/alexandra/fresco/lib/common/math/integer/TestProductAndSum.java index 65bb34a7a..6c0b4a84a 100644 --- a/lib/common/src/test/java/dk/alexandra/fresco/lib/common/math/integer/TestProductAndSum.java +++ b/lib/common/src/test/java/dk/alexandra/fresco/lib/common/math/integer/TestProductAndSum.java @@ -13,15 +13,43 @@ import java.math.BigInteger; import java.util.Arrays; import java.util.List; -import java.util.stream.Collectors; +/** Test of {@link ProductSIntList} and {@link SumSIntList}. */ public class TestProductAndSum { - public static class TestProduct - extends TestThreadFactory { + private static final class TestCase { + public final BigInteger expectedOutput; + public final List inputs; + + public TestCase(long expectedOutput, long... inputs) { + this.expectedOutput = BigInteger.valueOf(expectedOutput); + this.inputs = Arrays.stream(inputs).mapToObj(BigInteger::valueOf).toList(); + } + } - List inputs = Arrays.asList(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L).stream().map( - BigInteger::valueOf).collect(Collectors.toList()); + private static final List TEST_CASES_SUM = + List.of( + new TestCase(0), + new TestCase(123, 123), + new TestCase(2, 1, 1), + new TestCase(4, 2, 2), + new TestCase(6, 3, 3), + new TestCase(15, 1, 2, 4, 8), + new TestCase(55, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); + + private static final List TEST_CASES_PRODUCT = + List.of( + new TestCase(1), + new TestCase(123, 123), + new TestCase(1, 1, 1), + new TestCase(4, 2, 2), + new TestCase(9, 3, 3), + new TestCase(64, 1, 2, 4, 8), + new TestCase(3628800, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); + + /** Test of {@link ProductSIntList}. */ + public static final class TestProduct + extends TestThreadFactory { @Override public TestThread next() { @@ -29,45 +57,47 @@ public TestThread next() { @Override public void test() throws Exception { - // define functionality to be tested - Application testApplication = - root -> { - List> closed = inputs.stream().map(root.numeric()::known) - .collect(Collectors.toList()); - DRes result = AdvancedNumeric.using(root).product(closed); - DRes open = root.numeric().open(result); - return () -> open.out(); - }; - BigInteger output = runApplication(testApplication); - assertEquals(output, inputs.stream().reduce(BigInteger.ONE, (a, b) -> a.multiply(b))); + for (final TestCase testCase : TEST_CASES_PRODUCT) { + // define functionality to be tested + Application testApplication = + root -> { + List> closed = + testCase.inputs.stream().map(root.numeric()::known).toList(); + DRes result = AdvancedNumeric.using(root).product(closed); + DRes open = root.numeric().open(result); + return () -> open.out(); + }; + BigInteger output = runApplication(testApplication); + assertEquals(testCase.expectedOutput, output); + } } }; } } - public static class TestSum + /** Test of {@link SumSIntList}. */ + public static final class TestSum extends TestThreadFactory { - List inputs = Arrays.asList(1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L).stream().map( - BigInteger::valueOf).collect(Collectors.toList()); - @Override public TestThread next() { return new TestThread() { @Override public void test() throws Exception { - // define functionality to be tested - Application testApplication = - root -> { - List> closed = inputs.stream().map(root.numeric()::known) - .collect(Collectors.toList()); - DRes result = AdvancedNumeric.using(root).sum(closed); - DRes open = root.numeric().open(result); - return () -> open.out(); - }; - BigInteger output = runApplication(testApplication); - assertEquals(output, inputs.stream().reduce(BigInteger.ZERO, (a, b) -> a.add(b))); + for (final TestCase testCase : TEST_CASES_SUM) { + // define functionality to be tested + Application testApplication = + root -> { + List> closed = + testCase.inputs.stream().map(root.numeric()::known).toList(); + DRes result = AdvancedNumeric.using(root).sum(closed); + DRes open = root.numeric().open(result); + return () -> open.out(); + }; + BigInteger output = runApplication(testApplication); + assertEquals(testCase.expectedOutput, output); + } } }; } From 81fc5070c30a3ac5c523c8c07aae7195d8fd9591 Mon Sep 17 00:00:00 2001 From: Jon Michael Aanes Date: Tue, 11 Jun 2024 16:43:17 +0200 Subject: [PATCH 3/4] Disappointingly toList is not available before Java 16. --- .../lib/common/math/integer/TestProductAndSum.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/common/src/test/java/dk/alexandra/fresco/lib/common/math/integer/TestProductAndSum.java b/lib/common/src/test/java/dk/alexandra/fresco/lib/common/math/integer/TestProductAndSum.java index 6c0b4a84a..aab1e526d 100644 --- a/lib/common/src/test/java/dk/alexandra/fresco/lib/common/math/integer/TestProductAndSum.java +++ b/lib/common/src/test/java/dk/alexandra/fresco/lib/common/math/integer/TestProductAndSum.java @@ -23,7 +23,10 @@ private static final class TestCase { public TestCase(long expectedOutput, long... inputs) { this.expectedOutput = BigInteger.valueOf(expectedOutput); - this.inputs = Arrays.stream(inputs).mapToObj(BigInteger::valueOf).toList(); + this.inputs = + Arrays.stream(inputs) + .mapToObj(BigInteger::valueOf) + .collect(Collectors.toUnmodifiableList()); } } @@ -62,7 +65,9 @@ public void test() throws Exception { Application testApplication = root -> { List> closed = - testCase.inputs.stream().map(root.numeric()::known).toList(); + testCase.inputs.stream() + .map(root.numeric()::known) + .collect(Collectors.toUnmodifiableList()); DRes result = AdvancedNumeric.using(root).product(closed); DRes open = root.numeric().open(result); return () -> open.out(); @@ -90,7 +95,9 @@ public void test() throws Exception { Application testApplication = root -> { List> closed = - testCase.inputs.stream().map(root.numeric()::known).toList(); + testCase.inputs.stream() + .map(root.numeric()::known) + .collect(Collectors.toUnmodifiableList()); DRes result = AdvancedNumeric.using(root).sum(closed); DRes open = root.numeric().open(result); return () -> open.out(); From daaa6f44834472696b5cd9612b5861754a49a044 Mon Sep 17 00:00:00 2001 From: Jon Michael Aanes Date: Tue, 11 Jun 2024 16:44:01 +0200 Subject: [PATCH 4/4] Remember import --- .../fresco/lib/common/math/integer/TestProductAndSum.java | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/common/src/test/java/dk/alexandra/fresco/lib/common/math/integer/TestProductAndSum.java b/lib/common/src/test/java/dk/alexandra/fresco/lib/common/math/integer/TestProductAndSum.java index aab1e526d..9b541e62a 100644 --- a/lib/common/src/test/java/dk/alexandra/fresco/lib/common/math/integer/TestProductAndSum.java +++ b/lib/common/src/test/java/dk/alexandra/fresco/lib/common/math/integer/TestProductAndSum.java @@ -13,6 +13,7 @@ import java.math.BigInteger; import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; /** Test of {@link ProductSIntList} and {@link SumSIntList}. */ public class TestProductAndSum {