/*  */

#ifndef _UECC_ASM_OTHER_H_
#define _UECC_ASM_OTHER_H_

#if ((uECC_OPTIMIZATION_LEVEL >= 2) && (uECC_MAX_WORDS == 4) || (uECC_MAX_WORDS == 8))

#if !asm_clear
uECC_VLI_API void uECC_vli_clear(uECC_word_t *vli, wordcount_t num_words) {
    if (num_words == uECC_MAX_WORDS) {
        vli[0] = 0;
        vli[1] = 0;
        vli[2] = 0;
        vli[3] = 0;
#if (uECC_MAX_WORDS == 8)
        vli[4] = 0;
        vli[5] = 0;
        vli[6] = 0;
        vli[7] = 0;
#endif
    }
    else {
        int i;
        for (i = 0; i < num_words; ++i) {
            vli[(unsigned)i] = 0;
        }
    }
}
#define asm_clear 1
#endif /* !asm_clear */

/* Returns 1 if vli == 0, 0 otherwise. */
#if !asm_isZero
uECC_VLI_API uECC_word_t uECC_vli_isZero(const uECC_word_t *vli, wordcount_t num_words) {
    if (num_words == uECC_MAX_WORDS) {
        return ((vli[0] | vli[1] | vli[2] | vli[3]
#if (uECC_MAX_WORDS == 8)
            | vli[4] | vli[5] | vli[6] | vli[7]
#endif
            ) == 0);
    }
    else {
        uECC_word_t bits = 0;
        int i;
        for (i = 0; i < num_words; ++i) {
            bits |= vli[(unsigned)i];
        }
        return (bits == 0);
    }
}
#define asm_isZero 1
#endif /* !asm_isZero */

/* Sets dest = src. */
#if !asm_set
uECC_VLI_API void uECC_vli_set(uECC_word_t *dest, const uECC_word_t *src, wordcount_t num_words) {
    if (num_words == uECC_MAX_WORDS) {
        dest[0] = src[0];
        dest[1] = src[1];
        dest[2] = src[2];
        dest[3] = src[3];
#if (uECC_MAX_WORDS == 8)
        dest[4] = src[4];
        dest[5] = src[5];
        dest[6] = src[6];
        dest[7] = src[7];
#endif
    }
    else {
        int i;
        for (i = 0; i < num_words; ++i) {
            dest[(unsigned)i] = src[(unsigned)i];
        }
    }
}
#define asm_set 1
#endif /* !asm_set */

/* Returns sign of left - right. */
#if !asm_cmp_unsafe
static cmpresult_t uECC_vli_cmp_unsafe(const uECC_word_t *left,
                                       const uECC_word_t *right,
                                       wordcount_t num_words) {
    if (num_words == uECC_MAX_WORDS) {
        return (
#if (uECC_MAX_WORDS == 8)
            ((left[7] != right[7]) ? ((left[7] > right[7]) ? 1 : -1) :
             ((left[6] != right[6]) ? ((left[6] > right[6]) ? 1 : -1) :
              ((left[5] != right[5]) ? ((left[5] > right[5]) ? 1 : -1) :
               ((left[4] != right[4]) ? ((left[4] > right[4]) ? 1 : -1) :
#endif
                ((left[3] != right[3]) ? ((left[3] > right[3]) ? 1 : -1) :
                 ((left[2] != right[2]) ? ((left[2] > right[2]) ? 1 : -1) :
                  ((left[1] != right[1]) ? ((left[1] > right[1]) ? 1 : -1) :
                   ((left[0] != right[0]) ? ((left[0] > right[0]) ? 1 : -1) : 0))))
#if (uECC_MAX_WORDS == 8)
            ))))
#endif
            );
    }
    else {
        int i;
        for (i = num_words - 1; i >= 0; --i) {
            if (left[(unsigned)i] != right[(unsigned)i]) {
                return (left[(unsigned)i] > right[(unsigned)i]) ? 1 : -1;
            }
        }
        return 0;
    }
}
#define asm_cmp_unsafe 1
#endif /* !asm_cmp_unsafe */

/* Returns one if left == right, zero otherwise. */
#if !asm_equal
uECC_VLI_API uECC_word_t uECC_vli_equal(const uECC_word_t *left,
                                        const uECC_word_t *right,
                                        wordcount_t num_words) {
    if (num_words == uECC_MAX_WORDS) {
        return (((left[0] ^ right[0])
            | (left[1] ^ right[1])
            | (left[2] ^ right[2])
            | (left[3] ^ right[3])
#if (uECC_MAX_WORDS == 8)
            | (left[4] ^ right[4])
            | (left[5] ^ right[5])
            | (left[6] ^ right[6])
            | (left[7] ^ right[7])
#endif
            ) == 0);
    }
    else {
        uECC_word_t diff = 0;
        int i;
        for (i = 0; i < num_words; ++i) {
            diff |= (left[(unsigned)i] ^ right[(unsigned)i]);
        }
        return (diff == 0);
    }
}
#define asm_equal 1
#endif /* !asm_equal */

/* Computes vli = vli << 1. */
#if !asm_lshift1
uECC_VLI_API void uECC_vli_lshift1(uECC_word_t *vli, wordcount_t num_words) {
    if (num_words == uECC_MAX_WORDS) {
#if (uECC_MAX_WORDS == 8)
        vli[7] = (vli[7] << 1) | (vli[6] >> (uECC_WORD_BITS - 1));
        vli[6] = (vli[6] << 1) | (vli[5] >> (uECC_WORD_BITS - 1));
        vli[5] = (vli[5] << 1) | (vli[4] >> (uECC_WORD_BITS - 1));
        vli[4] = (vli[4] << 1) | (vli[3] >> (uECC_WORD_BITS - 1));
#endif
        vli[3] = (vli[3] << 1) | (vli[2] >> (uECC_WORD_BITS - 1));
        vli[2] = (vli[2] << 1) | (vli[1] >> (uECC_WORD_BITS - 1));
        vli[1] = (vli[1] << 1) | (vli[0] >> (uECC_WORD_BITS - 1));
        vli[0] <<= 1;
    }
    else {
        int i;
        uECC_word_t carry = 0;
        
        for (i = 0; i < num_words; i++) {
            uECC_word_t temp = vli[i];
            vli[i] = (temp << 1) | carry;
            carry = temp >> (uECC_WORD_BITS - 1);
        }
    }
}
#define asm_lshift1 1
#endif /* !asm_lshift1 */

/* Computes vli = vli >> 1. */
#if !asm_rshift1
uECC_VLI_API void uECC_vli_rshift1(uECC_word_t *vli, wordcount_t num_words) {
    if (num_words == uECC_MAX_WORDS) {
        vli[0] = (vli[0] >> 1) | (vli[1] << (uECC_WORD_BITS - 1));
        vli[1] = (vli[1] >> 1) | (vli[2] << (uECC_WORD_BITS - 1));
        vli[2] = (vli[2] >> 1) | (vli[3] << (uECC_WORD_BITS - 1));
#if (uECC_MAX_WORDS == 8)
        vli[3] = (vli[3] >> 1) | (vli[4] << (uECC_WORD_BITS - 1));
        vli[4] = (vli[4] >> 1) | (vli[5] << (uECC_WORD_BITS - 1));
        vli[5] = (vli[5] >> 1) | (vli[6] << (uECC_WORD_BITS - 1));
        vli[6] = (vli[6] >> 1) | (vli[7] << (uECC_WORD_BITS - 1));
        vli[7] >>= 1;
#else
        vli[3] >>= 1;
#endif
    }
    else {
        uECC_word_t *end = vli;
        uECC_word_t carry = 0;
        
        vli += num_words;
        while (vli-- > end) {
            uECC_word_t temp = *vli;
            *vli = (temp >> 1) | carry;
            carry = temp << (uECC_WORD_BITS - 1);
        }
    }
}
#define asm_rshift1 1
#endif /* !asm_rshift1 */

/* Computes result = left + right, returning carry. Can modify in place. */
#if !asm_add
uECC_VLI_API uECC_word_t uECC_vli_add(uECC_word_t *result,
                                      const uECC_word_t *left,
                                      const uECC_word_t *right,
                                      wordcount_t num_words) {
    if (num_words == uECC_MAX_WORDS) {
        uECC_word_t sum = left[0] + right[0];
        uECC_word_t carry;
        if (sum < left[0]) {
            result[0] = sum;
            sum = left[1] + right[1] + 1;
            if (sum <= left[1]) {
                result[1] = sum;
                sum = left[2] + right[2] + 1;
                if (sum <= left[2]) {
                    result[2] = sum;
                    sum = left[3] + right[3] + 1;
                    carry = (sum <= left[3]);
                }
                else {
                    result[2] = sum;
                    sum = left[3] + right[3];
                    carry = (sum < left[3]);
                }
            }
            else {
                result[1] = sum;
                sum = left[2] + right[2];
                if (sum < left[2]) {
                    result[2] = sum;
                    sum = left[3] + right[3] + 1;
                    carry = (sum <= left[3]);
                }
                else {
                    result[2] = sum;
                    sum = left[3] + right[3];
                    carry = (sum < left[3]);
                }
            }
        }
        else {
            result[0] = sum;
            sum = left[1] + right[1];
            if (sum < left[1]) {
                result[1] = sum;
                sum = left[2] + right[2] + 1;
                if (sum <= left[2]) {
                    result[2] = sum;
                    sum = left[3] + right[3] + 1;
                    carry = (sum <= left[3]);
                }
                else {
                    result[2] = sum;
                    sum = left[3] + right[3];
                    carry = (sum < left[3]);
                }
            }
            else {
                result[1] = sum;
                sum = left[2] + right[2];
                if (sum < left[2]) {
                    result[2] = sum;
                    sum = left[3] + right[3] + 1;
                    carry = (sum <= left[3]);
                }
                else {
                    result[2] = sum;
                    sum = left[3] + right[3];
                    carry = (sum < left[3]);
               }
            }
        }
        result[3] = sum;
#if (uECC_MAX_WORDS == 8)
        sum = left[4] + right[4] + carry;
        if (carry ? (sum <= left[4]) : (sum < left[4])) {
            result[4] = sum;
            sum = left[5] + right[5] + 1;
            if (sum <= left[5]) {
                result[5] = sum;
                sum = left[6] + right[6] + 1;
                if (sum <= left[6]) {
                    result[6] = sum;
                    sum = left[7] + right[7] + 1;
                    carry = (sum <= left[7]);
                }
                else {
                    result[6] = sum;
                    sum = left[7] + right[7];
                    carry = (sum < left[7]);
                }
            }
            else {
                result[5] = sum;
                sum = left[6] + right[6];
                if (sum < left[6]) {
                    result[6] = sum;
                    sum = left[7] + right[7] + 1;
                    carry = (sum <= left[7]);
                }
                else {
                    result[6] = sum;
                    sum = left[7] + right[7];
                    carry = (sum < left[7]);
                }
            }
        }
        else {
            result[4] = sum;
            sum = left[5] + right[5];
            if (sum < left[5]) {
                result[5] = sum;
                sum = left[6] + right[6] + 1;
                if (sum <= left[6]) {
                    result[6] = sum;
                    sum = left[7] + right[7] + 1;
                    carry = (sum <= left[7]);
                }
                else {
                    result[6] = sum;
                    sum = left[7] + right[7];
                    carry = (sum < left[7]);
                }
            }
            else {
                result[5] = sum;
                sum = left[6] + right[6];
                if (sum < left[6]) {
                    result[6] = sum;
                    sum = left[7] + right[7] + 1;
                    carry = (sum <= left[7]);
                }
                else {
                    result[6] = sum;
                    sum = left[7] + right[7];
                    carry = (sum < left[7]);
               }
            }
        }
        result[7] = sum;
#endif
        return carry;
    }
    else {
        uECC_word_t carry = 0;
        int i;
        for (i = 0; i < num_words; ++i) {
            uECC_word_t sum = left[(unsigned)i] + right[(unsigned)i] + carry;
            if      (sum > left[(unsigned)i]) carry = 0;
            else if (sum < left[(unsigned)i]) carry = 1;
            result[(unsigned)i] = sum;
        }
        return carry;
    }
}
#define asm_add 1
#endif /* !asm_add */

/* Computes result = left - right, returning borrow. Can modify in place. */
#if !asm_sub
uECC_VLI_API uECC_word_t uECC_vli_sub(uECC_word_t *result,
                                      const uECC_word_t *left,
                                      const uECC_word_t *right,
                                      wordcount_t num_words) {
    if (num_words == uECC_MAX_WORDS) {
        uECC_word_t diff = left[0] - right[0];
        uECC_word_t borrow;
        if (diff > left[0]) {
            result[0] = diff;
            diff = left[1] - right[1] - 1;
            if (diff >= left[1]) {
                result[1] = diff;
                diff = left[2] - right[2] - 1;
                if (diff >= left[2]) {
                    result[2] = diff;
                    diff = left[3] - right[3] - 1;
                    borrow = (diff >= left[3]);
                }
                else {
                    result[2] = diff;
                    diff = left[3] - right[3];
                    borrow = (diff > left[3]);
                }
            }
            else {
                result[1] = diff;
                diff = left[2] - right[2];
                if (diff > left[2]) {
                    result[2] = diff;
                    diff = left[3] - right[3] - 1;
                    borrow = (diff >= left[3]);
                }
                else {
                    result[2] = diff;
                    diff = left[3] - right[3];
                    borrow = (diff > left[3]);
                }
            }
        }
        else {
            result[0] = diff;
            diff = left[1] - right[1];
            if (diff > left[1]) {
                result[1] = diff;
                diff = left[2] - right[2] - 1;
                if (diff >= left[2]) {
                    result[2] = diff;
                    diff = left[3] - right[3] - 1;
                    borrow = (diff >= left[3]);
                }
                else {
                    result[2] = diff;
                    diff = left[3] - right[3];
                    borrow = (diff > left[3]);
                }
            }
            else {
                result[1] = diff;
                diff = left[2] - right[2];
                if (diff > left[2]) {
                    result[2] = diff;
                    diff = left[3] - right[3] - 1;
                    borrow = (diff >= left[3]);
                }
                else {
                    result[2] = diff;
                    diff = left[3] - right[3];
                    borrow = (diff > left[3]);
                }
            }
        }
        result[3] = diff;
#if (uECC_MAX_WORDS == 8)
        diff = left[4] - right[4] - borrow;
        if (borrow ? (diff >= left[4]) : (diff > left[4])) {
            result[4] = diff;
            diff = left[5] - right[5] - 1;
            if (diff >= left[5]) {
                result[5] = diff;
                diff = left[6] - right[6] - 1;
                if (diff >= left[6]) {
                    result[6] = diff;
                    diff = left[7] - right[7] - 1;
                    borrow = (diff >= left[7]);
                }
                else {
                    result[6] = diff;
                    diff = left[7] - right[7];
                    borrow = (diff > left[7]);
                }
            }
            else {
                result[5] = diff;
                diff = left[6] - right[6];
                if (diff > left[6]) {
                    result[6] = diff;
                    diff = left[7] - right[7] - 1;
                    borrow = (diff >= left[7]);
                }
                else {
                    result[6] = diff;
                    diff = left[7] - right[7];
                    borrow = (diff > left[7]);
                }
            }
        }
        else {
            result[4] = diff;
            diff = left[5] - right[5];
            if (diff > left[5]) {
                result[5] = diff;
                diff = left[6] - right[6] - 1;
                if (diff >= left[6]) {
                    result[6] = diff;
                    diff = left[7] - right[7] - 1;
                    borrow = (diff >= left[7]);
                }
                else {
                    result[6] = diff;
                    diff = left[7] - right[7];
                    borrow = (diff > left[7]);
                }
            }
            else {
                result[5] = diff;
                diff = left[6] - right[6];
                if (diff > left[6]) {
                    result[6] = diff;
                    diff = left[7] - right[7] - 1;
                    borrow = (diff >= left[7]);
                }
                else {
                    result[6] = diff;
                    diff = left[7] - right[7];
                    borrow = (diff > left[7]);
                }
            }
        }
        result[7] = diff;
#endif
        return borrow;
    }
    else {
        uECC_word_t borrow = 0;
        int i;
        for (i = 0; i < num_words; ++i) {
            uECC_word_t diff = left[(unsigned)i] - right[(unsigned)i] - borrow;
            if      (diff < left[(unsigned)i]) borrow = 0;
            else if (diff > left[(unsigned)i]) borrow = 1;
            result[(unsigned)i] = diff;
        }
        return borrow;
    }
}
#define asm_sub 1
#endif /* !asm_sub */

#if !asm_mult
uECC_VLI_API void uECC_vli_mult(uECC_word_t *result,
                                const uECC_word_t *left,
                                const uECC_word_t *right,
                                wordcount_t num_words) {
#if (uECC_MAX_WORDS == 4)
#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128
#else
    if (num_words == uECC_MAX_WORDS) {
        uECC_dword_t r01;
        uECC_word_t r2;
        uECC_dword_t p;
        r01 = ((uECC_dword_t)left[0]) * ((uECC_dword_t)right[0]);
        result[0] = (uECC_word_t) r01;
        r01 >>= uECC_WORD_BITS;
        p = ((uECC_dword_t)left[0]) * ((uECC_dword_t)right[1]); r01 += p; r2 = (r01 < p);
        p = ((uECC_dword_t)left[1]) * ((uECC_dword_t)right[0]); r01 += p; if (r01 < p) r2++;
        result[1] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        p = ((uECC_dword_t)left[0]) * ((uECC_dword_t)right[2]); r01 += p; r2 = (r01 < p);
        p = ((uECC_dword_t)left[1]) * ((uECC_dword_t)right[1]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[2]) * ((uECC_dword_t)right[0]); r01 += p; if (r01 < p) r2++;
        result[2] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        p = ((uECC_dword_t)left[0]) * ((uECC_dword_t)right[3]); r01 += p; r2 = (r01 < p);
        p = ((uECC_dword_t)left[1]) * ((uECC_dword_t)right[2]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[2]) * ((uECC_dword_t)right[1]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[3]) * ((uECC_dword_t)right[0]); r01 += p; if (r01 < p) r2++;
        result[3] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
#if (uECC_MAX_WORDS == 8)
        p = ((uECC_dword_t)left[0]) * ((uECC_dword_t)right[4]); r01 += p; r2 = (r01 < p);
        p = ((uECC_dword_t)left[1]) * ((uECC_dword_t)right[3]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[2]) * ((uECC_dword_t)right[2]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[3]) * ((uECC_dword_t)right[1]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[4]) * ((uECC_dword_t)right[0]); r01 += p; if (r01 < p) r2++;
        result[4] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        p = ((uECC_dword_t)left[0]) * ((uECC_dword_t)right[5]); r01 += p; r2 = (r01 < p);
        p = ((uECC_dword_t)left[1]) * ((uECC_dword_t)right[4]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[2]) * ((uECC_dword_t)right[3]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[3]) * ((uECC_dword_t)right[2]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[4]) * ((uECC_dword_t)right[1]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[5]) * ((uECC_dword_t)right[0]); r01 += p; if (r01 < p) r2++;
        result[5] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        p = ((uECC_dword_t)left[0]) * ((uECC_dword_t)right[6]); r01 += p; r2 = (r01 < p);
        p = ((uECC_dword_t)left[1]) * ((uECC_dword_t)right[5]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[2]) * ((uECC_dword_t)right[4]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[3]) * ((uECC_dword_t)right[3]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[4]) * ((uECC_dword_t)right[2]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[5]) * ((uECC_dword_t)right[1]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[6]) * ((uECC_dword_t)right[0]); r01 += p; if (r01 < p) r2++;
        result[6] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        p = ((uECC_dword_t)left[0]) * ((uECC_dword_t)right[7]); r01 += p; r2 = (r01 < p);
        p = ((uECC_dword_t)left[1]) * ((uECC_dword_t)right[6]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[2]) * ((uECC_dword_t)right[5]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[3]) * ((uECC_dword_t)right[4]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[4]) * ((uECC_dword_t)right[3]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[5]) * ((uECC_dword_t)right[2]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[6]) * ((uECC_dword_t)right[1]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[7]) * ((uECC_dword_t)right[0]); r01 += p; if (r01 < p) r2++;
        result[7] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        p = ((uECC_dword_t)left[1]) * ((uECC_dword_t)right[7]); r01 += p; r2 = (r01 < p);
        p = ((uECC_dword_t)left[2]) * ((uECC_dword_t)right[6]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[3]) * ((uECC_dword_t)right[5]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[4]) * ((uECC_dword_t)right[4]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[5]) * ((uECC_dword_t)right[3]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[6]) * ((uECC_dword_t)right[2]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[7]) * ((uECC_dword_t)right[1]); r01 += p; if (r01 < p) r2++;
        result[8] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        p = ((uECC_dword_t)left[2]) * ((uECC_dword_t)right[7]); r01 += p; r2 = (r01 < p);
        p = ((uECC_dword_t)left[3]) * ((uECC_dword_t)right[6]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[4]) * ((uECC_dword_t)right[5]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[5]) * ((uECC_dword_t)right[4]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[6]) * ((uECC_dword_t)right[3]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[7]) * ((uECC_dword_t)right[2]); r01 += p; if (r01 < p) r2++;
        result[9] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        p = ((uECC_dword_t)left[3]) * ((uECC_dword_t)right[7]); r01 += p; r2 = (r01 < p);
        p = ((uECC_dword_t)left[4]) * ((uECC_dword_t)right[6]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[5]) * ((uECC_dword_t)right[5]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[6]) * ((uECC_dword_t)right[4]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[7]) * ((uECC_dword_t)right[3]); r01 += p; if (r01 < p) r2++;
        result[10] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        p = ((uECC_dword_t)left[4]) * ((uECC_dword_t)right[7]); r01 += p; r2 = (r01 < p);
        p = ((uECC_dword_t)left[5]) * ((uECC_dword_t)right[6]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[6]) * ((uECC_dword_t)right[5]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[7]) * ((uECC_dword_t)right[4]); r01 += p; if (r01 < p) r2++;
        result[11] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
#endif
        p = ((uECC_dword_t)left[uECC_MAX_WORDS-3]) * ((uECC_dword_t)right[uECC_MAX_WORDS-1]); r01 += p; r2 = (r01 < p);
        p = ((uECC_dword_t)left[uECC_MAX_WORDS-2]) * ((uECC_dword_t)right[uECC_MAX_WORDS-2]); r01 += p; if (r01 < p) r2++;
        p = ((uECC_dword_t)left[uECC_MAX_WORDS-1]) * ((uECC_dword_t)right[uECC_MAX_WORDS-3]); r01 += p; if (r01 < p) r2++;
        result[2*uECC_MAX_WORDS-4] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        p = ((uECC_dword_t)left[uECC_MAX_WORDS-2]) * ((uECC_dword_t)right[uECC_MAX_WORDS-1]); r01 += p; r2 = (r01 < p);
        p = ((uECC_dword_t)left[uECC_MAX_WORDS-1]) * ((uECC_dword_t)right[uECC_MAX_WORDS-2]); r01 += p; if (r01 < p) r2++;
        result[2*uECC_MAX_WORDS-3] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        p = ((uECC_dword_t)left[uECC_MAX_WORDS-1]) * ((uECC_dword_t)right[uECC_MAX_WORDS-1]); r01 += p;
        result[2*uECC_MAX_WORDS-2] = (uECC_word_t) r01;
        result[2*uECC_MAX_WORDS-1] = (uECC_word_t)(r01 >> uECC_WORD_BITS);
    }
    else
#endif
#endif
    {
#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128
        uECC_word_t r0 = 0;
        uECC_word_t r1 = 0;
#else
        uECC_dword_t r01 = 0;
#endif
        uECC_word_t r2 = 0;
        int i, k;

        /* Compute each digit of result in sequence, maintaining the carries. */
        for (k = 0; k < num_words; ++k) {
            for (i = 0; i <= k; ++i) {
                uECC_word_t a = left[(unsigned)i];
                uECC_word_t b = right[(unsigned)(k - i)];
#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128
                uint64_t a0 = a & 0xffffffffull;
                uint64_t a1 = a >> 32;
                uint64_t b0 = b & 0xffffffffull;
                uint64_t b1 = b >> 32;

                uint64_t i0 = a0 * b0;
                uint64_t i1 = a0 * b1;
                uint64_t i2 = a1 * b0;
                uint64_t i3 = a1 * b1;

                uint64_t p0, p1;

                i2 += (i0 >> 32);
                i2 += i1;
                if (i2 < i1) { /* overflow */
                    i3 += 0x100000000ull;
                }

                p0 = (i0 & 0xffffffffull) | (i2 << 32);
                p1 = i3 + (i2 >> 32);

                r0 += p0;
                r1 += (p1 + (r0 < p0));
                if ((r1 < p1) || (r1 == p1 && r0 < p0)) r2++;
#else
                uECC_dword_t p = (uECC_dword_t)a * b;
                r01 += p;
                if (r01 < p) r2++;
#endif
            }
#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128
            result[(unsigned)k] = r0;
            r0 = r1;
            r1 = r2;
#else
            result[(unsigned)k] = (uECC_word_t) r01;
            r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
#endif
            r2 = 0;
        }
        for (k = num_words; k < num_words * 2 - 1; ++k) {
            for (i = (k + 1) - num_words; i < num_words; ++i) {
                uECC_word_t a = left[(unsigned)i];
                uECC_word_t b = right[(unsigned)(k - i)];
#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128
                uint64_t a0 = a & 0xffffffffull;
                uint64_t a1 = a >> 32;
                uint64_t b0 = b & 0xffffffffull;
                uint64_t b1 = b >> 32;

                uint64_t i0 = a0 * b0;
                uint64_t i1 = a0 * b1;
                uint64_t i2 = a1 * b0;
                uint64_t i3 = a1 * b1;

                uint64_t p0, p1;

                i2 += (i0 >> 32);
                i2 += i1;
                if (i2 < i1) { /* overflow */
                    i3 += 0x100000000ull;
                }

                p0 = (i0 & 0xffffffffull) | (i2 << 32);
                p1 = i3 + (i2 >> 32);

                r0 += p0;
                r1 += (p1 + (r0 < p0));
                if ((r1 < p1) || (r1 == p1 && r0 < p0)) r2++;
#else
                uECC_dword_t p = (uECC_dword_t)a * b;
                r01 += p;
                if (r01 < p) r2++;
#endif
            }
#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128
            result[(unsigned)k] = r0;
            r0 = r1;
            r1 = r2;
#else
            result[(unsigned)k] = (uECC_word_t) r01;
            r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
#endif
            r2 = 0;
        }
#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128
        result[num_words * 2 - 1] = r0;
#else
        result[num_words * 2 - 1] = (uECC_word_t) r01;
#endif
    }
}
#define asm_mult 1
#endif /* !asm_mult */

#if uECC_SQUARE_FUNC
#if !asm_square
uECC_VLI_API void uECC_vli_square(uECC_word_t *result,
                                  const uECC_word_t *left,
                                  wordcount_t num_words) {
#if (uECC_MAX_WORDS == 4)
#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128
#else
    if (num_words == uECC_MAX_WORDS) {
        uECC_dword_t r01, d;
        uECC_word_t r2;
        uECC_dword_t p;
        r01 = ((uECC_dword_t)left[0]) * ((uECC_dword_t)left[0]);
        result[0] = (uECC_word_t) r01;
        r01 >>= uECC_WORD_BITS;
        p = ((uECC_dword_t)left[0]) * ((uECC_dword_t)left[1]); r01 += p; r2 = (r01 < p);
                                             r01 += p; r2 += (r01 < p);
        result[1] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        p = ((uECC_dword_t)left[1]) * ((uECC_dword_t)left[1]); r01 += p; r2 = (r01 < p);
        p = ((uECC_dword_t)left[0]) * ((uECC_dword_t)left[2]); r01 += p; if (r01 < p) r2++;
                                             r01 += p; if (r01 < p) r2++;
        result[2] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        d = ((uECC_dword_t)left[0]) * ((uECC_dword_t)left[3]);
        p = ((uECC_dword_t)left[1]) * ((uECC_dword_t)left[2]); d += p; r2 = (d < p) ? 2 : 0;
                                             r01 += d; if (r01 < d) r2++;
                                             r01 += d; if (r01 < d) r2++;
        result[3] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
#if (uECC_MAX_WORDS == 8)
        p = ((uECC_dword_t)left[2]) * ((uECC_dword_t)left[2]); r01 += p; r2 = (r01 < p);
        d = ((uECC_dword_t)left[0]) * ((uECC_dword_t)left[4]);
        p = ((uECC_dword_t)left[1]) * ((uECC_dword_t)left[3]); d += p; if (d < p) r2 += 2;
                                             r01 += d; if (r01 < d) r2++;
                                             r01 += d; if (r01 < d) r2++;
        result[4] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        d = ((uECC_dword_t)left[0]) * ((uECC_dword_t)left[5]);
        p = ((uECC_dword_t)left[1]) * ((uECC_dword_t)left[4]); d += p; r2 = (d < p) ? 2 : 0;
        p = ((uECC_dword_t)left[2]) * ((uECC_dword_t)left[3]); d += p; if (d < p) r2 += 2;
                                             r01 += d; if (r01 < d) r2++;
                                             r01 += d; if (r01 < d) r2++;
        result[5] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        p = ((uECC_dword_t)left[3]) * ((uECC_dword_t)left[3]); r01 += p; r2 = (r01 < p);
        d = ((uECC_dword_t)left[0]) * ((uECC_dword_t)left[6]);
        p = ((uECC_dword_t)left[1]) * ((uECC_dword_t)left[5]); d += p; if (d < p) r2 += 2;
        p = ((uECC_dword_t)left[2]) * ((uECC_dword_t)left[4]); d += p; if (d < p) r2 += 2;
                                             r01 += d; if (r01 < d) r2++;
                                             r01 += d; if (r01 < d) r2++;
        result[6] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        d = ((uECC_dword_t)left[0]) * ((uECC_dword_t)left[7]);
        p = ((uECC_dword_t)left[1]) * ((uECC_dword_t)left[6]); d += p; r2 = (d < p) ? 2 : 0;
        p = ((uECC_dword_t)left[2]) * ((uECC_dword_t)left[5]); d += p; if (d < p) r2 += 2;
        p = ((uECC_dword_t)left[3]) * ((uECC_dword_t)left[4]); d += p; if (d < p) r2 += 2;
                                             r01 += d; if (r01 < d) r2++;
                                             r01 += d; if (r01 < d) r2++;
        result[7] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        p = ((uECC_dword_t)left[4]) * ((uECC_dword_t)left[4]); r01 += p; r2 = (r01 < p);
        d = ((uECC_dword_t)left[1]) * ((uECC_dword_t)left[7]);
        p = ((uECC_dword_t)left[2]) * ((uECC_dword_t)left[6]); d += p; if (d < p) r2 += 2;
        p = ((uECC_dword_t)left[3]) * ((uECC_dword_t)left[5]); d += p; if (d < p) r2 += 2;
                                             r01 += d; if (r01 < d) r2++;
                                             r01 += d; if (r01 < d) r2++;
        result[8] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        d = ((uECC_dword_t)left[2]) * ((uECC_dword_t)left[7]);
        p = ((uECC_dword_t)left[3]) * ((uECC_dword_t)left[6]); d += p; r2 = (d < p) ? 2 : 0;
        p = ((uECC_dword_t)left[4]) * ((uECC_dword_t)left[5]); d += p; if (d < p) r2 += 2;
                                             r01 += d; if (r01 < d) r2++;
                                             r01 += d; if (r01 < d) r2++;
        result[9] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        p = ((uECC_dword_t)left[5]) * ((uECC_dword_t)left[5]); r01 += p; r2 = (r01 < p);
        d = ((uECC_dword_t)left[3]) * ((uECC_dword_t)left[7]);
        p = ((uECC_dword_t)left[4]) * ((uECC_dword_t)left[6]); d += p; if (d < p) r2 += 2;
                                             r01 += d; if (r01 < d) r2++;
                                             r01 += d; if (r01 < d) r2++;
        result[10] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        d = ((uECC_dword_t)left[4]) * ((uECC_dword_t)left[7]);
        p = ((uECC_dword_t)left[5]) * ((uECC_dword_t)left[6]); d += p; r2 = (d < p) ? 2 : 0;
                                             r01 += d; if (r01 < d) r2++;
                                             r01 += d; if (r01 < d) r2++;
        result[11] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
#endif
        p = ((uECC_dword_t)left[uECC_MAX_WORDS-2]) * ((uECC_dword_t)left[uECC_MAX_WORDS-2]); r01 += p; r2 = (r01 < p);
        p = ((uECC_dword_t)left[uECC_MAX_WORDS-3]) * ((uECC_dword_t)left[uECC_MAX_WORDS-1]); r01 += p; if (r01 < p) r2++;
                                                                           r01 += p; if (r01 < p) r2++;
        result[2*uECC_MAX_WORDS-4] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        p = ((uECC_dword_t)left[uECC_MAX_WORDS-2]) * ((uECC_dword_t)left[uECC_MAX_WORDS-1]); r01 += p; r2 = (r01 < p);
                                                                           r01 += p; if (r01 < p) r2++;
        result[2*uECC_MAX_WORDS-3] = (uECC_word_t) r01;
        r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
        p = ((uECC_dword_t)left[uECC_MAX_WORDS-1]) * ((uECC_dword_t)left[uECC_MAX_WORDS-1]); r01 += p;
        result[2*uECC_MAX_WORDS-2] = (uECC_word_t) r01;
        result[2*uECC_MAX_WORDS-1] = (uECC_word_t)(r01 >> uECC_WORD_BITS);
    }
    else
#endif
#endif
    {
#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128
        uECC_word_t r0 = 0;
        uECC_word_t r1 = 0;
#else
        uECC_dword_t r01 = 0;
        uECC_dword_t d = 0;
#endif
        uECC_word_t r2 = 0;
        int i, k;
        
        for (k = 0; k < num_words * 2 - 1; ++k) {
            int min = (k < num_words ? 0 : (k + 1) - num_words);
            for (i = min; 2 * i < k; ++i) {
                uECC_word_t a = left[(unsigned)i];
                uECC_word_t b = left[(unsigned)(k - i)];
#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128
                uint64_t a0 = a & 0xffffffffull;
                uint64_t a1 = a >> 32;
                uint64_t b0 = b & 0xffffffffull;
                uint64_t b1 = b >> 32;

                uint64_t i0 = a0 * b0;
                uint64_t i1 = a0 * b1;
                uint64_t i2 = a1 * b0;
                uint64_t i3 = a1 * b1;

                uint64_t p0, p1;

                i2 += (i0 >> 32);
                i2 += i1;
                if (i2 < i1)
                { /* overflow */
                    i3 += 0x100000000ull;
                }

                p0 = (i0 & 0xffffffffull) | (i2 << 32);
                p1 = i3 + (i2 >> 32);

                r2 += (p1 >> 63);
                p1 = (p1 << 1) | (p0 >> 63);
                p0 <<= 1;

                r0 += p0;
                r1 += (p1 + (r0 < p0));
                if ((r1 < p1) || (r1 == p1 && r0 < p0)) r2++;
#else
                uECC_dword_t p = (uECC_dword_t)a * b;
                d += p;
                if (d < p) r2 += 2;
#endif
            }
            if (2 * i == k) {
                uECC_word_t a = left[(unsigned)i];
#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128
                uint64_t a0 = a & 0xffffffffull;
                uint64_t a1 = a >> 32;

                uint64_t i0 = a0 * a0;
                uint64_t i1 = a0 * a1;
                uint64_t i2 = i1;
                uint64_t i3 = a1 * a1;

                uint64_t p0, p1;

                i2 += (i0 >> 32);
                i2 += i1;
                if (i2 < i1) { /* overflow */
                    i3 += 0x100000000ull;
                }

                p0 = (i0 & 0xffffffffull) | (i2 << 32);
                p1 = i3 + (i2 >> 32);

                r0 += p0;
                r1 += (p1 + (r0 < p0));
                if ((r1 < p1) || (r1 == p1 && r0 < p0)) r2++;
#else
                uECC_dword_t p = (uECC_dword_t)a * a;
                r01 += p;
                if (r01 < p) r2++;
#endif
            }
#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128
            result[(unsigned)k] = r0;
            r0 = r1;
            r1 = r2;
#else
            r01 += d;
            if (r01 < d) r2++;
            r01 += d;
            if (r01 < d) r2++;
            result[(unsigned)k] = (uECC_word_t) r01;
            r01 = (r01 >> uECC_WORD_BITS) | (((uECC_dword_t) r2) << uECC_WORD_BITS);
            d = 0;
#endif
            r2 = 0;
        }
#if uECC_WORD_SIZE == 8 && !SUPPORTS_INT128
        result[num_words * 2 - 1] = r0;
#else
        result[num_words * 2 - 1] = (uECC_word_t) r01;
#endif
    }
}
#define asm_square 1
#endif /* !asm_square */
#endif /* uECC_SQUARE_FUNC */

#endif /* ((uECC_OPTIMIZATION_LEVEL >= 2) && (uECC_MAX_WORDS == 4) || (uECC_MAX_WORDS == 8)) */
#endif /* _UECC_ASM_OTHER_H_ */
