more fixed sizes, hopefully fix mscv warnings
This commit is contained in:
parent
9dd4a4b5da
commit
7dd7223587
@ -13,7 +13,7 @@ int PQCLEAN_LEDAKEMLT12_LEAKTIME_bf_decoding(DIGIT err[],
|
|||||||
uint8_t unsatParityChecks[N0 * P];
|
uint8_t unsatParityChecks[N0 * P];
|
||||||
POSITION_T currQBlkPos[M], currQBitPos[M];
|
POSITION_T currQBlkPos[M], currQBitPos[M];
|
||||||
POSITION_T syndromePosToFlip, tmp;
|
POSITION_T syndromePosToFlip, tmp;
|
||||||
unsigned int correlation, corrt_syndrome_based;
|
uint32_t correlation, corrt_syndrome_based;
|
||||||
size_t currQoneIdx, endQblockIdx, currblockoffset;
|
size_t currQoneIdx, endQblockIdx, currblockoffset;
|
||||||
int check;
|
int check;
|
||||||
int iteration = 0;
|
int iteration = 0;
|
||||||
@ -22,7 +22,7 @@ int PQCLEAN_LEDAKEMLT12_LEAKTIME_bf_decoding(DIGIT err[],
|
|||||||
PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_copy(currSyndrome, privateSyndrome);
|
PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_copy(currSyndrome, privateSyndrome);
|
||||||
memset(unsatParityChecks, 0x00, N0 * P * sizeof(uint8_t));
|
memset(unsatParityChecks, 0x00, N0 * P * sizeof(uint8_t));
|
||||||
for (size_t i = 0; i < N0; i++) {
|
for (size_t i = 0; i < N0; i++) {
|
||||||
for (size_t valueIdx = 0; valueIdx < P; valueIdx++) {
|
for (POSITION_T valueIdx = 0; valueIdx < P; valueIdx++) {
|
||||||
for (size_t HtrOneIdx = 0; HtrOneIdx < DV; HtrOneIdx++) {
|
for (size_t HtrOneIdx = 0; HtrOneIdx < DV; HtrOneIdx++) {
|
||||||
tmp = (HtrPosOnes[i][HtrOneIdx] + valueIdx) >= P ?
|
tmp = (HtrPosOnes[i][HtrOneIdx] + valueIdx) >= P ?
|
||||||
(HtrPosOnes[i][HtrOneIdx] + valueIdx) - P :
|
(HtrPosOnes[i][HtrOneIdx] + valueIdx) - P :
|
||||||
@ -39,7 +39,7 @@ int PQCLEAN_LEDAKEMLT12_LEAKTIME_bf_decoding(DIGIT err[],
|
|||||||
|
|
||||||
// Computation of correlation with a full Q matrix
|
// Computation of correlation with a full Q matrix
|
||||||
for (size_t i = 0; i < N0; i++) {
|
for (size_t i = 0; i < N0; i++) {
|
||||||
for (size_t j = 0; j < P; j++) {
|
for (POSITION_T j = 0; j < P; j++) {
|
||||||
currQoneIdx = endQblockIdx = 0;
|
currQoneIdx = endQblockIdx = 0;
|
||||||
correlation = 0;
|
correlation = 0;
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ int PQCLEAN_LEDAKEMLT12_LEAKTIME_bf_decoding(DIGIT err[],
|
|||||||
tmp = QtrPosOnes[i][currQoneIdx] + j;
|
tmp = QtrPosOnes[i][currQoneIdx] + j;
|
||||||
tmp = tmp >= P ? tmp - P : tmp;
|
tmp = tmp >= P ? tmp - P : tmp;
|
||||||
currQBitPos[currQoneIdx] = tmp;
|
currQBitPos[currQoneIdx] = tmp;
|
||||||
currQBlkPos[currQoneIdx] = blockIdx;
|
currQBlkPos[currQoneIdx] = (POSITION_T)blockIdx;
|
||||||
correlation += unsatParityChecks[tmp + currblockoffset];
|
correlation += unsatParityChecks[tmp + currblockoffset];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,12 +10,11 @@
|
|||||||
int PQCLEAN_LEDAKEMLT12_LEAKTIME_DFR_test(POSITION_T LSparse[N0][DV * M], uint8_t *secondIterThreshold) {
|
int PQCLEAN_LEDAKEMLT12_LEAKTIME_DFR_test(POSITION_T LSparse[N0][DV * M], uint8_t *secondIterThreshold) {
|
||||||
|
|
||||||
POSITION_T LSparse_loc[N0][DV * M]; /* vector of N_0 sparse blocks */
|
POSITION_T LSparse_loc[N0][DV * M]; /* vector of N_0 sparse blocks */
|
||||||
unsigned int gamma[N0][N0][P] = {{{0}}};
|
uint8_t gamma[N0][N0][P] = {{{0}}};
|
||||||
unsigned int maxMut[N0], maxMutMinusOne[N0];
|
uint32_t gammaHist[N0][DV * M + 1] = {{0}};
|
||||||
unsigned int allBlockMaxSumst, allBlockMaxSumstMinusOne;
|
size_t maxMut[N0], maxMutMinusOne[N0];
|
||||||
unsigned int gammaHist[N0][DV * M + 1] = {{0}};
|
size_t allBlockMaxSumst, allBlockMaxSumstMinusOne;
|
||||||
unsigned int toAdd;
|
size_t histIdx, toAdd;
|
||||||
size_t histIdx;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < N0; i++) {
|
for (size_t i = 0; i < N0; i++) {
|
||||||
for (size_t j = 0; j < DV * M; j++) {
|
for (size_t j = 0; j < DV * M; j++) {
|
||||||
@ -73,7 +72,7 @@ int PQCLEAN_LEDAKEMLT12_LEAKTIME_DFR_test(POSITION_T LSparse[N0][DV * M], uint8_
|
|||||||
/*seek max values across all gamma blocks */
|
/*seek max values across all gamma blocks */
|
||||||
allBlockMaxSumst = maxMut[0];
|
allBlockMaxSumst = maxMut[0];
|
||||||
allBlockMaxSumstMinusOne = maxMutMinusOne[0];
|
allBlockMaxSumstMinusOne = maxMutMinusOne[0];
|
||||||
for (size_t gammaBlockRowIdx = 0; gammaBlockRowIdx < N0 ; gammaBlockRowIdx++) {
|
for (size_t gammaBlockRowIdx = 0; gammaBlockRowIdx < N0; gammaBlockRowIdx++) {
|
||||||
allBlockMaxSumst = allBlockMaxSumst < maxMut[gammaBlockRowIdx] ?
|
allBlockMaxSumst = allBlockMaxSumst < maxMut[gammaBlockRowIdx] ?
|
||||||
maxMut[gammaBlockRowIdx] :
|
maxMut[gammaBlockRowIdx] :
|
||||||
allBlockMaxSumst;
|
allBlockMaxSumst;
|
||||||
|
@ -134,9 +134,7 @@ void PQCLEAN_LEDAKEMLT12_LEAKTIME_gf2x_transpose_in_place(DIGIT A[]) {
|
|||||||
A[NUM_DIGITS_GF2X_ELEMENT - 1 - i] = rev1;
|
A[NUM_DIGITS_GF2X_ELEMENT - 1 - i] = rev1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NUM_DIGITS_GF2X_ELEMENT % 2 == 1) {
|
|
||||||
A[NUM_DIGITS_GF2X_ELEMENT / 2] = reverse_digit(A[NUM_DIGITS_GF2X_ELEMENT / 2]);
|
A[NUM_DIGITS_GF2X_ELEMENT / 2] = reverse_digit(A[NUM_DIGITS_GF2X_ELEMENT / 2]);
|
||||||
}
|
|
||||||
|
|
||||||
if (slack_bits_amount) {
|
if (slack_bits_amount) {
|
||||||
PQCLEAN_LEDAKEMLT12_LEAKTIME_right_bit_shift_n(NUM_DIGITS_GF2X_ELEMENT, A, slack_bits_amount);
|
PQCLEAN_LEDAKEMLT12_LEAKTIME_right_bit_shift_n(NUM_DIGITS_GF2X_ELEMENT, A, slack_bits_amount);
|
||||||
|
@ -13,7 +13,7 @@ int PQCLEAN_LEDAKEMLT32_LEAKTIME_bf_decoding(DIGIT err[],
|
|||||||
uint8_t unsatParityChecks[N0 * P];
|
uint8_t unsatParityChecks[N0 * P];
|
||||||
POSITION_T currQBlkPos[M], currQBitPos[M];
|
POSITION_T currQBlkPos[M], currQBitPos[M];
|
||||||
POSITION_T syndromePosToFlip, tmp;
|
POSITION_T syndromePosToFlip, tmp;
|
||||||
unsigned int correlation, corrt_syndrome_based;
|
uint32_t correlation, corrt_syndrome_based;
|
||||||
size_t currQoneIdx, endQblockIdx, currblockoffset;
|
size_t currQoneIdx, endQblockIdx, currblockoffset;
|
||||||
int check;
|
int check;
|
||||||
int iteration = 0;
|
int iteration = 0;
|
||||||
@ -22,7 +22,7 @@ int PQCLEAN_LEDAKEMLT32_LEAKTIME_bf_decoding(DIGIT err[],
|
|||||||
PQCLEAN_LEDAKEMLT32_LEAKTIME_gf2x_copy(currSyndrome, privateSyndrome);
|
PQCLEAN_LEDAKEMLT32_LEAKTIME_gf2x_copy(currSyndrome, privateSyndrome);
|
||||||
memset(unsatParityChecks, 0x00, N0 * P * sizeof(uint8_t));
|
memset(unsatParityChecks, 0x00, N0 * P * sizeof(uint8_t));
|
||||||
for (size_t i = 0; i < N0; i++) {
|
for (size_t i = 0; i < N0; i++) {
|
||||||
for (size_t valueIdx = 0; valueIdx < P; valueIdx++) {
|
for (POSITION_T valueIdx = 0; valueIdx < P; valueIdx++) {
|
||||||
for (size_t HtrOneIdx = 0; HtrOneIdx < DV; HtrOneIdx++) {
|
for (size_t HtrOneIdx = 0; HtrOneIdx < DV; HtrOneIdx++) {
|
||||||
tmp = (HtrPosOnes[i][HtrOneIdx] + valueIdx) >= P ?
|
tmp = (HtrPosOnes[i][HtrOneIdx] + valueIdx) >= P ?
|
||||||
(HtrPosOnes[i][HtrOneIdx] + valueIdx) - P :
|
(HtrPosOnes[i][HtrOneIdx] + valueIdx) - P :
|
||||||
@ -39,7 +39,7 @@ int PQCLEAN_LEDAKEMLT32_LEAKTIME_bf_decoding(DIGIT err[],
|
|||||||
|
|
||||||
// Computation of correlation with a full Q matrix
|
// Computation of correlation with a full Q matrix
|
||||||
for (size_t i = 0; i < N0; i++) {
|
for (size_t i = 0; i < N0; i++) {
|
||||||
for (size_t j = 0; j < P; j++) {
|
for (POSITION_T j = 0; j < P; j++) {
|
||||||
currQoneIdx = endQblockIdx = 0;
|
currQoneIdx = endQblockIdx = 0;
|
||||||
correlation = 0;
|
correlation = 0;
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ int PQCLEAN_LEDAKEMLT32_LEAKTIME_bf_decoding(DIGIT err[],
|
|||||||
tmp = QtrPosOnes[i][currQoneIdx] + j;
|
tmp = QtrPosOnes[i][currQoneIdx] + j;
|
||||||
tmp = tmp >= P ? tmp - P : tmp;
|
tmp = tmp >= P ? tmp - P : tmp;
|
||||||
currQBitPos[currQoneIdx] = tmp;
|
currQBitPos[currQoneIdx] = tmp;
|
||||||
currQBlkPos[currQoneIdx] = blockIdx;
|
currQBlkPos[currQoneIdx] = (POSITION_T)blockIdx;
|
||||||
correlation += unsatParityChecks[tmp + currblockoffset];
|
correlation += unsatParityChecks[tmp + currblockoffset];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,12 +10,11 @@
|
|||||||
int PQCLEAN_LEDAKEMLT32_LEAKTIME_DFR_test(POSITION_T LSparse[N0][DV * M], uint8_t *secondIterThreshold) {
|
int PQCLEAN_LEDAKEMLT32_LEAKTIME_DFR_test(POSITION_T LSparse[N0][DV * M], uint8_t *secondIterThreshold) {
|
||||||
|
|
||||||
POSITION_T LSparse_loc[N0][DV * M]; /* vector of N_0 sparse blocks */
|
POSITION_T LSparse_loc[N0][DV * M]; /* vector of N_0 sparse blocks */
|
||||||
unsigned int gamma[N0][N0][P] = {{{0}}};
|
uint8_t gamma[N0][N0][P] = {{{0}}};
|
||||||
unsigned int maxMut[N0], maxMutMinusOne[N0];
|
uint32_t gammaHist[N0][DV * M + 1] = {{0}};
|
||||||
unsigned int allBlockMaxSumst, allBlockMaxSumstMinusOne;
|
size_t maxMut[N0], maxMutMinusOne[N0];
|
||||||
unsigned int gammaHist[N0][DV * M + 1] = {{0}};
|
size_t allBlockMaxSumst, allBlockMaxSumstMinusOne;
|
||||||
unsigned int toAdd;
|
size_t histIdx, toAdd;
|
||||||
size_t histIdx;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < N0; i++) {
|
for (size_t i = 0; i < N0; i++) {
|
||||||
for (size_t j = 0; j < DV * M; j++) {
|
for (size_t j = 0; j < DV * M; j++) {
|
||||||
@ -73,7 +72,7 @@ int PQCLEAN_LEDAKEMLT32_LEAKTIME_DFR_test(POSITION_T LSparse[N0][DV * M], uint8_
|
|||||||
/*seek max values across all gamma blocks */
|
/*seek max values across all gamma blocks */
|
||||||
allBlockMaxSumst = maxMut[0];
|
allBlockMaxSumst = maxMut[0];
|
||||||
allBlockMaxSumstMinusOne = maxMutMinusOne[0];
|
allBlockMaxSumstMinusOne = maxMutMinusOne[0];
|
||||||
for (size_t gammaBlockRowIdx = 0; gammaBlockRowIdx < N0 ; gammaBlockRowIdx++) {
|
for (size_t gammaBlockRowIdx = 0; gammaBlockRowIdx < N0; gammaBlockRowIdx++) {
|
||||||
allBlockMaxSumst = allBlockMaxSumst < maxMut[gammaBlockRowIdx] ?
|
allBlockMaxSumst = allBlockMaxSumst < maxMut[gammaBlockRowIdx] ?
|
||||||
maxMut[gammaBlockRowIdx] :
|
maxMut[gammaBlockRowIdx] :
|
||||||
allBlockMaxSumst;
|
allBlockMaxSumst;
|
||||||
|
@ -134,10 +134,6 @@ void PQCLEAN_LEDAKEMLT32_LEAKTIME_gf2x_transpose_in_place(DIGIT A[]) {
|
|||||||
A[NUM_DIGITS_GF2X_ELEMENT - 1 - i] = rev1;
|
A[NUM_DIGITS_GF2X_ELEMENT - 1 - i] = rev1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NUM_DIGITS_GF2X_ELEMENT % 2 == 1) {
|
|
||||||
A[NUM_DIGITS_GF2X_ELEMENT / 2] = reverse_digit(A[NUM_DIGITS_GF2X_ELEMENT / 2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slack_bits_amount) {
|
if (slack_bits_amount) {
|
||||||
PQCLEAN_LEDAKEMLT32_LEAKTIME_right_bit_shift_n(NUM_DIGITS_GF2X_ELEMENT, A, slack_bits_amount);
|
PQCLEAN_LEDAKEMLT32_LEAKTIME_right_bit_shift_n(NUM_DIGITS_GF2X_ELEMENT, A, slack_bits_amount);
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ int PQCLEAN_LEDAKEMLT52_LEAKTIME_bf_decoding(DIGIT err[],
|
|||||||
uint8_t unsatParityChecks[N0 * P];
|
uint8_t unsatParityChecks[N0 * P];
|
||||||
POSITION_T currQBlkPos[M], currQBitPos[M];
|
POSITION_T currQBlkPos[M], currQBitPos[M];
|
||||||
POSITION_T syndromePosToFlip, tmp;
|
POSITION_T syndromePosToFlip, tmp;
|
||||||
unsigned int correlation, corrt_syndrome_based;
|
uint32_t correlation, corrt_syndrome_based;
|
||||||
size_t currQoneIdx, endQblockIdx, currblockoffset;
|
size_t currQoneIdx, endQblockIdx, currblockoffset;
|
||||||
int check;
|
int check;
|
||||||
int iteration = 0;
|
int iteration = 0;
|
||||||
@ -22,7 +22,7 @@ int PQCLEAN_LEDAKEMLT52_LEAKTIME_bf_decoding(DIGIT err[],
|
|||||||
PQCLEAN_LEDAKEMLT52_LEAKTIME_gf2x_copy(currSyndrome, privateSyndrome);
|
PQCLEAN_LEDAKEMLT52_LEAKTIME_gf2x_copy(currSyndrome, privateSyndrome);
|
||||||
memset(unsatParityChecks, 0x00, N0 * P * sizeof(uint8_t));
|
memset(unsatParityChecks, 0x00, N0 * P * sizeof(uint8_t));
|
||||||
for (size_t i = 0; i < N0; i++) {
|
for (size_t i = 0; i < N0; i++) {
|
||||||
for (size_t valueIdx = 0; valueIdx < P; valueIdx++) {
|
for (POSITION_T valueIdx = 0; valueIdx < P; valueIdx++) {
|
||||||
for (size_t HtrOneIdx = 0; HtrOneIdx < DV; HtrOneIdx++) {
|
for (size_t HtrOneIdx = 0; HtrOneIdx < DV; HtrOneIdx++) {
|
||||||
tmp = (HtrPosOnes[i][HtrOneIdx] + valueIdx) >= P ?
|
tmp = (HtrPosOnes[i][HtrOneIdx] + valueIdx) >= P ?
|
||||||
(HtrPosOnes[i][HtrOneIdx] + valueIdx) - P :
|
(HtrPosOnes[i][HtrOneIdx] + valueIdx) - P :
|
||||||
@ -39,7 +39,7 @@ int PQCLEAN_LEDAKEMLT52_LEAKTIME_bf_decoding(DIGIT err[],
|
|||||||
|
|
||||||
// Computation of correlation with a full Q matrix
|
// Computation of correlation with a full Q matrix
|
||||||
for (size_t i = 0; i < N0; i++) {
|
for (size_t i = 0; i < N0; i++) {
|
||||||
for (size_t j = 0; j < P; j++) {
|
for (POSITION_T j = 0; j < P; j++) {
|
||||||
currQoneIdx = endQblockIdx = 0;
|
currQoneIdx = endQblockIdx = 0;
|
||||||
correlation = 0;
|
correlation = 0;
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ int PQCLEAN_LEDAKEMLT52_LEAKTIME_bf_decoding(DIGIT err[],
|
|||||||
tmp = QtrPosOnes[i][currQoneIdx] + j;
|
tmp = QtrPosOnes[i][currQoneIdx] + j;
|
||||||
tmp = tmp >= P ? tmp - P : tmp;
|
tmp = tmp >= P ? tmp - P : tmp;
|
||||||
currQBitPos[currQoneIdx] = tmp;
|
currQBitPos[currQoneIdx] = tmp;
|
||||||
currQBlkPos[currQoneIdx] = blockIdx;
|
currQBlkPos[currQoneIdx] = (POSITION_T)blockIdx;
|
||||||
correlation += unsatParityChecks[tmp + currblockoffset];
|
correlation += unsatParityChecks[tmp + currblockoffset];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,12 +10,11 @@
|
|||||||
int PQCLEAN_LEDAKEMLT52_LEAKTIME_DFR_test(POSITION_T LSparse[N0][DV * M], uint8_t *secondIterThreshold) {
|
int PQCLEAN_LEDAKEMLT52_LEAKTIME_DFR_test(POSITION_T LSparse[N0][DV * M], uint8_t *secondIterThreshold) {
|
||||||
|
|
||||||
POSITION_T LSparse_loc[N0][DV * M]; /* vector of N_0 sparse blocks */
|
POSITION_T LSparse_loc[N0][DV * M]; /* vector of N_0 sparse blocks */
|
||||||
unsigned int gamma[N0][N0][P] = {{{0}}};
|
uint8_t gamma[N0][N0][P] = {{{0}}};
|
||||||
unsigned int maxMut[N0], maxMutMinusOne[N0];
|
uint32_t gammaHist[N0][DV * M + 1] = {{0}};
|
||||||
unsigned int allBlockMaxSumst, allBlockMaxSumstMinusOne;
|
size_t maxMut[N0], maxMutMinusOne[N0];
|
||||||
unsigned int gammaHist[N0][DV * M + 1] = {{0}};
|
size_t allBlockMaxSumst, allBlockMaxSumstMinusOne;
|
||||||
unsigned int toAdd;
|
size_t histIdx, toAdd;
|
||||||
size_t histIdx;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < N0; i++) {
|
for (size_t i = 0; i < N0; i++) {
|
||||||
for (size_t j = 0; j < DV * M; j++) {
|
for (size_t j = 0; j < DV * M; j++) {
|
||||||
@ -73,7 +72,7 @@ int PQCLEAN_LEDAKEMLT52_LEAKTIME_DFR_test(POSITION_T LSparse[N0][DV * M], uint8_
|
|||||||
/*seek max values across all gamma blocks */
|
/*seek max values across all gamma blocks */
|
||||||
allBlockMaxSumst = maxMut[0];
|
allBlockMaxSumst = maxMut[0];
|
||||||
allBlockMaxSumstMinusOne = maxMutMinusOne[0];
|
allBlockMaxSumstMinusOne = maxMutMinusOne[0];
|
||||||
for (size_t gammaBlockRowIdx = 0; gammaBlockRowIdx < N0 ; gammaBlockRowIdx++) {
|
for (size_t gammaBlockRowIdx = 0; gammaBlockRowIdx < N0; gammaBlockRowIdx++) {
|
||||||
allBlockMaxSumst = allBlockMaxSumst < maxMut[gammaBlockRowIdx] ?
|
allBlockMaxSumst = allBlockMaxSumst < maxMut[gammaBlockRowIdx] ?
|
||||||
maxMut[gammaBlockRowIdx] :
|
maxMut[gammaBlockRowIdx] :
|
||||||
allBlockMaxSumst;
|
allBlockMaxSumst;
|
||||||
|
@ -134,10 +134,6 @@ void PQCLEAN_LEDAKEMLT52_LEAKTIME_gf2x_transpose_in_place(DIGIT A[]) {
|
|||||||
A[NUM_DIGITS_GF2X_ELEMENT - 1 - i] = rev1;
|
A[NUM_DIGITS_GF2X_ELEMENT - 1 - i] = rev1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NUM_DIGITS_GF2X_ELEMENT % 2 == 1) {
|
|
||||||
A[NUM_DIGITS_GF2X_ELEMENT / 2] = reverse_digit(A[NUM_DIGITS_GF2X_ELEMENT / 2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slack_bits_amount) {
|
if (slack_bits_amount) {
|
||||||
PQCLEAN_LEDAKEMLT52_LEAKTIME_right_bit_shift_n(NUM_DIGITS_GF2X_ELEMENT, A, slack_bits_amount);
|
PQCLEAN_LEDAKEMLT52_LEAKTIME_right_bit_shift_n(NUM_DIGITS_GF2X_ELEMENT, A, slack_bits_amount);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user