/*
 * Copyright (c) 2012-2020 MIRACL UK Ltd.
 *
 * This file is part of MIRACL Core
 * (see https://github.com/miracl/core).
 *
 * 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.
 */

#ifndef FP_HIFIVE_H
#define FP_HIFIVE_H

#include "big_B336_60.h"
#include "config_field_HIFIVE.h"

using namespace core;

#define MODBITS_HIFIVE MBITS_HIFIVE
#define TBITS_HIFIVE (MBITS_HIFIVE%BASEBITS_B336_60)              /**< Number of active bits in top word */
#define TMASK_HIFIVE (((chunk)1<<TBITS_HIFIVE)-1)             /**< Mask for active bits in top word */
#define FEXCESS_HIFIVE (((sign32)1<<MAXXES_HIFIVE)-1)		    /**< 2^(BASEBITS*NLEN-MODBITS) - normalised BIG can be multiplied by less than this before reduction */
#define OMASK_HIFIVE (-((chunk)(1)<<TBITS_HIFIVE))            /**<  for masking out overflow bits */

namespace HIFIVE {

/**
	@brief FP Structure - quadratic extension field
*/

typedef struct
{
    B336_60::BIG g;	/**< Big representation of field element */
    sign32 XES;	/**< Excess */
} FP;

/* Field Params - see rom.c */
extern const B336_60::BIG Modulus;	/**< Actual Modulus set in rom_field*.c */
extern const B336_60::BIG ROI;	    /**< Root of Unity  set in rom_field*.c */
extern const B336_60::BIG R2modp;	/**< Montgomery constant */
extern const chunk MConst;		/**< Constant associated with Modulus - for Montgomery = 1/p mod 2^BASEBITS */
extern const B336_60::BIG SQRTm3;      /**< Square root of -3 */
extern const B336_60::BIG CRu;    /**< Cube Root of Unity */
//extern const int BTset;			/**< Set Bit in Generalised Mersenne */
extern const B336_60::BIG Fra; /**< real part of Pairing-friendly curve Frobenius Constant */
extern const B336_60::BIG Frb; /**< imaginary part of Pairing-friendly curve Frobenius Constant */
extern const B336_60::BIG TWK; /**< Tweak for square roots, pre-calculated from field norm */
//#define FUSED_MODMUL
//#define DEBUG_REDUCE

/* FP prototypes */

/**	@brief Create FP from integer
 *
	@param x FP to be initialised
	@param a integer
 */
extern void FP_from_int(FP *x,int a);


/**	@brief Tests for FP equal to zero mod Modulus
 *
	@param x FP number to be tested
	@return 1 if zero, else returns 0
 */
extern int FP_iszilch(FP *x);

/**	@brief Tests for lexically largest 
 *
	@param x FP number to be tested if larger than -x
	@return 1 if larger, else returns 0
 */
extern int FP_islarger(FP *x);

/**	@brief Serialize out FP  
 *
    @param b buffer for output
	@param x FP number to be serialized
 */
extern void FP_toBytes(char *b,FP *x);

/**	@brief Serialize in FP  
 *
	@param x FP number to be serialized
    @param b buffer for input
 */
extern void FP_fromBytes(FP *x,char *b);

/**	@brief Tests for FP equal to one mod Modulus
 *
	@param x FP number to be tested
	@return 1 if one, else returns 0
 */
extern int FP_isunity(FP *x);

/**	@brief Set FP to zero
 *
	@param x FP number to be set to 0
 */
extern void FP_zero(FP *x);

/**	@brief Copy an FP
 *
	@param y FP number to be copied to
	@param x FP to be copied from
 */
extern void FP_copy(FP *y, FP *x);

/**	@brief Copy from ROM to an FP
 *
	@param y FP number to be copied to
	@param x BIG to be copied from ROM
 */
extern void FP_rcopy(FP *y, const B336_60::BIG x);


/**	@brief Compares two FPs
 *
	@param x FP number
	@param y FP number
	@return 1 if equal, else returns 0
 */
extern int FP_equals(FP *x, FP *y);


/**	@brief Conditional constant time swap of two FP numbers
 *
	Conditionally swaps parameters in constant time (without branching)
	@param x an FP number
	@param y another FP number
	@param s swap takes place if not equal to 0
 */
extern void FP_cswap(FP *x, FP *y, int s);
/**	@brief Conditional copy of FP number
 *
	Conditionally copies second parameter to the first (without branching)
	@param x an FP number
	@param y another FP number
	@param s copy takes place if not equal to 0
 */
extern void FP_cmove(FP *x, FP *y, int s);
/**	@brief Converts from BIG integer to residue form mod Modulus
 *
	@param x BIG number to be converted
	@param y FP result
 */
extern void FP_nres(FP *y, B336_60::BIG x);
/**	@brief Converts from residue form back to BIG integer form
 *
	@param y FP number to be converted to BIG
	@param x BIG result
 */
extern void FP_redc(B336_60::BIG x, FP *y);
/**	@brief Sets FP to representation of unity in residue form
 *
	@param x FP number to be set equal to unity.
 */
extern void FP_one(FP *x);

/**	@brief returns "sign" of an FP
 *
	@param x FP number
    @return 0 for positive, 1 for negative
 */
extern int FP_sign(FP *x);


/**	@brief Reduces DBIG to BIG exploiting special form of the modulus
 *
	This function comes in different flavours depending on the form of Modulus that is currently in use.
	@param r BIG number, on exit = d mod Modulus
	@param d DBIG number to be reduced
 */
extern void FP_mod(B336_60::BIG r, B336_60::DBIG d);

#ifdef FUSED_MODMUL
extern void FP_modmul(B336_60::BIG, B336_60::BIG, B336_60::BIG);
#endif

/**	@brief Fast Modular multiplication of two FPs, mod Modulus
 *
	Uses appropriate fast modular reduction method
	@param x FP number, on exit the modular product = y*z mod Modulus
	@param y FP number, the multiplicand
	@param z FP number, the multiplier
 */
extern void FP_mul(FP *x, FP *y, FP *z);
/**	@brief Fast Modular multiplication of an FP, by a small integer, mod Modulus
 *
	@param x FP number, on exit the modular product = y*i mod Modulus
	@param y FP number, the multiplicand
	@param i a small number, the multiplier
 */
extern void FP_imul(FP *x, FP *y, int i);
/**	@brief Fast Modular squaring of an FP, mod Modulus
 *
	Uses appropriate fast modular reduction method
	@param x FP number, on exit the modular product = y^2 mod Modulus
	@param y FP number, the number to be squared

 */
extern void FP_sqr(FP *x, FP *y);
/**	@brief Modular addition of two FPs, mod Modulus
 *
	@param x FP number, on exit the modular sum = y+z mod Modulus
	@param y FP number
	@param z FP number
 */
extern void FP_add(FP *x, FP *y, FP *z);
/**	@brief Modular subtraction of two FPs, mod Modulus
 *
	@param x FP number, on exit the modular difference = y-z mod Modulus
	@param y FP number
	@param z FP number
 */
extern void FP_sub(FP *x, FP *y, FP *z);
/**	@brief Modular division by 2 of an FP, mod Modulus
 *
	@param x FP number, on exit =y/2 mod Modulus
	@param y FP number
 */
extern void FP_div2(FP *x, FP *y);
/**	@brief Fast Modular exponentiation of an FP, to the power of a BIG, mod Modulus
 *
	@param x FP number, on exit  = y^z mod Modulus
	@param y FP number
	@param z BIG number exponent
 */
extern void FP_pow(FP *x, FP *y, B336_60::BIG z);

/**	@brief Inverse square root precalculation
 *
	@param r FP number, on exit  = x^(p-2*e-1)/2^(e+1) mod Modulus
	@param x FP number
 */
extern void FP_progen(FP *r,FP *x);

/**	@brief Fast Modular square root of a an FP, mod Modulus
 *
	@param x FP number, on exit  = sqrt(y) mod Modulus
	@param y FP number, the number whose square root is calculated
    @param h an optional input precalculation
 */
extern void FP_sqrt(FP *x, FP *y, FP *h);

/**	@brief Modular negation of a an FP, mod Modulus
 *
	@param x FP number, on exit = -y mod Modulus
	@param y FP number

 */
extern void FP_neg(FP *x, FP *y);
/**	@brief Outputs an FP number to the console
 *
	Converts from residue form before output
	@param x an FP number
 */
extern void FP_output(FP *x);
/**	@brief Outputs an FP number to the console, in raw form
 *
	@param x a BIG number
 */
extern void FP_rawoutput(FP *x);
/**	@brief Reduces possibly unreduced FP mod Modulus
 *
	@param x FP number, on exit reduced mod Modulus
 */
extern void FP_reduce(FP *x);
/**	@brief normalizes FP
 *
	@param x FP number, on exit normalized
 */
extern void FP_norm(FP *x);
/**	@brief Tests for FP a quadratic residue mod Modulus
 *
	@param x FP number to be tested
    @param h an optional output precalculation
	@return 1 if quadratic residue, else returns 0 if quadratic non-residue
 */
extern int FP_qr(FP *x,FP *h);


/**	@brief Simultaneous Inverse and Square root
 *
	@param i FP number, on exit = 1/x mod Modulus
	@param s FP number, on exit = sqrt(x) mod Modulus
	@param x FP number
	@return 1 if quadratic residue, else returns 0 if quadratic non-residue
 */
extern int FP_invsqrt(FP *i,FP *s,FP *x);

/**	@brief Simultaneous Inverse and Square root of different numbers
 *
	@param i FP number, on exit = 1/i mod Modulus
	@param s FP number, on exit = sqrt(s) mod Modulus
	@return 1 if quadratic residue, else returns 0 if quadratic non-residue
 */
extern int FP_tpo(FP* i, FP* s);


/**	@brief Modular inverse of a an FP, mod Modulus
 *
	@param x FP number, on exit = 1/y mod Modulus
	@param y FP number
    @param h an optional input precalculation
 */
extern void FP_inv(FP *x, FP *y, FP *h);

/**	@brief Special exponent of an FP, mod Modulus
 *
	@param x FP number, on exit = x^s mod Modulus
	@param y FP number
 */
extern void FP_fpow(FP *x, FP *y);

/**	@brief Generate random FP
 *
	@param x random FP number
	@param rng random number generator
 */
extern void FP_rand(FP *x, core::csprng *rng);

}

#endif