//*****************************************************************************
// Copyright 2017-2018 Intel Corporation
//
// 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 <bitset>
#include <cmath>
#include <limits>
#include <sstream>

#include "gtest/gtest.h"

#include "ngraph/ngraph.hpp"
#include "util/all_close_f.hpp"

using namespace std;
using namespace ngraph;

union FloatUnion {
    float f;
    uint32_t i;
};

string float_to_bits(float f)
{
    FloatUnion fu{f};
    stringstream ss;
    ss << bitset<32>(fu.i);
    return ss.str();
}

float bits_to_float(const string& s)
{
    if (s.size() != 32)
    {
        throw ngraph_error("Input length must be 32");
    }
    bitset<32> bs(s);
    FloatUnion fu;
    fu.i = static_cast<uint32_t>(bs.to_ulong());
    return fu.f;
}

// Test the exact bounds near +0.f
//
// With mantissa_bits = 8, tolerance_bits = 2
//
//                           Targeted bit
//                           |
//                           v
// s e e e e e e e e m m m m m m m m m m m m m m m m m m m m m m m
//               =>|      8      |
//                           | 2 |<=
//
// [Upper bound]
//                           Add 1 at this bit
//                           |
//                           v
// 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// +                         1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// ---------------------------------------------------------------
// 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
//
// [Lower bound]
//                           Minus 1 at this bit
//                           |
//                           v
// 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// -                         1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// ---------------------------------------------------------------
// 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
//
// Convert to 2's compliment
// 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
//
// Mask the sign bit
// 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
TEST(all_close_f, mantissa_8_near_0)
{
    // 0.f, the ground-truth value
    float expected = bits_to_float("00000000000000000000000000000000");
    float computed;

    // ~3.67342E-40, the exact upper bound
    computed = bits_to_float("00000000000001000000000000000000");
    EXPECT_TRUE(test::close_f(expected, computed, 8, 2));

    // ~3.67343E-40, the next representable number bigger than upper bound
    computed = bits_to_float("00000000000001000000000000000001");
    EXPECT_FALSE(test::close_f(expected, computed, 8, 2));

    // ~-3.67342E-40, the exact lower bound
    computed = bits_to_float("10000000000001000000000000000000");
    EXPECT_TRUE(test::close_f(expected, computed, 8, 2));

    // ~-3.67343E-40, the next representable number smaller than lower bound
    computed = bits_to_float("10000000000001000000000000000001");
    EXPECT_FALSE(test::close_f(expected, computed, 8, 2));
}

// Test the exact bounds near -0.f
//
// With mantissa_bits = 8, tolerance_bits = 2
//
//                           Targeted bit
//                           |
//                           v
// s e e e e e e e e m m m m m m m m m m m m m m m m m m m m m m m
//               =>|      8      |
//                           | 2 |<=
//
// [Upper bound]
//                           Minus 1 at this bit
//                           |
//                           v
// 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// -                         1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// ---------------------------------------------------------------
// 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
//
// Convert to 2's compliment
// 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
//
// Mask off sign bit
// 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
//
// [Lower bound]
//                           Add 1 at this bit
//                           |
//                           v
// 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// +                         1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// ---------------------------------------------------------------
// 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
TEST(all_close_f, mantissa_8_near_n0)
{
    // 0.f, the ground-truth value
    float expected = bits_to_float("10000000000000000000000000000000");
    float computed;

    // ~3.67342E-40, the exact upper bound
    computed = bits_to_float("00000000000001000000000000000000");
    EXPECT_TRUE(test::close_f(expected, computed, 8, 2));

    // ~3.67343E-40, the next representable number bigger than upper bound
    computed = bits_to_float("00000000000001000000000000000001");
    EXPECT_FALSE(test::close_f(expected, computed, 8, 2));

    // ~-3.67342E-40, the exact lower bound
    computed = bits_to_float("10000000000001000000000000000000");
    EXPECT_TRUE(test::close_f(expected, computed, 8, 2));

    // ~-3.67343E-40, the next representable number smaller than lower bound
    computed = bits_to_float("10000000000001000000000000000001");
    EXPECT_FALSE(test::close_f(expected, computed, 8, 2));
}

// Test the exact bounds near 1.f
//
// With mantissa_bits = 8, tolerance_bits = 2
//
//                           Targeted bit
//                           |
//                           v
// s e e e e e e e e m m m m m m m m m m m m m m m m m m m m m m m
//               =>|      8      |
//                           | 2 |<=
//
// [Upper bound]
//                           Add 1 at this bit to get upper bound
//                           |
//                           v
// 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// +                         1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// ---------------------------------------------------------------
// 0 0 1 1 1 1 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
//
// [Lower bound]
//                           Minus 1 at this bit to get lower bound
//                           |
//                           v
// 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// -                         1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// ---------------------------------------------------------------
// 0 0 1 1 1 1 1 1 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
TEST(all_close_f, mantissa_8_near_1)
{
    // 1.f, the ground-truth value
    float expected = bits_to_float("00111111100000000000000000000000");
    float computed;

    // 1.03125f, the exact upper bound
    computed = bits_to_float("00111111100001000000000000000000");
    EXPECT_TRUE(test::close_f(expected, computed, 8, 2));

    // 1.031250119f, the next representable number bigger than upper bound
    computed = bits_to_float("00111111100001000000000000000001");
    EXPECT_FALSE(test::close_f(expected, computed, 8, 2));

    // 0.984375f, the exact lower bound
    computed = bits_to_float("00111111011111000000000000000000");
    EXPECT_TRUE(test::close_f(expected, computed, 8, 2));

    // 0.9843749404f, the next representable number smaller than lower bound
    computed = bits_to_float("00111111011110111111111111111111");
    EXPECT_FALSE(test::close_f(expected, computed, 8, 2));
}

// Test the exact bounds near -1.f
//
// With mantissa_bits = 8, tolerance_bits = 2
//
//                           Targeted bit
//                           |
//                           v
// s e e e e e e e e m m m m m m m m m m m m m m m m m m m m m m m
//               =>|      8      |
//                           | 2 |<=
//
// [Upper bound]
//                           Minus 1 at this bit
//                           |
//                           v
// 1 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// -                         1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// ---------------------------------------------------------------
// 1 0 1 1 1 1 1 1 0 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
//
// [Lower bound]
//                           Add 1 at this bit
//                           |
//                           v
// 1 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// +                         1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
// ---------------------------------------------------------------
// 1 0 1 1 1 1 1 1 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
TEST(all_close_f, mantissa_8_near_n1)
{
    // -1.f, the ground-truth value
    float expected = bits_to_float("10111111100000000000000000000000");
    float computed;

    // -0.984375f, the exact upper bound
    computed = bits_to_float("10111111011111000000000000000000");
    EXPECT_TRUE(test::close_f(expected, computed, 8, 2));

    // -0.984374940395355224609375f, the next representable number bigger than upper bound
    computed = bits_to_float("10111111011110111111111111111111");
    EXPECT_FALSE(test::close_f(expected, computed, 8, 2));

    // -1.03125f, the exact lower bound
    computed = bits_to_float("10111111100001000000000000000000");
    EXPECT_TRUE(test::close_f(expected, computed, 8, 2));

    // -1.03125011920928955078125f, the next representable number smaller than lower bound
    computed = bits_to_float("10111111100001000000000000000001");
    EXPECT_FALSE(test::close_f(expected, computed, 8, 2));
}

// For intuitive understanding of tightness of bounds in decimal
// Test bounds near 0, 1, 10, 100, 1000 with mantissa_bits = 8, tolerance_bits = 2
//
//                           Targeted bit
//                           |
//                           v
// s e e e e e e e e m m m m m m m m m m m m m m m m m m m m m m m
//               =>|      8      |
//                           | 2 |<=
TEST(all_close_f, mantissa_8_near_0_1_10_100_1000)
{
    float expected;
    float upper_bound;
    float bigger_than_upper_bound;
    float lower_bound;
    float smaller_than_lower_bound;

    // Bounds around 0: 0 +- 3.67e-40
    expected = 0.f;                          // 00000000000000000000000000000000
    upper_bound = 3.67342e-40f;              // 00000000000001000000000000000000, approximated
    bigger_than_upper_bound = 3.67343e-40f;  // 00000000000001000000000000000001, approximated
    lower_bound = -3.67342e-40f;             // 10000000000001000000000000000000, approximated
    smaller_than_lower_bound = 3.67343e-40f; // 10000000000001000000000000000001, approximated
    EXPECT_TRUE(test::close_f(expected, upper_bound, 8, 2));
    EXPECT_FALSE(test::close_f(expected, bigger_than_upper_bound, 8, 2));
    EXPECT_TRUE(test::close_f(expected, lower_bound, 8, 2));
    EXPECT_FALSE(test::close_f(expected, smaller_than_lower_bound, 8, 2));

    // Bounds around 1: 1 +- 0.03
    expected = 1.f;                           // 00111111100000000000000000000000
    upper_bound = 1.03125f;                   // 00111111100001000000000000000000
    bigger_than_upper_bound = 1.031250119f;   // 00111111100001000000000000000001
    lower_bound = 0.984375f;                  // 00111111011111000000000000000000
    smaller_than_lower_bound = 0.9843749404f; // 00111111011110111111111111111111
    EXPECT_TRUE(test::close_f(expected, upper_bound, 8, 2));
    EXPECT_FALSE(test::close_f(expected, bigger_than_upper_bound, 8, 2));
    EXPECT_TRUE(test::close_f(expected, lower_bound, 8, 2));
    EXPECT_FALSE(test::close_f(expected, smaller_than_lower_bound, 8, 2));

    // Bounds around 10: 10 +- 0.25
    expected = 10.f;                                    // 01000001001000000000000000000000
    upper_bound = 10.25f;                               // 01000001001001000000000000000000
    bigger_than_upper_bound = 10.25000095367431640625f; // 01000001001001000000000000000001
    lower_bound = 9.75f;                                // 01000001000111000000000000000000
    smaller_than_lower_bound = 9.74999904632568359375f; // 01000001000110111111111111111111
    EXPECT_TRUE(test::close_f(expected, upper_bound, 8, 2));
    EXPECT_FALSE(test::close_f(expected, bigger_than_upper_bound, 8, 2));
    EXPECT_TRUE(test::close_f(expected, lower_bound, 8, 2));
    EXPECT_FALSE(test::close_f(expected, smaller_than_lower_bound, 8, 2));

    // Bounds around 100: 100 +- 2
    expected = 100.f;                                 // 01000010110010000000000000000000
    upper_bound = 102.f;                              // 01000010110011000000000000000000
    bigger_than_upper_bound = 102.00000762939453125f; // 01000010110011000000000000000001
    lower_bound = 98.0f;                              // 01000010110001000000000000000000
    smaller_than_lower_bound = 97.99999237060546875f; // 01000010110000111111111111111111
    EXPECT_TRUE(test::close_f(expected, upper_bound, 8, 2));
    EXPECT_FALSE(test::close_f(expected, bigger_than_upper_bound, 8, 2));
    EXPECT_TRUE(test::close_f(expected, lower_bound, 8, 2));
    EXPECT_FALSE(test::close_f(expected, smaller_than_lower_bound, 8, 2));

    // Bounds around 1000: 1000 +- 16
    expected = 1000.f;                              // 01000100011110100000000000000000
    upper_bound = 1016.f;                           // 01000100011111100000000000000000
    bigger_than_upper_bound = 1016.00006103515625f; // 01000100011111100000000000000001
    lower_bound = 984.0f;                           // 01000100011101100000000000000000
    smaller_than_lower_bound = 983.99993896484375f; // 01000100011101011111111111111111
    EXPECT_TRUE(test::close_f(expected, upper_bound, 8, 2));
    EXPECT_FALSE(test::close_f(expected, bigger_than_upper_bound, 8, 2));
    EXPECT_TRUE(test::close_f(expected, lower_bound, 8, 2));
    EXPECT_FALSE(test::close_f(expected, smaller_than_lower_bound, 8, 2));
}

// For intuitive understanding of tightness of bounds in decimal
// Test bounds near 0, 1, 10, 100, 1000 with mantissa_bits = 24, tolerance_bits = 2
//
//                                                           Targeted bit
//                                                           |
//                                                           v
// s e e e e e e e e m m m m m m m m m m m m m m m m m m m m m m m
//               =>|                     24                      |
//                                                           | 2 |<=
TEST(all_close_f, mantissa_24_near_0_1_10_100_1000)
{
    float expected;
    float upper_bound;
    float bigger_than_upper_bound;
    float lower_bound;
    float smaller_than_lower_bound;

    // Bounds around 0: 0 +- 5.6e-45
    expected = 0.f;
    upper_bound = bits_to_float("00000000000000000000000000000100");
    bigger_than_upper_bound = bits_to_float("00000000000000000000000000000101");
    lower_bound = bits_to_float("10000000000000000000000000000100");
    smaller_than_lower_bound = bits_to_float("10000000000000000000000000000101");
    EXPECT_TRUE(test::close_f(expected, upper_bound, 24, 2));
    EXPECT_FALSE(test::close_f(expected, bigger_than_upper_bound, 24, 2));
    EXPECT_TRUE(test::close_f(expected, lower_bound, 24, 2));
    EXPECT_FALSE(test::close_f(expected, smaller_than_lower_bound, 24, 2));

    // Bounds around 1: 1 +- 4.77e-7
    expected = 1.f;
    upper_bound = bits_to_float("00111111100000000000000000000100");
    bigger_than_upper_bound = bits_to_float("00111111100000000000000000000101");
    lower_bound = bits_to_float("00111111011111111111111111111100");
    smaller_than_lower_bound = bits_to_float("00111111011111111111111111111011");
    EXPECT_TRUE(test::close_f(expected, upper_bound, 24, 2));
    EXPECT_FALSE(test::close_f(expected, bigger_than_upper_bound, 24, 2));
    EXPECT_TRUE(test::close_f(expected, lower_bound, 24, 2));
    EXPECT_FALSE(test::close_f(expected, smaller_than_lower_bound, 24, 2));

    // Bounds around 10: 10 +- 3.81e-6
    expected = 10.f;
    upper_bound = bits_to_float("01000001001000000000000000000100");
    bigger_than_upper_bound = bits_to_float("01000001001000000000000000000101");
    lower_bound = bits_to_float("01000001000111111111111111111100");
    smaller_than_lower_bound = bits_to_float("01000001000111111111111111111011");
    EXPECT_TRUE(test::close_f(expected, upper_bound, 24, 2));
    EXPECT_FALSE(test::close_f(expected, bigger_than_upper_bound, 24, 2));
    EXPECT_TRUE(test::close_f(expected, lower_bound, 24, 2));
    EXPECT_FALSE(test::close_f(expected, smaller_than_lower_bound, 24, 2));

    // Bounds around 100: 100 +- 3.05e-5
    expected = 100.f;
    upper_bound = bits_to_float("01000010110010000000000000000100");
    bigger_than_upper_bound = bits_to_float("01000010110010000000000000000101");
    lower_bound = bits_to_float("01000010110001111111111111111100");
    smaller_than_lower_bound = bits_to_float("01000010110001111111111111111011");
    EXPECT_TRUE(test::close_f(expected, upper_bound, 24, 2));
    EXPECT_FALSE(test::close_f(expected, bigger_than_upper_bound, 24, 2));
    EXPECT_TRUE(test::close_f(expected, lower_bound, 24, 2));
    EXPECT_FALSE(test::close_f(expected, smaller_than_lower_bound, 24, 2));

    // Bounds around 1000: 1000 +- 2.44e-4
    expected = 1000.f;
    upper_bound = bits_to_float("01000100011110100000000000000100");
    bigger_than_upper_bound = bits_to_float("01000100011110100000000000000101");
    lower_bound = bits_to_float("01000100011110011111111111111100");
    smaller_than_lower_bound = bits_to_float("01000100011110011111111111111011");
    EXPECT_TRUE(test::close_f(expected, upper_bound, 24, 2));
    EXPECT_FALSE(test::close_f(expected, bigger_than_upper_bound, 24, 2));
    EXPECT_TRUE(test::close_f(expected, lower_bound, 24, 2));
    EXPECT_FALSE(test::close_f(expected, smaller_than_lower_bound, 24, 2));
}

TEST(all_close_f, inf_nan)
{
    float zero = 0.f;
    float infinity = numeric_limits<float>::infinity();
    float neg_infinity = -numeric_limits<float>::infinity();
    float quiet_nan = numeric_limits<float>::quiet_NaN();
    float signaling_nan = numeric_limits<float>::signaling_NaN();

    EXPECT_FALSE(test::close_f(zero, infinity));
    EXPECT_FALSE(test::close_f(zero, neg_infinity));
    EXPECT_FALSE(test::close_f(zero, quiet_nan));
    EXPECT_FALSE(test::close_f(zero, signaling_nan));

    EXPECT_FALSE(test::close_f(infinity, infinity));
    EXPECT_FALSE(test::close_f(neg_infinity, neg_infinity));
    EXPECT_FALSE(test::close_f(quiet_nan, quiet_nan));
    EXPECT_FALSE(test::close_f(signaling_nan, signaling_nan));
}
