/*
* (C) 2016 Jack Lloyd
*
* Botan is released under the Simplified BSD License (see license.txt)
*/

#include "tests.h"

#if defined(BOTAN_HAS_BIGINT_MP)
   #include <botan/internal/mp_core.h>
#endif

namespace Botan_Tests {

namespace {

#if defined(BOTAN_HAS_BIGINT_MP)

class MP_Unit_Tests final : public Test {
   public:
      std::vector<Test::Result> run() override {
         std::vector<Test::Result> results;

         results.push_back(test_cnd_swap());
         results.push_back(test_cnd_add());
         results.push_back(test_cnd_sub());
         results.push_back(test_cnd_abs());

         return results;
      }

   private:
      static Result test_cnd_add() {
         Result result("bigint_cnd_add");

         const Botan::word max = ~static_cast<Botan::word>(0);

         Botan::word a = 2;
         Botan::word c = Botan::bigint_cnd_add<Botan::word>(0, &a, &max, 1);

         result.test_u64_eq("No op", a, 2);
         result.test_u64_eq("No op", c, 0);

         c = Botan::bigint_cnd_add<Botan::word>(1, &a, &max, 1);

         result.test_u64_eq("Add", a, 1);
         result.test_u64_eq("Carry", c, 1);

         // TODO more tests

         return result;
      }

      static Result test_cnd_sub() {
         Result result("bigint_cnd_sub");

         Botan::word a = 2;
         const Botan::word b = 3;
         Botan::word c = Botan::bigint_cnd_sub<Botan::word>(0, &a, &b, 1);

         result.test_u64_eq("No op", a, 2);
         result.test_u64_eq("No op", c, 0);

         c = Botan::bigint_cnd_sub<Botan::word>(1, &a, &b, 1);

         result.test_u64_eq("Sub", a, ~static_cast<Botan::word>(0));
         result.test_u64_eq("Borrow", c, 1);

         return result;
      }

      static Result test_cnd_abs() {
         Result result("bigint_cnd_abs");

         const Botan::word max = Botan::WordInfo<Botan::word>::max;

         Botan::word x1 = max;
         Botan::bigint_cnd_abs<Botan::word>(1, &x1, 1);
         result.test_u64_eq("Abs", x1, 1);

         x1 = 0;
         Botan::bigint_cnd_abs<Botan::word>(1, &x1, 1);
         result.test_u64_eq("Abs", x1, 0);

         x1 = 1;
         Botan::bigint_cnd_abs<Botan::word>(1, &x1, 1);
         result.test_u64_eq("Abs", x1, max);

         x1 = 1;
         Botan::bigint_cnd_abs<Botan::word>(0, &x1, 1);
         result.test_u64_eq("No change", x1, 1);

         Botan::word x2[2] = {max, max};

         Botan::bigint_cnd_abs<Botan::word>(1, x2, 2);
         result.test_u64_eq("Abs", x2[0], 1);
         result.test_u64_eq("Abs", x2[1], 0);

         return result;
      }

      static Result test_cnd_swap() {
         Result result("bigint_cnd_swap");

         // null with zero length is ok
         Botan::bigint_cnd_swap<Botan::word>(0, nullptr, nullptr, 0);
         Botan::bigint_cnd_swap<Botan::word>(1, nullptr, nullptr, 0);

         Botan::word x1 = 5;
         Botan::word y1 = 9;

         Botan::bigint_cnd_swap<Botan::word>(0, &x1, &y1, 1);
         result.test_u64_eq("No swap", x1, 5);
         Botan::bigint_cnd_swap<Botan::word>(1, &x1, &y1, 1);
         result.test_u64_eq("Swap", x1, 9);

         Botan::word x5[5] = {0, 1, 2, 3, 4};
         Botan::word y5[5] = {3, 2, 1, 0, 9};

         // Should only modify first four
         Botan::bigint_cnd_swap<Botan::word>(1, x5, y5, 4);

         for(size_t i = 0; i != 4; ++i) {
            result.test_u64_eq("Swap x5", x5[i], static_cast<Botan::word>(3 - i));
         }
         result.test_u64_eq("Not touched", x5[4], 4);

         for(size_t i = 0; i != 4; ++i) {
            result.test_u64_eq("Swap y5", y5[i], static_cast<Botan::word>(i));
         }
         result.test_u64_eq("Not touched", y5[4], 9);

         return result;
      }
};

BOTAN_REGISTER_TEST("math", "mp_unit", MP_Unit_Tests);

#endif

}  // namespace

}  // namespace Botan_Tests
