diff --git a/include/cryb/mpi.h b/include/cryb/mpi.h index f0012b1..7624a7c 100644 --- a/include/cryb/mpi.h +++ b/include/cryb/mpi.h @@ -52,6 +52,8 @@ const char *cryb_mpi_version(void); #define mpi_dec cryb_mpi_dec #define mpi_dec_abs cryb_mpi_dec_abs #define mpi_destroy cryb_mpi_destroy +#define mpi_div cryb_mpi_div +#define mpi_div_abs cryb_mpi_div_abs #define mpi_eq cryb_mpi_eq #define mpi_eq_abs cryb_mpi_eq_abs #define mpi_eq_abs_u32 cryb_mpi_eq_abs_u32 @@ -106,6 +108,8 @@ int mpi_copy(cryb_mpi *, const cryb_mpi *); int mpi_dec(cryb_mpi *); int mpi_dec_abs(cryb_mpi *); void mpi_destroy(cryb_mpi *); +int mpi_div(cryb_mpi *, cryb_mpi *, const cryb_mpi *, const cryb_mpi *); +int mpi_div_abs(cryb_mpi *, cryb_mpi *, const cryb_mpi *, const cryb_mpi *); int mpi_eq(const cryb_mpi *, const cryb_mpi *); int mpi_eq_abs(const cryb_mpi *, const cryb_mpi *); int mpi_eq_abs_u32(const cryb_mpi *, uint32_t); diff --git a/lib/mpi/Makefile.am b/lib/mpi/Makefile.am index bca426d..6ae83b7 100644 --- a/lib/mpi/Makefile.am +++ b/lib/mpi/Makefile.am @@ -13,6 +13,8 @@ libcryb_mpi_la_SOURCES = \ cryb_mpi_dec.c \ cryb_mpi_dec_abs.c \ cryb_mpi_destroy.c \ + cryb_mpi_div.c \ + cryb_mpi_div_abs.c \ cryb_mpi_eq.c \ cryb_mpi_eq_abs.c \ cryb_mpi_eq_i32.c \ diff --git a/lib/mpi/cryb_mpi_div.c b/lib/mpi/cryb_mpi_div.c new file mode 100644 index 0000000..702c992 --- /dev/null +++ b/lib/mpi/cryb_mpi_div.c @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017 Dag-Erling Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cryb/impl.h" + +#include +#include + +#include + +#include "cryb_mpi_impl.h" + +/* + * Store the quotient and remainder of A divided by B in Q and R. + */ +int +mpi_div(cryb_mpi *Q, cryb_mpi *R, const cryb_mpi *A, const cryb_mpi *B) +{ + int neg; + + neg = A->neg ^ B->neg; + if (mpi_div_abs(Q, R, A, B) < 0) + return (-1); + if (Q->msb > 0) + Q->neg = neg; + return (0); +} + diff --git a/lib/mpi/cryb_mpi_div_abs.c b/lib/mpi/cryb_mpi_div_abs.c new file mode 100644 index 0000000..ca02c90 --- /dev/null +++ b/lib/mpi/cryb_mpi_div_abs.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2017 Dag-Erling Smørgrav + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "cryb/impl.h" + +#include +#include +#include +#include + +#include +#include + +#include "cryb_mpi_impl.h" + +/* + * Store the quotient and remainder of A divided by B in Q and R. + * + * Assumes Q != R, but any combination of {Q,R} == {A,B} is fine. Either + * Q or R or both may be NULL, althought the latter is pointless. + */ +int +mpi_div_abs(cryb_mpi *Q, cryb_mpi *R, const cryb_mpi *A, const cryb_mpi *B) +{ + cryb_mpi AA = CRYB_MPI_ZERO, QQ = CRYB_MPI_ZERO; + int cmp; + + /* trivial cases */ + if (B->msb == 0) { + /* divide something by zero */ + errno = EINVAL; + return (-1); + } else if (A->msb == 0) { + /* divide zero by something */ + if (Q != NULL) + mpi_zero(Q); + if (R != NULL) + mpi_zero(R); + return (0); + } else if (B->msb == 1) { + /* divide something by one */ + if (Q != A && Q != NULL && mpi_copy(Q, A) != 0) + return (-1); + if (R != NULL) + mpi_zero(R); + return (0); + } else if ((cmp = mpi_cmp_abs(A, B)) == 0) { + /* divide something by itself */ + if (Q != NULL) + mpi_set(Q, 1); + if (R != NULL) + mpi_zero(R); + return (0); + } else if (cmp < 0) { + /* divide something by something larger */ + if (Q != NULL) + mpi_zero(Q); + if (R != A && R != NULL && mpi_copy(R, A) != 0) + return (-1); + return (0); + } + + /* division is destructive, so we work on copies */ + if (mpi_copy(&AA, A) != 0) + return (-1); + mpi_zero(&QQ); + + /* + * Repeatedly subtract B from A until they are the same length, + * then one last time if A is still greater than B. + */ + while (AA.msb > B->msb) { + mpi_sub_abs(&AA, &AA, B); + mpi_inc_abs(&QQ); + } + if (mpi_cmp_abs(&AA, B) >= 0) { + mpi_sub_abs(&AA, &AA, B); + mpi_inc_abs(&QQ); + } + + /* store result where requested and clean up */ + if (Q != NULL) + mpi_swap(Q, &QQ); + if (R != NULL) + mpi_swap(R, &AA); + mpi_destroy(&AA); + mpi_destroy(&QQ); + return (0); +} diff --git a/t/t_mpi_muldiv.c b/t/t_mpi_muldiv.c index dcbad57..764a313 100644 --- a/t/t_mpi_muldiv.c +++ b/t/t_mpi_muldiv.c @@ -145,42 +145,70 @@ static struct t_div_case { uint8_t b[16]; size_t bmsb; int bneg:1; - uint8_t e[16]; - size_t emsb; - int eneg:1; + uint8_t q[16]; + size_t qmsb; + int qneg:1; + uint8_t r[16]; + size_t rmsb; + int rneg:1; } t_div_cases[] = { + { + "0 / 1 == 0", + { }, 0, 0, + { 0x01 }, 1, 0, + { }, 0, 0, + { }, 0, 0, + }, { "1 / 1 == 1", { 0x01 }, 1, 0, { 0x01 }, 1, 0, { 0x01 }, 1, 0, + { }, 0, 0, }, + { + "4 / 2 == 2", + { 0x04 }, 3, 0, + { 0x02 }, 2, 0, + { 0x02 }, 2, 0, + { }, 0, 0, + }, + { + "257 / 127 == 2 rem 3", + { 0x01, 0x01 }, 9, 0, + { 0x7f }, 7, 0, + { 0x02 }, 2, 0, + { 0x03 }, 2, 0, + }, + /* XXX needs lots more tests! */ }; static int t_mpi_div_tc(char **desc CRYB_UNUSED, void *arg) { struct t_div_case *tc = arg; - cryb_mpi a = CRYB_MPI_ZERO, b = CRYB_MPI_ZERO, e = CRYB_MPI_ZERO; - cryb_mpi x = CRYB_MPI_ZERO; + cryb_mpi a = CRYB_MPI_ZERO, b = CRYB_MPI_ZERO; + cryb_mpi q = CRYB_MPI_ZERO, r = CRYB_MPI_ZERO; + cryb_mpi eq = CRYB_MPI_ZERO, er = CRYB_MPI_ZERO; int ret = 1; mpi_load(&a, tc->a, (tc->amsb + 7) / 8); a.neg = tc->aneg; mpi_load(&b, tc->b, (tc->bmsb + 7) / 8); b.neg = tc->bneg; - mpi_load(&e, tc->e, (tc->emsb + 7) / 8); - e.neg = tc->eneg; -#if 0 - ret &= t_compare_i(0, mpi_div_abs(&x, &a, &b)); -#else - mpi_load(&x, tc->e, (tc->emsb + 7) / 8); -#endif - ret &= t_compare_mpi(&e, &x); + mpi_load(&eq, tc->q, (tc->qmsb + 7) / 8); + eq.neg = tc->qneg; + mpi_load(&er, tc->r, (tc->rmsb + 7) / 8); + er.neg = tc->rneg; + ret &= t_compare_i(0, mpi_div_abs(&q, &r, &a, &b)); + ret &= t_compare_mpi(&eq, &q); + ret &= t_compare_mpi(&er, &r); mpi_destroy(&a); mpi_destroy(&b); - mpi_destroy(&e); - mpi_destroy(&x); + mpi_destroy(&q); + mpi_destroy(&r); + mpi_destroy(&eq); + mpi_destroy(&er); return (ret); }