text: //
  // Copyright 2019 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.
  //
  
  #include "differential_privacy/algorithms/numerical-mechanisms.h"
  
  #include "gmock/gmock.h"
  #include "gtest/gtest.h"
  #include "differential_privacy/algorithms/distributions.h"
  
  namespace differential_privacy {
  namespace {
  
  using testing::_;
  using testing::DoubleEq;
  using testing::DoubleNear;
  using testing::Ge;
  using testing::Return;
  
  class MockLaplaceDistribution : public internal::LaplaceDistribution {
   public:
    MockLaplaceDistribution() : internal::LaplaceDistribution(1.0, 1.0) {}
    MOCK_METHOD1(Sample, double(double));
  };
  
  template <typename T>
  class NumericalMechanismsTest : public ::testing::Test {};
  
  typedef ::testing::Types<int64_t, double> NumericTypes;
  TYPED_TEST_SUITE(NumericalMechanismsTest, NumericTypes);
  
  TYPED_TEST(NumericalMechanismsTest, LaplaceBuilder) {
    LaplaceMechanism::Builder test_builder;
    std::unique_ptr<LaplaceMechanism> test_mechanism =
        test_builder.SetEpsilon(1).SetSensitivity(3).Build().ValueOrDie();
  
    EXPECT_DOUBLE_EQ(test_mechanism->GetEpsilon(), 1);
    EXPECT_DOUBLE_EQ(test_mechanism->GetSensitivity(), 3);
  }
  
  TYPED_TEST(NumericalMechanismsTest, LaplaceBuilderSensitivityTooHigh) {
    LaplaceMechanism::Builder test_builder;
    base::StatusOr<std::unique_ptr<LaplaceMechanism>> test_mechanism =
        test_builder.SetEpsilon(1)
            .SetSensitivity(std::numeric_limits<double>::max())
            .Build();
    EXPECT_FALSE(test_mechanism.ok());
  }
  
  TEST(NumericalMechanismsTest, LaplaceAddsNoise) {
    auto distro = absl::make_unique<MockLaplaceDistribution>();
    ON_CALL(*distro, Sample(_)).WillByDefault(Return(10.0));
    LaplaceMechanism mechanism(1.0, 1.0, std::move(distro));
  
    EXPECT_THAT(mechanism.AddNoise(0.0), DoubleNear(10.0, 5.0));
  }
  
  TEST(NumericalMechanismsTest, LaplaceAddsNoNoiseWhenSensitivityIsZero) {
    LaplaceMechanism mechanism(1.0, 0.0);
  
    EXPECT_THAT(mechanism.AddNoise(12.3), DoubleEq(12.3));
  }
  
  TEST(NumericalMechanismsTest, LaplaceDiversityCorrect) {
    LaplaceMechanism mechanism(1.0, 1.0);
    EXPECT_EQ(mechanism.GetDiversity(), 1.0);
  
    LaplaceMechanism mechanism2(2.0, 1.0);
    EXPECT_EQ(mechanism2.GetDiversity(), 0.5);
  
    LaplaceMechanism mechanism3(2.0, 3.0);
    EXPECT_EQ(mechanism3.GetDiversity(), 1.5);
  }
  
  TEST(NumericalMechanismsTest, LaplaceBudgetCorrect) {
    auto distro = absl::make_unique<MockLaplaceDistribution>();
    EXPECT_CALL(*distro, Sample(1.0)).Times(1);
    EXPECT_CALL(*distro, Sample(2.0)).Times(1);
    EXPECT_CALL(*distro, Sample(4.0)).Times(1);
    LaplaceMechanism mechanism(1.0, 1.0, std::move(distro));
  
    mechanism.AddNoise(0.0, 1.0);
    mechanism.AddNoise(0.0, 0.5);
    mechanism.AddNoise(0.0, 0.25);
  }
  
  TEST(NumericalMechanismsTest, LaplaceSnaps) {
    auto distro = absl::make_unique<MockLaplaceDistribution>();
    EXPECT_CALL(*distro, Sample(_))
        .WillOnce(Return(10.0))
        .WillOnce(Return(10.001));
    LaplaceMechanism mechanism(1.0, 1.0, std::move(distro));
  
    EXPECT_THAT(mechanism.AddNoise(0.0),
                DoubleNear(mechanism.AddNoise(0.0), 0.0001));
  }
  
  TEST(NumericalMechanismsTest, LaplaceWorksForIntegers) {
    auto distro = absl::make_unique<MockLaplaceDistribution>();
    ON_CALL(*distro, Sample(_)).WillByDefault(Return(10.0));
    LaplaceMechanism mechanism(1.0, 1.0, std::move(distro));
  
    EXPECT_EQ(static_cast<int64_t>(mechanism.AddNoise(0)), 10);
  }
  
  TEST(NumericalMechanismsTest, LaplaceConfidenceInterval) {
    double epsilon = 0.5;
    double sensitivity = 1.0;
    double level = .95;
    double budget = .5;
    LaplaceMechanism mechanism(epsilon, sensitivity);
    ConfidenceInterval confidence_interval =
        mechanism.NoiseConfidenceInterval(level, budget);
    EXPECT_EQ(confidence_interval.lower_bound(),
              log(1 - level) / epsilon / budget);
    EXPECT_EQ(confidence_interval.upper_bound(),
              -log(1 - level) / epsilon / budget);
    EXPECT_EQ(confidence_interval.confidence_level(), level);
  }
  
  TYPED_TEST(NumericalMechanismsTest, LaplaceBuilderClone) {
    LaplaceMechanism::Builder test_builder;
    std::unique_ptr<LaplaceMechanism::Builder> clone =
        test_builder.SetEpsilon(1).SetSensitivity(3).Clone();
    std::unique_ptr<LaplaceMechanism> test_mechanism =
        clone->Build().ValueOrDie();
  
    EXPECT_DOUBLE_EQ(test_mechanism->GetEpsilon(), 1);
    EXPECT_DOUBLE_EQ(test_mechanism->GetSensitivity(), 3);
  }
  
  TEST(NumericalMechanismsTest, LaplaceEstimatesL1WithL0AndLInf) {
    LaplaceMechanism::Builder builder;
    std::unique_ptr<LaplaceMechanism> mechanism = builder.SetEpsilon(1)
                                                      .SetL0Sensitivity(5)
                                                      .SetLInfSensitivity(3)
                                                      .Build()
                                                      .ValueOrDie();
    EXPECT_THAT(mechanism->GetSensitivity(), Ge(3));
  }
  }  // namespace
  }  // namespace differential_privacy
  