#include <stdlib.h>
#include "jni.h"
#include "gmp.h"
#include "com_scytl_math_BigIntegers.h"
#include "mpz_jni.h"

/*
 * Class:     com_scytl_math_BigIntegers
 * Method:    modPowNative
 * Signature: ([B[B[B)[B
 *
 * Performs BigInteger.modPow operation.
 *
 * @param env the JNI environment
 * @param class the calling Java class
 * @param base the base, non-negative
 * @param exponent the exponent, non-negative
 * @param modulus the modulus, positive
 * @return the modPow value.
 */
JNIEXPORT jbyteArray JNICALL Java_com_scytl_math_BigIntegers_modPowNative
  (JNIEnv *env, jclass class, jbyteArray base, jbyteArray exponent, jbyteArray modulus)
{
    mpz_t mbase;
    mpz_init_set_jbyteArray(mbase, env, base);
    mpz_t mexponent;
    mpz_init_set_jbyteArray(mexponent, env, exponent);
    mpz_t mmodulus;
    mpz_init_set_jbyteArray(mmodulus, env, modulus);
    mpz_t mvalue;
    mpz_init2(mvalue, mpz_sizeinbase(mmodulus, 2));

    mpz_powm(mvalue, mbase, mexponent, mmodulus);

    jbyteArray value = mpz_get_jbyteArray(env, mvalue);

    mpz_clear(mbase);
    mpz_clear(mexponent);
    mpz_clear(mmodulus);
    mpz_clear(mvalue);

    return value;
}

/*
 * Class:     com_scytl_math_BigIntegers
 * Method:    multiplyNative
 * Signature: ([B[B)[B
 *
 * Multiplies two given numbers.
 *
 * @param env the environment
 * @param class the class
 * @param number1 the first number
 * @param number2 the second number.
 */
JNIEXPORT jbyteArray JNICALL Java_com_scytl_math_BigIntegers_multiplyNative
  (JNIEnv *env, jclass class, jbyteArray number1, jbyteArray number2)
{
    mpz_t mnumber1;
    mpz_init_set_jbyteArray(mnumber1, env, number1);
    mpz_t mnumber2;
    mpz_init_set_jbyteArray(mnumber2, env, number2);
    mpz_t mvalue;
    mpz_init2(mvalue, mpz_sizeinbase(mnumber1, 2) + mpz_sizeinbase(mnumber2, 2));

    mpz_mul(mvalue, mnumber1, mnumber2);

    jbyteArray value = mpz_get_jbyteArray(env, mvalue);

    mpz_clear(mnumber1);
    mpz_clear(mnumber2);
    mpz_clear(mvalue);

    return value;
}

/*
 * Class:     com_scytl_math_BigIntegers
 * Method:    modMultiplyNative
 * Signature: ([B[B[B)[B
 *
 * Multiplies two given numbers modulo the specified modulus.
 *
 * @param env the environment
 * @param class the class
 * @param number1 the first number
 * @param number2 the second number
 * @param modulus the modulus.
 */
JNIEXPORT jbyteArray JNICALL Java_com_scytl_math_BigIntegers_modMultiplyNative
  (JNIEnv *env, jclass class, jbyteArray number1, jbyteArray number2, jbyteArray modulus)
{
    mpz_t mnumber1;
    mpz_init_set_jbyteArray(mnumber1, env, number1);
    mpz_t mnumber2;
    mpz_init_set_jbyteArray(mnumber2, env, number2);
    mpz_t mmodulus;
    mpz_init_set_jbyteArray(mmodulus, env, modulus);
    mpz_t mvalue;
    mpz_init2(mvalue, mpz_sizeinbase(mnumber1, 2) + mpz_sizeinbase(mnumber2, 2));

    mpz_mul(mvalue, mnumber1, mnumber2);
    mpz_mod(mvalue, mvalue, mmodulus);

    jbyteArray value = mpz_get_jbyteArray(env, mvalue);

    mpz_clear(mnumber1);
    mpz_clear(mnumber2);
    mpz_clear(mmodulus);
    mpz_clear(mvalue);

    return value;
}
