/*
 *  Copyright (C) 2017 - This file is part of libecc project
 *
 *  Authors:
 *      Ryad BENADJILA <ryadbenadjila@gmail.com>
 *      Arnaud EBALARD <arnaud.ebalard@ssi.gouv.fr>
 *      Jean-Pierre FLORI <jean-pierre.flori@ssi.gouv.fr>
 *
 *  Contributors:
 *      Nicolas VIVET <nicolas.vivet@ssi.gouv.fr>
 *      Karim KHALFALLAH <karim.khalfallah@ssi.gouv.fr>
 *
 *  This software is licensed under a dual BSD and GPL v2 license.
 *  See LICENSE file at the root folder of the project.
 */
#ifndef __WORDS_H__
#define __WORDS_H__

/*
 * Types for words and a few useful macros.
 */

/*
 * If a word size is forced, we use the proper words.h definition.
 * By default, 64-bit word size is used since it is the most reasonable
 * choice across the known platforms (see below).
 */

#define __concat(x) #x
#define _concat(file_prefix, x) __concat(file_prefix##x.h)
#define concat(file_prefix, x) _concat(file_prefix, x)

#if defined(WORDSIZE)
	/* The word size is forced by the compilation chain */
#if (WORDSIZE == 16) || (WORDSIZE == 32) || (WORDSIZE == 64)
	/* Dynamic include depending on the word size */
#include concat(words_, WORDSIZE)
#else
#error Unsupported word size concat
#endif
#else
/* The word size is usually deduced from the known platforms.
 * By default when we have fast builtin uint64_t operations,
 * we use WORDSIZE=64. This is obviously the case on 64-bit platforms,
 * but this should also be the case on most 32-bit platforms where
 * native instructions allow a 32-bit x 32-bit to 64-bit multiplication.
 *
 * There might however be some platforms where this is not the case.
 * Cortex-M0/M0+ for example does not have such a native multiplication
 * instruction, yielding in slower code for WORDSIZE=64 than for WORDSIZE=32.
 * This is also the case for old Thumb ARM CPUs (pre Thumb-2).
 *
 * On 8-bit and 16-bit platform, we prefer to let the user decide on the best
 * option (see below)!
 */

#if defined(__x86_64__) || defined(__i386__) || defined(__ppc64__) || defined(__ppc__) ||\
    defined(__arm__) || defined(__aarch64__) || defined(__mips__) || defined(__s390x__) ||\
    defined(__SH4__) || defined(__sparc__) || (defined(__riscv_xlen) && (__riscv_xlen == 64))
#define WORDSIZE 64
#include "words_64.h"
#else
/* We let the user fix the WORDSIZE manually */
#error "Unrecognized platform. Please specify the word size of your target (with make 16, make 32, make 64)"
#endif /* Unrecognized */
#endif

typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
typedef u16 bitcnt_t;

/*
 * Shift behavior is not defined for a shift count
 * higher than (WORD_BITS - 1). These macros emulate
 * the behavior one would expect, i.e. return 0 when
 * shift count is equal or more than word size.
 */
#define WLSHIFT(w, c) (((c) >= WORD_BITS) ? WORD(0) : (word_t)((w) << (c)))
#define WRSHIFT(w, c) (((c) >= WORD_BITS) ? WORD(0) : (word_t)((w) >> (c)))

/* To be fixed: not really constant-time. */
#define WORD_MIN(a, b) ((a) > (b) ? (b) : (a))

/* WORD_MASK[_IF[NOT]ZERO]: mask of word size and associated macros. */
#define WORD_MASK WORD_MAX

/* These two macros assume two-complement representation. */
#define WORD_MASK_IFZERO(w) (((word_t)((w) != 0)) - WORD(1))
#define WORD_MASK_IFNOTZERO(w) (((word_t)((w) == 0)) - WORD(1))

#define HWORD_MASK HWORD_MAX

/* WORD_HIGHBIT: constant of word size with only MSB set. */
#define WORD_HIGHBIT (WORD(1) << (WORD_BITS - 1))

/* WORD_MUL: multiply two words using schoolbook multiplication on half words */
#define WORD_MUL(outh, outl, in1, in2) do {				\
	word_t in1h, in1l, in2h, in2l;					\
	word_t tmph, tmpm, tmpl;					\
	word_t tmpm1, tmpm2;						\
	word_t carrym, carryl;						\
	/* Get high and low half words. */				\
	in1h = (in1) >> HWORD_BITS;					\
	in1l = (in1) & HWORD_MASK;					\
	in2h = (in2) >> HWORD_BITS;					\
	in2l = (in2) & HWORD_MASK;					\
	/* Compute low product. */					\
	tmpl = in2l * in1l;						\
	/* Compute middle product. */					\
	tmpm1 = in2h * in1l;						\
	tmpm2 = in2l * in1h;						\
	tmpm = tmpm1 + tmpm2;						\
	/* Store middle product carry. */				\
	carrym = tmpm < tmpm1;						\
	/* Compute full low product. */					\
	(outl) = tmpl;							\
	(outl) += (tmpm & HWORD_MASK) << HWORD_BITS;			\
	/* Store full low product carry. */				\
	carryl = (outl) < tmpl;						\
	/* Compute full high product. */				\
	carryl += tmpm >> HWORD_BITS;					\
	carryl += carrym << HWORD_BITS;					\
	tmph = in2h * in1h;						\
	/* No carry can occur below. */					\
	(outh) = tmph + carryl;						\
	} while (0)

#endif /* __WORDS_H__ */
