1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System.Diagnostics;
6 
7 namespace System.Numerics
8 {
9     internal static partial class BigIntegerCalculator
10     {
11         // Executes different exponentiation algorithms, which are
12         // based on the classic square-and-multiply method.
13 
14         // https://en.wikipedia.org/wiki/Exponentiation_by_squaring
15 
Pow(uint value, uint power)16         public static uint[] Pow(uint value, uint power)
17         {
18             // The basic pow method for a 32-bit integer.
19             // To spare memory allocations we first roughly
20             // estimate an upper bound for our buffers.
21 
22             int size = PowBound(power, 1, 1);
23             BitsBuffer v = new BitsBuffer(size, value);
24             return PowCore(power, ref v);
25         }
26 
Pow(uint[] value, uint power)27         public static uint[] Pow(uint[] value, uint power)
28         {
29             Debug.Assert(value != null);
30 
31             // The basic pow method for a big integer.
32             // To spare memory allocations we first roughly
33             // estimate an upper bound for our buffers.
34 
35             int size = PowBound(power, value.Length, 1);
36             BitsBuffer v = new BitsBuffer(size, value);
37             return PowCore(power, ref v);
38         }
39 
PowCore(uint power, ref BitsBuffer value)40         private static uint[] PowCore(uint power, ref BitsBuffer value)
41         {
42             // Executes the basic pow algorithm.
43 
44             int size = value.GetSize();
45 
46             BitsBuffer temp = new BitsBuffer(size, 0);
47             BitsBuffer result = new BitsBuffer(size, 1);
48 
49             PowCore(power, ref value, ref result, ref temp);
50 
51             return result.GetBits();
52         }
53 
PowBound(uint power, int valueLength, int resultLength)54         private static int PowBound(uint power, int valueLength,
55                                     int resultLength)
56         {
57             // The basic pow algorithm, but instead of squaring
58             // and multiplying we just sum up the lengths.
59 
60             while (power != 0)
61             {
62                 checked
63                 {
64                     if ((power & 1) == 1)
65                         resultLength += valueLength;
66                     if (power != 1)
67                         valueLength += valueLength;
68                 }
69                 power = power >> 1;
70             }
71 
72             return resultLength;
73         }
74 
PowCore(uint power, ref BitsBuffer value, ref BitsBuffer result, ref BitsBuffer temp)75         private static void PowCore(uint power, ref BitsBuffer value,
76                                     ref BitsBuffer result, ref BitsBuffer temp)
77         {
78             // The basic pow algorithm using square-and-multiply.
79 
80             while (power != 0)
81             {
82                 if ((power & 1) == 1)
83                     result.MultiplySelf(ref value, ref temp);
84                 if (power != 1)
85                     value.SquareSelf(ref temp);
86                 power = power >> 1;
87             }
88         }
89 
Pow(uint value, uint power, uint modulus)90         public static uint Pow(uint value, uint power, uint modulus)
91         {
92             // The 32-bit modulus pow method for a 32-bit integer
93             // raised by a 32-bit integer...
94 
95             return PowCore(power, modulus, value, 1);
96         }
97 
Pow(uint[] value, uint power, uint modulus)98         public static uint Pow(uint[] value, uint power, uint modulus)
99         {
100             Debug.Assert(value != null);
101 
102             // The 32-bit modulus pow method for a big integer
103             // raised by a 32-bit integer...
104 
105             uint v = Remainder(value, modulus);
106             return PowCore(power, modulus, v, 1);
107         }
108 
Pow(uint value, uint[] power, uint modulus)109         public static uint Pow(uint value, uint[] power, uint modulus)
110         {
111             Debug.Assert(power != null);
112 
113             // The 32-bit modulus pow method for a 32-bit integer
114             // raised by a big integer...
115 
116             return PowCore(power, modulus, value, 1);
117         }
118 
Pow(uint[] value, uint[] power, uint modulus)119         public static uint Pow(uint[] value, uint[] power, uint modulus)
120         {
121             Debug.Assert(value != null);
122             Debug.Assert(power != null);
123 
124             // The 32-bit modulus pow method for a big integer
125             // raised by a big integer...
126 
127             uint v = Remainder(value, modulus);
128             return PowCore(power, modulus, v, 1);
129         }
130 
PowCore(uint[] power, uint modulus, ulong value, ulong result)131         private static uint PowCore(uint[] power, uint modulus,
132                                     ulong value, ulong result)
133         {
134             // The 32-bit modulus pow algorithm for all but
135             // the last power limb using square-and-multiply.
136 
137             for (int i = 0; i < power.Length - 1; i++)
138             {
139                 uint p = power[i];
140                 for (int j = 0; j < 32; j++)
141                 {
142                     if ((p & 1) == 1)
143                         result = (result * value) % modulus;
144                     value = (value * value) % modulus;
145                     p = p >> 1;
146                 }
147             }
148 
149             return PowCore(power[power.Length - 1], modulus, value, result);
150         }
151 
PowCore(uint power, uint modulus, ulong value, ulong result)152         private static uint PowCore(uint power, uint modulus,
153                                     ulong value, ulong result)
154         {
155             // The 32-bit modulus pow algorithm for the last or
156             // the only power limb using square-and-multiply.
157 
158             while (power != 0)
159             {
160                 if ((power & 1) == 1)
161                     result = (result * value) % modulus;
162                 if (power != 1)
163                     value = (value * value) % modulus;
164                 power = power >> 1;
165             }
166 
167             return (uint)(result % modulus);
168         }
169 
Pow(uint value, uint power, uint[] modulus)170         public static uint[] Pow(uint value, uint power, uint[] modulus)
171         {
172             Debug.Assert(modulus != null);
173 
174             // The big modulus pow method for a 32-bit integer
175             // raised by a 32-bit integer...
176 
177             int size = modulus.Length + modulus.Length;
178             BitsBuffer v = new BitsBuffer(size, value);
179             return PowCore(power, modulus, ref v);
180         }
181 
Pow(uint[] value, uint power, uint[] modulus)182         public static uint[] Pow(uint[] value, uint power, uint[] modulus)
183         {
184             Debug.Assert(value != null);
185             Debug.Assert(modulus != null);
186 
187             // The big modulus pow method for a big integer
188             // raised by a 32-bit integer...
189 
190             if (value.Length > modulus.Length)
191                 value = Remainder(value, modulus);
192 
193             int size = modulus.Length + modulus.Length;
194             BitsBuffer v = new BitsBuffer(size, value);
195             return PowCore(power, modulus, ref v);
196         }
197 
Pow(uint value, uint[] power, uint[] modulus)198         public static uint[] Pow(uint value, uint[] power, uint[] modulus)
199         {
200             Debug.Assert(power != null);
201             Debug.Assert(modulus != null);
202 
203             // The big modulus pow method for a 32-bit integer
204             // raised by a big integer...
205 
206             int size = modulus.Length + modulus.Length;
207             BitsBuffer v = new BitsBuffer(size, value);
208             return PowCore(power, modulus, ref v);
209         }
210 
Pow(uint[] value, uint[] power, uint[] modulus)211         public static uint[] Pow(uint[] value, uint[] power, uint[] modulus)
212         {
213             Debug.Assert(value != null);
214             Debug.Assert(power != null);
215             Debug.Assert(modulus != null);
216 
217             // The big modulus pow method for a big integer
218             // raised by a big integer...
219 
220             if (value.Length > modulus.Length)
221                 value = Remainder(value, modulus);
222 
223             int size = modulus.Length + modulus.Length;
224             BitsBuffer v = new BitsBuffer(size, value);
225             return PowCore(power, modulus, ref v);
226         }
227 
228         // Mutable for unit testing...
229         private static int ReducerThreshold = 32;
230 
PowCore(uint[] power, uint[] modulus, ref BitsBuffer value)231         private static uint[] PowCore(uint[] power, uint[] modulus,
232                                       ref BitsBuffer value)
233         {
234             // Executes the big pow algorithm.
235 
236             int size = value.GetSize();
237 
238             BitsBuffer temp = new BitsBuffer(size, 0);
239             BitsBuffer result = new BitsBuffer(size, 1);
240 
241             if (modulus.Length < ReducerThreshold)
242             {
243                 PowCore(power, modulus, ref value, ref result, ref temp);
244             }
245             else
246             {
247                 FastReducer reducer = new FastReducer(modulus);
248                 PowCore(power, ref reducer, ref value, ref result, ref temp);
249             }
250 
251             return result.GetBits();
252         }
253 
PowCore(uint power, uint[] modulus, ref BitsBuffer value)254         private static uint[] PowCore(uint power, uint[] modulus,
255                                       ref BitsBuffer value)
256         {
257             // Executes the big pow algorithm.
258 
259             int size = value.GetSize();
260 
261             BitsBuffer temp = new BitsBuffer(size, 0);
262             BitsBuffer result = new BitsBuffer(size, 1);
263 
264             if (modulus.Length < ReducerThreshold)
265             {
266                 PowCore(power, modulus, ref value, ref result, ref temp);
267             }
268             else
269             {
270                 FastReducer reducer = new FastReducer(modulus);
271                 PowCore(power, ref reducer, ref value, ref result, ref temp);
272             }
273 
274             return result.GetBits();
275         }
276 
PowCore(uint[] power, uint[] modulus, ref BitsBuffer value, ref BitsBuffer result, ref BitsBuffer temp)277         private static void PowCore(uint[] power, uint[] modulus,
278                                     ref BitsBuffer value, ref BitsBuffer result,
279                                     ref BitsBuffer temp)
280         {
281             // The big modulus pow algorithm for all but
282             // the last power limb using square-and-multiply.
283 
284             // NOTE: we're using an ordinary remainder here,
285             // since the reducer overhead doesn't pay off.
286 
287             for (int i = 0; i < power.Length - 1; i++)
288             {
289                 uint p = power[i];
290                 for (int j = 0; j < 32; j++)
291                 {
292                     if ((p & 1) == 1)
293                     {
294                         result.MultiplySelf(ref value, ref temp);
295                         result.Reduce(modulus);
296                     }
297                     value.SquareSelf(ref temp);
298                     value.Reduce(modulus);
299                     p = p >> 1;
300                 }
301             }
302 
303             PowCore(power[power.Length - 1], modulus, ref value, ref result,
304                 ref temp);
305         }
306 
PowCore(uint power, uint[] modulus, ref BitsBuffer value, ref BitsBuffer result, ref BitsBuffer temp)307         private static void PowCore(uint power, uint[] modulus,
308                                     ref BitsBuffer value, ref BitsBuffer result,
309                                     ref BitsBuffer temp)
310         {
311             // The big modulus pow algorithm for the last or
312             // the only power limb using square-and-multiply.
313 
314             // NOTE: we're using an ordinary remainder here,
315             // since the reducer overhead doesn't pay off.
316 
317             while (power != 0)
318             {
319                 if ((power & 1) == 1)
320                 {
321                     result.MultiplySelf(ref value, ref temp);
322                     result.Reduce(modulus);
323                 }
324                 if (power != 1)
325                 {
326                     value.SquareSelf(ref temp);
327                     value.Reduce(modulus);
328                 }
329                 power = power >> 1;
330             }
331         }
332 
PowCore(uint[] power, ref FastReducer reducer, ref BitsBuffer value, ref BitsBuffer result, ref BitsBuffer temp)333         private static void PowCore(uint[] power, ref FastReducer reducer,
334                                     ref BitsBuffer value, ref BitsBuffer result,
335                                     ref BitsBuffer temp)
336         {
337             // The big modulus pow algorithm for all but
338             // the last power limb using square-and-multiply.
339 
340             // NOTE: we're using a special reducer here,
341             // since it's additional overhead does pay off.
342 
343             for (int i = 0; i < power.Length - 1; i++)
344             {
345                 uint p = power[i];
346                 for (int j = 0; j < 32; j++)
347                 {
348                     if ((p & 1) == 1)
349                     {
350                         result.MultiplySelf(ref value, ref temp);
351                         result.Reduce(ref reducer);
352                     }
353                     value.SquareSelf(ref temp);
354                     value.Reduce(ref reducer);
355                     p = p >> 1;
356                 }
357             }
358 
359             PowCore(power[power.Length - 1], ref reducer, ref value, ref result,
360                 ref temp);
361         }
362 
PowCore(uint power, ref FastReducer reducer, ref BitsBuffer value, ref BitsBuffer result, ref BitsBuffer temp)363         private static void PowCore(uint power, ref FastReducer reducer,
364                                     ref BitsBuffer value, ref BitsBuffer result,
365                                     ref BitsBuffer temp)
366         {
367             // The big modulus pow algorithm for the last or
368             // the only power limb using square-and-multiply.
369 
370             // NOTE: we're using a special reducer here,
371             // since it's additional overhead does pay off.
372 
373             while (power != 0)
374             {
375                 if ((power & 1) == 1)
376                 {
377                     result.MultiplySelf(ref value, ref temp);
378                     result.Reduce(ref reducer);
379                 }
380                 if (power != 1)
381                 {
382                     value.SquareSelf(ref temp);
383                     value.Reduce(ref reducer);
384                 }
385                 power = power >> 1;
386             }
387         }
388 
ActualLength(uint[] value)389         private static int ActualLength(uint[] value)
390         {
391             // Since we're reusing memory here, the actual length
392             // of a given value may be less then the array's length
393 
394             return ActualLength(value, value.Length);
395         }
396 
ActualLength(uint[] value, int length)397         private static int ActualLength(uint[] value, int length)
398         {
399             Debug.Assert(value != null);
400             Debug.Assert(length <= value.Length);
401 
402             while (length > 0 && value[length - 1] == 0)
403                 --length;
404             return length;
405         }
406     }
407 }
408