/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.codahale.shamir.tests;

import static com.codahale.shamir.Generators.byteArrays;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.quicktheories.quicktheories.QuickTheory.qt;
import static org.quicktheories.quicktheories.generators.SourceDSL.integers;

import com.codahale.shamir.SecretSharing;
import com.codahale.shamir.Share;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.Arrays;
import java.util.Collections;
import org.junit.Test;

public class SecretSharingTest {

  @Test
  public void thresholdTooLow() throws Exception {
    assertThatThrownBy(() -> SecretSharing.split(1, 1, new byte[10]))
        .isInstanceOf(IllegalArgumentException.class)
        .hasMessage("K must be > 1");
  }

  @Test
  public void thresholdTooHigh() throws Exception {
    assertThatThrownBy(() -> SecretSharing.split(1, 2, new byte[10]))
        .isInstanceOf(IllegalArgumentException.class)
        .hasMessage("N must be >= K");
  }

  @Test
  public void emptyShares() throws Exception {
    assertThatThrownBy(() -> SecretSharing.combine(Collections.emptySet()))
        .isInstanceOf(IllegalArgumentException.class)
        .hasMessage("No shares provided");
  }

  @Test
  public void irregularShares() throws Exception {
    final Share one = new Share(1, new byte[1]);
    final Share two = new Share(2, new byte[2]);

    assertThatThrownBy(() -> SecretSharing.combine(ImmutableSet.of(one, two)))
        .isInstanceOf(IllegalArgumentException.class)
        .hasMessage("Varying lengths of share values");
  }

  @Test
  public void singleByteSecret() throws Exception {
    final byte[] secret = new byte[]{-41};
    assertThat(SecretSharing.combine(SecretSharing.split(8, 3, secret)))
        .isEqualTo(secret);
  }

  @Test
  public void moreThanByteMaxValueShares() throws Exception {
    final byte[] secret = new byte[]{-41};
    assertThat(SecretSharing.combine(SecretSharing.split(200, 3, secret)))
        .isEqualTo(secret);
  }

  @Test
  public void tooManyShares() throws Exception {
    final byte[] secret = new byte[]{-41};
    assertThatThrownBy(() -> SecretSharing.split(2_000, 3, secret))
        .isInstanceOf(IllegalArgumentException.class)
        .hasMessage("N must be <= 255");
  }

  @Test
  public void roundTrip() throws Exception {
    qt().forAll(integers().between(2, 5), integers().between(2, 5), byteArrays())
        .asWithPrecursor((top, k, secret) -> SecretSharing.split(top + k, k, secret))
        .check((top, k, secret, shares) ->
            // Try each subset of the set of shares.
            Sets.powerSet(shares).stream()
                .allMatch(subset -> {
                  // Empty subsets don't count.
                  if (subset.isEmpty()) {
                    return true;
                  }

                  // Combine the shares and try to recover the original secret.
                  final byte[] recovered = SecretSharing.combine(subset);

                  // Every subset of shares with K or more shares will combine to form the original
                  // secret.
                  if (subset.size() >= k) {
                    return Arrays.equals(recovered, secret);
                  }

                  // No subset of shares with fewer than K shares will combine to form the original
                  // secret. We only check this for secrets three bytes or longer to limit our false
                  // positive rate to under 1/2^24.
                  return secret.length <= 3 || !Arrays.equals(recovered, secret);
                }));
  }
}

@@ -1093,21 +1093,26 @@
 va.util.
-Array
+Collection
 s;%0Aimpor
@@ -1119,35 +1119,27 @@
 t java.util.
-Collections
+Set
 ;%0Aimport org
@@ -3158,16 +3158,22 @@
   .check
+Assert
 ((top, k
@@ -3192,19 +3192,19 @@
 ares) -%3E
-%0A  
+ %7B%0A
         
@@ -3212,11 +3212,11 @@
  // 
-Try
+For
  eac
@@ -3244,17 +3244,17 @@
 f shares
-.
+:
 %0A       
@@ -3256,17 +3256,40 @@
         
- 
+for (Set%3CShare%3E subset :
  Sets.po
@@ -3306,17 +3306,11 @@
 res)
-.stream()
+) %7B
 %0A   
@@ -3322,88 +3322,38 @@
     
-    .allMatch(subset -%3E %7B%0A                  // E
+// Don't evaluate e
 mpty 
-sub
 sets
- don't count.%0A      
+.%0A
     
@@ -3402,23 +3402,14 @@
     
-      return tr
+contin
 ue;%0A
@@ -3416,38 +3416,27 @@
             
-      %7D%0A%0A     
+%7D%0A%0A
             
@@ -3435,17 +3435,16 @@
         
- 
 // Combi
@@ -3509,22 +3509,16 @@
         
-      
 final by
@@ -3578,22 +3578,16 @@
         
-      
 // Every
@@ -3664,38 +3664,32 @@
 nal%0A            
-      
 // secret.%0A     
@@ -3691,30 +3691,24 @@
             
-      
 if (subset.s
@@ -3739,46 +3739,40 @@
     
-      return Arrays.equals(recovered, 
+assertThat(recovered).isEqualTo(
 secr
@@ -3784,39 +3784,27 @@
             
-      %7D%0A%0A      
+%7D%0A%0A
             
@@ -3881,30 +3881,24 @@
 he original%0A
-      
             
@@ -3992,22 +3992,16 @@
         
-      
 // posit
@@ -4042,20 +4042,32 @@
     
-      return
+if (subset.size() %3C k &&
  sec
@@ -4081,69 +4081,109 @@
 gth 
-%3C= 3 %7C%7C !Arrays.equals(recovered, secret);%0A                %7D)
+%3E= 3) %7B%0A              assertThat(recovered).isNotEqualTo(secret);%0A            %7D%0A          %7D%0A        %7D
 );%0A 

Refactor away from streams.