From 21f82da45054e310c8294c44bb071d7317089e74 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Mon, 5 Aug 2013 21:13:48 +0100 Subject: [PATCH] math/mpreduce.c: Add extensive commentary. The behaviour of this code must have been something of a mystery. It's not arbitrary, but it is a little subtle in places. Add a full explanation of the whole thing. --- math/mpreduce.c | 137 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 132 insertions(+), 5 deletions(-) diff --git a/math/mpreduce.c b/math/mpreduce.c index 669f516..b4543a6 100644 --- a/math/mpreduce.c +++ b/math/mpreduce.c @@ -38,6 +38,42 @@ DA_DECL(instr_v, mpreduce_instr); +/*----- Theory ------------------------------------------------------------* + * + * We're given a modulus %$p = 2^n - d$%, where %$d < 2^n$%, and some %$x$%, + * and we want to compute %$x \bmod p$%. We work in base %$2^w$%, for some + * appropriate %$w$%. The important observation is that + * %$d \equiv 2^n \pmod p$%. + * + * Suppose %$x = x' + z 2^k$%, where %$k \ge n$%; then + * %$x \equiv x' + d z 2^{k-n} \pmod p$%. We can use this to trim the + * representation of %$x$%; each time, we reduce %$x$% by a mutliple of + * %$2^{k-n} p$%. We can do this in two passes: firstly by taking whole + * words off the top, and then (if necessary) by trimming the top word. + * Finally, if %$p \le x < 2^n$% then %$0 \le x - p < p$% and we're done. + * + * A common trick, apparently, is to choose %$d$% such that it has a very + * sparse non-adjacent form, and, moreover, that this form is nicely aligned + * with common word sizes. (That is, write %$d = \sum_{0\le ip); DA_DESTROY(&iv); @@ -126,7 +227,30 @@ int mpreduce_create(mpreduce *r, mp *p) #undef INSTR - /* --- Wrap up --- */ + /* --- Wrap up --- * + * + * Store the generated instruction sequence in our context structure. If + * %$p$%'s bit length %$n$% is a multiple of the word size %$w$% then + * there's nothing much else to do here. Otherwise, we have an additional + * job. + * + * The reduction operation has three phases. The first trims entire words + * from the argument, and the instruction sequence constructed above does + * this well; the second phase reduces an integer which has the same number + * of words as %$p$%, but strictly more bits. (The third phase is a single + * conditional subtraction of %$p$%, in case the argument is the same bit + * length as %$p$% but greater; this doesn't concern us here.) To handle + * the second phase, we create another copy of the instruction stream, with + * all of the target shifts adjusted upwards by %$s = n \bmod w$%. + * + * In this case, we are acting on an %$(N - 1)$%-word operand, and so + * (given the remarks above) must check that this is still valid, but a + * moment's reflection shows that this must be fine: the most distant + * target must be the bit %$s$% from the top of the least-significant word; + * but since we shift all of the targets up by %$s$%, this now addresses + * the bottom bit of the next most significant word, and there is no + * underflow. + */ r->in = DA_LEN(&iv); if (!r->in) @@ -272,7 +396,7 @@ mp *mpreduce_do(mpreduce *r, mp *d, mp *x) if (d) MP_DROP(d); MP_DEST(x, MP_LEN(x), x->f); - /* --- Do the reduction --- */ + /* --- Stage one: trim excess words from the most significant end --- */ #ifdef DEBUG _r = MP_NEW; @@ -297,6 +421,9 @@ mp *mpreduce_do(mpreduce *r, mp *d, mp *x) #endif } } + + /* --- Stage two: trim excess bits from the most significant word --- */ + if (r->s) { while (*vl >> r->s) { z = *vl >> r->s; @@ -311,7 +438,7 @@ mp *mpreduce_do(mpreduce *r, mp *d, mp *x) } } - /* --- Finishing touches --- */ + /* --- Stage three: conditional subtraction --- */ MP_SHRINK(x); if (MP_CMP(x, >=, r->p)) -- 2.11.0