blob: 94a8e759bee2510b7b62c25aa22648cdef6e7fb1 [file] [log] [blame]
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright (c) 2014, STMicroelectronics International N.V.
*/
#include "mpa.h"
/*************************************************************
*
* HELPER FUNCTIONS
*
*************************************************************/
/*------------------------------------------------------------
*
* __mpa_shift_words_left
*
*/
void __mpa_shift_words_left(mpanum op, mpa_word_t q)
{
mpa_word_t i;
if (q == 0 || __mpanum_is_zero(op))
return;
for (i = __mpanum_size(op) + q - 1; i > q - 1; i--)
op->d[i] = op->d[i - q];
mpa_memset(op->d, 0, BYTES_PER_WORD * q);
/* update the size of op */
if (op->size > 0)
op->size += q;
else
op->size -= q;
}
/*------------------------------------------------------------
*
* __mpa_shift_words_right
*
*/
void __mpa_shift_words_right(mpanum op, mpa_word_t q)
{
mpa_word_t i;
if (q == 0 || __mpanum_is_zero(op))
return;
if (q >= __mpanum_size(op)) {
mpa_set_word(op, 0);
return;
}
for (i = 0; i < __mpanum_size(op) - q; i++)
op->d[i] = op->d[i + q];
/* update the size of dest */
if (op->size > 0)
op->size -= q;
else
op->size += q;
}
/*************************************************************
*
* LIB FUNCTIONS
*
*************************************************************/
/* --------------------------------------------------------------------
* mpa_shift_left
*
* Shifts src left by "steps" step and put result in dest.
* It does not care about signs. Dest will have same sign as src.
*/
void mpa_shift_left(mpanum dest, mpanum src, mpa_word_t steps)
{
mpa_word_t q; /* quotient of steps div WORD_SIZE */
mpa_word_t r; /* remainder of steps div WORD_SIZE */
mpa_word_t i;
/* the bits of the word which will be shifted into another word */
mpa_word_t rbits;
mpa_word_t need_extra_word;
/*
* Copy first, then check, since even a shifted zero should
* be copied.
*/
mpa_copy(dest, src);
__mpa_set_unused_digits_to_zero(dest);
if (steps == 0 || __mpanum_is_zero(dest))
return;
r = steps & (WORD_SIZE - 1); /* 0 <= r < WORD_SIZE */
q = steps >> LOG_OF_WORD_SIZE; /* 0 <= q */
/*
* The size of dest will always increase by at least q.
* If we're shifting r bits and the r highest bits in
* the MSW of dest is zero, we don't need the extra word
* Note:
* We cannot do
* if (_mpanumMSW(dest) >> (WORD_SIZE - r))
* since some compilers (MS) does not shift the word
* if the shift quantity is larger or equal to the word size...
* Otherwise it would be natural to say that (a >> b) is just zero
* if b is larger than the number of bit of a, but no no...
*/
need_extra_word = 0;
if (r == 0) { /* and q > 0 */
/*
* We have a simple shift by words
*/
for (i = __mpanum_size(dest) + q - 1; i > q - 1; i--)
dest->d[i] = dest->d[i - q];
} else {
if (__mpanum_msw(dest) &
(((((mpa_word_t)1 << r) - 1u)) << (WORD_SIZE - r)))
need_extra_word = 1;
/*
* We have a combination of word and bit shifting.
*
* If need_extra_word is 1, the MSW is special and handled
* here
*/
i = __mpanum_size(dest) + q + need_extra_word;
if (need_extra_word) {
rbits = dest->d[i - q - 1] >> (WORD_SIZE - r);
dest->d[i] ^= rbits;
}
i--;
dest->d[i] = dest->d[i - q] << r;
while (i > q) {
rbits = dest->d[i - q - 1] >> (WORD_SIZE - r);
dest->d[i] ^= rbits;
i--;
dest->d[i] = dest->d[i - q] << r;
}
}
mpa_memset(dest->d, 0, BYTES_PER_WORD * q);
/* update the size of dest */
if (dest->size > 0)
dest->size += q + need_extra_word;
else
dest->size -= q + need_extra_word;
}
/*------------------------------------------------------------
*
* mpa_shift_right
*
* Shifts src right by "steps" step and put result in dest.
* It does not care about signs. Dest will have same sign as src.
*
*/
void mpa_shift_right(mpanum dest, mpanum src, mpa_word_t steps)
{
mpa_word_t q; /* quotient of steps div WORD_SIZE */
mpa_word_t r; /* remainder of steps div WORD_SIZE */
mpa_word_t i;
/* the bits of the word which will be shifted into another word */
mpa_word_t rbits;
/*
* Copy first, then check, since even a shifted zero should
* be copied.
*/
mpa_copy(dest, src);
__mpa_set_unused_digits_to_zero(dest);
if (steps == 0 || __mpanum_is_zero(dest))
return;
r = steps & (WORD_SIZE - 1); /* 0 <= r < WORD_SIZE */
q = steps >> LOG_OF_WORD_SIZE; /* 0 <= q */
if (q >= __mpanum_size(dest)) {
mpa_set_word(dest, 0);
return;
}
/*
* Here we have:
* 0 <= r < WORD_SIZE - 1
* 0 <= q < _mpanumSize(dest)
*/
if (r == 0) { /* and q > 0 */
/* Simple shift by words */
for (i = 0; i < __mpanum_size(dest) - q; i++)
dest->d[i] = dest->d[i + q];
} else {
/* combination of word and bit shifting */
for (i = 0; i < __mpanum_size(dest) - q - 1; i++) {
dest->d[i] = dest->d[i + q];
rbits = dest->d[i + q + 1] & ((1UL << r) - 1);
dest->d[i] =
(dest->d[i] >> r) ^ (rbits << (WORD_SIZE - r));
}
/* final word is special */
dest->d[i] = dest->d[i + q] >> r;
}
/* update the size of dest */
if (dest->size > 0)
dest->size -= q;
else
dest->size += q;
/* Take care of the case when we shifted out all bits from MSW */
if (__mpanum_msw(dest) == 0) {
if (dest->size > 0)
dest->size--;
else
dest->size++;
}
}