blob: ff7e157f65063c36838e39f08f68bd4d7ad380b8 [file] [log] [blame]
//
// Copyright 2020 Google LLC
//
// 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.google.privacy.differentialprivacy;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public final class SecureNoiseMathTest {
@Test
public void ceilPowerOfTwo_nonPositiveInput_throwsException() {
assertThrows(IllegalArgumentException.class, () -> SecureNoiseMath.ceilPowerOfTwo(0.0));
assertThrows(IllegalArgumentException.class, () -> SecureNoiseMath.ceilPowerOfTwo(-1.0));
}
@Test
public void ceilPowerOfTwo_infiniteInput_throwsException() {
assertThrows(
IllegalArgumentException.class,
() -> SecureNoiseMath.ceilPowerOfTwo(Double.POSITIVE_INFINITY));
assertThrows(
IllegalArgumentException.class,
() -> SecureNoiseMath.ceilPowerOfTwo(Double.NEGATIVE_INFINITY));
}
@Test
public void ceilPowerOfTwo_inputIsNaN_throwsException() {
assertThrows(IllegalArgumentException.class, () -> SecureNoiseMath.ceilPowerOfTwo(Double.NaN));
}
@Test
public void ceilPowerOfTwo_inputIsGreaterThan2ToThePowerOf1023_throwsException() {
assertThrows(
IllegalArgumentException.class,
() -> SecureNoiseMath.ceilPowerOfTwo(Math.pow(2.001, 1023)));
assertThrows(
IllegalArgumentException.class, () -> SecureNoiseMath.ceilPowerOfTwo(Double.MAX_VALUE));
}
@Test
public void ceilPowerOfTwo_returnsExactPowerOfTwo() {
// Since x is a finite positive number, x is a power of 2 if and only if it has a mantissa of 0.
final long mantissaMask = 0x000fffffffffffffL;
assertThat(Double.doubleToLongBits(SecureNoiseMath.ceilPowerOfTwo(0.123456789)) & mantissaMask)
.isEqualTo(0x0000000000000000L);
assertThat(Double.doubleToLongBits(SecureNoiseMath.ceilPowerOfTwo(987654321.0)) & mantissaMask)
.isEqualTo(0x0000000000000000L);
}
@Test
public void ceilPowerOfTwo_inputIsAPowerOfTwo_returnsInput() {
for (double exponent = -1022.0; exponent <= 1023.0; exponent++) {
assertThat(SecureNoiseMath.ceilPowerOfTwo(Math.pow(2.0, exponent)))
.isEqualTo(Math.pow(2.0, exponent));
}
}
@Test
public void ceilPowerOfTwo_inputIsNotAPowerOfTwo_hardCodedExamplesReturnCorrectResult() {
assertThat(SecureNoiseMath.ceilPowerOfTwo(0.2078795763)).isEqualTo(0.25);
assertThat(SecureNoiseMath.ceilPowerOfTwo(0.6931471805)).isEqualTo(1.0);
assertThat(SecureNoiseMath.ceilPowerOfTwo(3.1415926535)).isEqualTo(4.0);
assertThat(SecureNoiseMath.ceilPowerOfTwo(36.4621596072)).isEqualTo(64.0);
}
@Test
public void ceilPowerOfTwo_inputIsNotAPowerOfTwo_exhaustiveExponentCoverReturnsCorrectResult() {
for (double exponent = -1022.0; exponent <= -1.0; exponent++) {
assertThat(SecureNoiseMath.ceilPowerOfTwo(Math.pow(2.001, exponent)))
.isEqualTo(Math.pow(2.0, exponent));
}
assertThat(SecureNoiseMath.ceilPowerOfTwo(0.99)).isEqualTo(1.0);
for (double exponent = 1.0; exponent <= 1022.0; exponent++) {
assertThat(SecureNoiseMath.ceilPowerOfTwo(Math.pow(2.001, exponent)))
.isEqualTo(Math.pow(2.0, exponent + 1.0));
}
}
@Test
public void roundToMultipleOfPowerOfTwo_granularityIsNotAPowerOfTwo_throwsException() {
assertThrows(
IllegalArgumentException.class,
() -> SecureNoiseMath.roundToMultipleOfPowerOfTwo(2.0, 0.0));
assertThrows(
IllegalArgumentException.class,
() -> SecureNoiseMath.roundToMultipleOfPowerOfTwo(2.0, 0.3));
assertThrows(
IllegalArgumentException.class,
() -> SecureNoiseMath.roundToMultipleOfPowerOfTwo(2.0, -3.0));
assertThrows(
IllegalArgumentException.class,
() -> SecureNoiseMath.roundToMultipleOfPowerOfTwo(2.0, -1.0));
assertThrows(
IllegalArgumentException.class,
() -> SecureNoiseMath.roundToMultipleOfPowerOfTwo(2.0, Double.NEGATIVE_INFINITY));
assertThrows(
IllegalArgumentException.class,
() -> SecureNoiseMath.roundToMultipleOfPowerOfTwo(2.0, Double.POSITIVE_INFINITY));
}
@Test
public void roundToMultipleOfPowerOfTwo_inputIsAMultipleOfTheGranularity_returnsInput() {
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(0.0, 0.5)).isEqualTo(0.0);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(0.125, 0.125)).isEqualTo(0.125);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(-0.125, 0.125)).isEqualTo(-0.125);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(16512.0, 1.0)).isEqualTo(16512.0);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(-16512.0, 1.0)).isEqualTo(-16512.0);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(3936.0, 32.0)).isEqualTo(3936.0);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(-3936.0, 32.0)).isEqualTo(-3936.0);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(7.9990234375, 0.0009765625))
.isEqualTo(7.9990234375);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(-7.9990234375, 0.0009765625))
.isEqualTo(-7.9990234375);
}
@Test
public void
roundToMultipleOfPowerOfTwo_inputIsNotAMultipleOfTheGranularity_returnsCorrectResult() {
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(0.124, 0.125)).isEqualTo(0.125);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(-0.124, 0.125)).isEqualTo(-0.125);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(0.126, 0.125)).isEqualTo(0.125);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(-0.126, 0.125)).isEqualTo(-0.125);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(16512.499, 1.0)).isEqualTo(16512.0);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(-16512.499, 1.0)).isEqualTo(-16512.0);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(16511.501, 1.0)).isEqualTo(16512.0);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(-16511.501, 1.0)).isEqualTo(-16512.0);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(3920.3257, 32.0)).isEqualTo(3936.0);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(-3920.3257, 32.0)).isEqualTo(-3936.0);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(3951.7654, 32.0)).isEqualTo(3936.0);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(-3951.7654, 32.0)).isEqualTo(-3936.0);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(7.9990232355, 0.0009765625))
.isEqualTo(7.9990234375);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(-7.9990232355, 0.0009765625))
.isEqualTo(-7.9990234375);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(7.9990514315, 0.0009765625))
.isEqualTo(7.9990234375);
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo(-7.9990514315, 0.0009765625))
.isEqualTo(-7.9990234375);
}
@Test
public void roundToMultipleOfPowerOfTwo_scaledInputExceedsLongRange_returnsInput() {
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo((double) (Long.MAX_VALUE - 1), 0.125))
.isEqualTo((double) (Long.MAX_VALUE - 1));
assertThat(SecureNoiseMath.roundToMultipleOfPowerOfTwo((double) (Long.MIN_VALUE + 1), 0.125))
.isEqualTo((double) (Long.MIN_VALUE + 1));
}
@Test
public void roundToMultiple_granularityIsNotPositive_throwsException() {
assertThrows(IllegalArgumentException.class, () -> SecureNoiseMath.roundToMultiple(1, 0));
assertThrows(IllegalArgumentException.class, () -> SecureNoiseMath.roundToMultiple(1, -1));
assertThrows(
IllegalArgumentException.class,
() -> SecureNoiseMath.roundToMultiple(1, Integer.MIN_VALUE));
}
@Test
public void roundToMultiple_granularityIsOne_returnsInput() {
assertThat(SecureNoiseMath.roundToMultiple(0, 1)).isEqualTo(0);
assertThat(SecureNoiseMath.roundToMultiple(1, 1)).isEqualTo(1);
assertThat(SecureNoiseMath.roundToMultiple(-1, 1)).isEqualTo(-1);
assertThat(SecureNoiseMath.roundToMultiple(648391, 1)).isEqualTo(648391);
assertThat(SecureNoiseMath.roundToMultiple(-648391, 1)).isEqualTo(-648391);
}
@Test
public void roundToMultiple_granularityIsEven_returnsCorrectResult() {
assertThat(SecureNoiseMath.roundToMultiple(0, 4)).isEqualTo(0);
assertThat(SecureNoiseMath.roundToMultiple(1, 4)).isEqualTo(0);
assertThat(SecureNoiseMath.roundToMultiple(2, 4)).isEqualTo(4);
assertThat(SecureNoiseMath.roundToMultiple(3, 4)).isEqualTo(4);
assertThat(SecureNoiseMath.roundToMultiple(4, 4)).isEqualTo(4);
assertThat(SecureNoiseMath.roundToMultiple(-1, 4)).isEqualTo(0);
assertThat(SecureNoiseMath.roundToMultiple(-2, 4)).isEqualTo(0);
assertThat(SecureNoiseMath.roundToMultiple(-3, 4)).isEqualTo(-4);
assertThat(SecureNoiseMath.roundToMultiple(-4, 4)).isEqualTo(-4);
assertThat(SecureNoiseMath.roundToMultiple(648389, 4)).isEqualTo(648388);
assertThat(SecureNoiseMath.roundToMultiple(648390, 4)).isEqualTo(648392);
assertThat(SecureNoiseMath.roundToMultiple(648391, 4)).isEqualTo(648392);
assertThat(SecureNoiseMath.roundToMultiple(648392, 4)).isEqualTo(648392);
assertThat(SecureNoiseMath.roundToMultiple(-648389, 4)).isEqualTo(-648388);
assertThat(SecureNoiseMath.roundToMultiple(-648390, 4)).isEqualTo(-648388);
assertThat(SecureNoiseMath.roundToMultiple(-648391, 4)).isEqualTo(-648392);
assertThat(SecureNoiseMath.roundToMultiple(-648392, 4)).isEqualTo(-648392);
}
@Test
public void roundToMultiple_granularityIsOdd_returnsCorrectResult() {
assertThat(SecureNoiseMath.roundToMultiple(0, 3)).isEqualTo(0);
assertThat(SecureNoiseMath.roundToMultiple(1, 3)).isEqualTo(0);
assertThat(SecureNoiseMath.roundToMultiple(2, 3)).isEqualTo(3);
assertThat(SecureNoiseMath.roundToMultiple(3, 3)).isEqualTo(3);
assertThat(SecureNoiseMath.roundToMultiple(-1, 3)).isEqualTo(0);
assertThat(SecureNoiseMath.roundToMultiple(-2, 3)).isEqualTo(-3);
assertThat(SecureNoiseMath.roundToMultiple(-3, 3)).isEqualTo(-3);
assertThat(SecureNoiseMath.roundToMultiple(648391, 3)).isEqualTo(648390);
assertThat(SecureNoiseMath.roundToMultiple(648392, 3)).isEqualTo(648393);
assertThat(SecureNoiseMath.roundToMultiple(648393, 3)).isEqualTo(648393);
assertThat(SecureNoiseMath.roundToMultiple(-648391, 3)).isEqualTo(-648390);
assertThat(SecureNoiseMath.roundToMultiple(-648392, 3)).isEqualTo(-648393);
assertThat(SecureNoiseMath.roundToMultiple(-648393, 3)).isEqualTo(-648393);
}
@Test
public void nextLargerDouble_inputRepresentableAsDouble_returnsDoubleOfSameValue() {
assertThat(SecureNoiseMath.nextLargerDouble(0L)).isEqualTo(0.0);
assertThat(SecureNoiseMath.nextLargerDouble(1L)).isEqualTo(1.0);
assertThat(SecureNoiseMath.nextLargerDouble(-1L)).isEqualTo(-1.0);
// Smallest positive long value for which the next double is a distance of 2 away
assertThat(SecureNoiseMath.nextLargerDouble(9007199254740992L)).isEqualTo(9007199254740992.0);
// Largest negative long value for which the previous double is a distance of 2 away
assertThat(SecureNoiseMath.nextLargerDouble(-9007199254740992L)).isEqualTo(-9007199254740992.0);
assertThat((long) SecureNoiseMath.nextLargerDouble(8646911284551352320L))
.isEqualTo(8646911284551352320L);
assertThat((long) SecureNoiseMath.nextLargerDouble(-8646911284551352320L))
.isEqualTo(-8646911284551352320L);
// Largest long value accurately representable as a double
assertThat((long) SecureNoiseMath.nextLargerDouble(Long.MAX_VALUE - 1023))
.isEqualTo(Long.MAX_VALUE - 1023);
// Smallest long value accurately representable as a double
assertThat((long) SecureNoiseMath.nextLargerDouble(Long.MIN_VALUE)).isEqualTo(Long.MIN_VALUE);
}
@Test
public void nextLargerDouble_inputNotRepresentableAsDouble_returnsNextLargerDouble() {
// Smallest positive long value not representable as a double
assertThat((long) SecureNoiseMath.nextLargerDouble(9007199254740993L))
.isEqualTo(9007199254740994L);
// Largest negative long value not representable as a double
assertThat((long) SecureNoiseMath.nextLargerDouble(-9007199254740993L))
.isEqualTo(-9007199254740992L);
assertThat((long) SecureNoiseMath.nextLargerDouble(8646911284551352321L))
.isEqualTo(8646911284551353344L);
assertThat((long) SecureNoiseMath.nextLargerDouble(8646911284551353343L))
.isEqualTo(8646911284551353344L);
assertThat((long) SecureNoiseMath.nextLargerDouble(-8646911284551352321L))
.isEqualTo(-8646911284551352320L);
assertThat((long) SecureNoiseMath.nextLargerDouble(-8646911284551353343L))
.isEqualTo(-8646911284551352320L);
// Casting Long.MAX_VALUE to double should result in the next larger double, i.e., 2^63
assertThat(SecureNoiseMath.nextLargerDouble(Long.MAX_VALUE)).isEqualTo(Math.pow(2.0, 63.0));
}
@Test
public void nextSmallerDouble_inputRepresentableAsDouble_returnsDoubleOfSameValue() {
assertThat(SecureNoiseMath.nextSmallerDouble(0L)).isEqualTo(0.0);
assertThat(SecureNoiseMath.nextSmallerDouble(1L)).isEqualTo(1.0);
assertThat(SecureNoiseMath.nextSmallerDouble(-1L)).isEqualTo(-1.0);
// Smallest positive long value for which the next double is a distance of 2 away
assertThat(SecureNoiseMath.nextSmallerDouble(9007199254740992L)).isEqualTo(9007199254740992.0);
// Largest negative long value for which the previous double is a distance of 2 away
assertThat(SecureNoiseMath.nextSmallerDouble(-9007199254740992L))
.isEqualTo(-9007199254740992.0);
assertThat((long) SecureNoiseMath.nextSmallerDouble(8646911284551352320L))
.isEqualTo(8646911284551352320L);
assertThat((long) SecureNoiseMath.nextSmallerDouble(-8646911284551352320L))
.isEqualTo(-8646911284551352320L);
// Largest long value accurately representable as a double
assertThat((long) SecureNoiseMath.nextSmallerDouble(Long.MAX_VALUE - 1023))
.isEqualTo(Long.MAX_VALUE - 1023);
// Smallest long value accurately representable as a double
assertThat((long) SecureNoiseMath.nextSmallerDouble(Long.MIN_VALUE)).isEqualTo(Long.MIN_VALUE);
}
@Test
public void nextSmallerDouble_inputNotRepresentableAsDouble_returnsNextSmallerDouble() {
// Smallest positive long value not representable as a double
assertThat((long) SecureNoiseMath.nextSmallerDouble(9007199254740993L))
.isEqualTo(9007199254740992L);
// Largest negative long value not representable as a double
assertThat((long) SecureNoiseMath.nextSmallerDouble(-9007199254740993L))
.isEqualTo(-9007199254740994L);
assertThat((long) SecureNoiseMath.nextSmallerDouble(8646911284551352321L))
.isEqualTo(8646911284551352320L);
assertThat((long) SecureNoiseMath.nextSmallerDouble(8646911284551353343L))
.isEqualTo(8646911284551352320L);
assertThat((long) SecureNoiseMath.nextSmallerDouble(-8646911284551352321L))
.isEqualTo(-8646911284551353344L);
assertThat((long) SecureNoiseMath.nextSmallerDouble(-8646911284551353343L))
.isEqualTo(-8646911284551353344L);
}
}