Why is 10^9942066 the biggest power I can calculate without overflows? - ruby

In ruby, some large numbers are larger than infinity. Through binary search, I discovered:
(1.0/0) > 10**9942066.000000001 # => false
(1.0/0) > 10**9942066 # => true
RUBY_VERSION # => "2.3.0"
Why is this? What is special about 109942066? It doesn't seem to be an arbitrary number like 9999999, it is not close to any power of two (it's approximately equivelent to 233026828.36662442).
Why isn't ruby's infinity infinite? How is 109942066 involved?
I now realize, any number greater than 109942066 will overflow to infinity:
10**9942066.000000001 #=> Infinity
10**9942067 #=> Infinity
But that still leaves the question: Why 109942066?

TL;DR
I did the calculations done inside numeric.c's int_pow manually, checking where an integer overflow (and a propagation to Bignum's, including a call to rb_big_pow) occurs. Once the call to rb_big_pow happens there is a check whether the two intermediate values you've got in int_pow are too large or not, and the cutoff value seems to be just around 9942066 (if you're using a base of 10 for the power). Approximately this value is close to
BIGLEN_LIMIT / ceil(log2(base^n)) * n ==
32*1024*1024 / ceil(log2(10^16)) * 16 ==
32*1024*1024 / 54 * 16 ~=
9942054
where BIGLEN_LIMIT is an internal limit in ruby which is used as a constant to check if a power calculation would be too big or not, and is defined as 32*1024*1024. base is 10, and n is the largest power-of-2 exponent for the base that would still fit inside a Fixnum.
Unfortunately I can't find a better way than this approximation, due to the algorithm used to calculate powers of big numbers, but it might be good enough to use as an upper limit if your code needs to check validity before doing exponentiation on big numbers.
Original question:
The problem is not with 9942066, but that with one of your number being an integer, the other one being a float. So
(10**9942066).class # => Bignum
(10**9942066.00000001).class # => Float
The first one is representable by a specific number internally, which is smaller than Infinity. The second one, as it's still a float is not representable by an actual number, and is simply replaced by Infinity, which is of course not larger than Infinity.
Updated question:
You are right that there seem to be some difference around 9942066 (if you're using a 64-bit ruby under Linux, as the limits might be different under other systems). While ruby does use the GMP library to handle big numbers, it does some precheck before even going to GMP, as shown by the warnings you can receive. It will also do the exponentiation manually using GMP's mul commands, without calling GMP's pow functions.
Fortunately the warnings are easy to catch:
irb(main):010:0> (10**9942066).class
=> Bignum
irb(main):005:0> (10**9942067).class
(irb):5: warning: in a**b, b may be too big
=> Float
And then you can actually check where these warnings are emitted inside ruby's bignum.c library.
But first we need to get to the Bignum realm, as both of our numbers are simple Fixnums. The initial part of the calculation, and the "upgrade" from fixnum to bignum is done inside numeric.c. Ruby does quick exponentiation, and at every step it checks whether the result would still fit into a Fixnum (which is 2 bits less than the system bitsize: 62 bits on a 64 bit machine). If not, it will then convert the values to the Bignum realm, and continues the calculations there. We are interested at the point where this conversion happens, so let's try to figure out when it does in our 10^9942066 example (I'm using x,y,z variables as present inside the ruby's numeric.c code):
x = 10^1 z = 10^0 y = 9942066
x = 10^2 z = 10^0 y = 4971033
x = 10^2 z = 10^2 y = 4971032
x = 10^4 z = 10^2 y = 2485516
x = 10^8 z = 10^2 y = 1242758
x = 10^16 z = 10^2 y = 621379
x = 10^16 z = 10^18 y = 621378
x = OWFL
At this point x will overflow (10^32 > 2^62-1), so the process will continue on the Bignum realm by calculating x**y, which is (10^16)^621378 (which are actually still both Fixnums at this stage)
If you now go back to bignum.c and check how it determines if a number is too large or not, you can see that it will check the number of bits required to hold x, and multiply this number with y. If the result is larger than 32*1024*1024, it will then fail (emit a warning and does the calculations using basic floats).
(10^16) is 54 bits (ceil(log_2(10^16)) == 54), 54*621378 is 33554412. This is only slightly smaller than 33554432 (by 20), the limit after which ruby will not do Bignum exponentiation, but simply convert y to double, and hope for the best (which will obviously fail, and just return Infinity)
Now let's try to check this with 9942067:
x = 10^1 z = 10^0 y = 9942067
x = 10^1 z = 10^1 y = 9942066
x = 10^2 z = 10^1 y = 4971033
x = 10^2 z = 10^3 y = 4971032
x = 10^4 z = 10^3 y = 2485516
x = 10^8 z = 10^3 y = 1242758
x = 10^16 z = 10^3 y = 621379
x = 10^16 z = OWFL
Here, at the point z overflows (10^19 > 2^62-1), the calculation will continue on the Bignum realm, and will calculate x**y. Note that here it will calculate (10^16)^621379, and while (10^16) is still 54 bits, 54*621379 is 33554466, which is larger than 33554432 (by 34). As it's larger you'll get the warning, and ruby will only to calculations using double, hence the result is Infinity.
Note that these checks are only done if you are using the power function. That's why you can still do (10**9942066)*10, as similar checks are not present when doing plain multiplication, meaning you could implement your own quick exponentiation method in ruby, in which case it will still work with larger values, although you won't have this safety check anymore. See for example this quick implementation:
def unbounded_pow(x,n)
if n < 0
x = 1.0 / x
n = -n
end
return 1 if n == 0
y = 1
while n > 1
if n.even?
x = x*x
n = n/2
else
y = x*y
x = x*x
n = (n-1)/2
end
end
x*y
end
puts (10**9942066) == (unbounded_pow(10,9942066)) # => true
puts (10**9942067) == (unbounded_pow(10,9942067)) # => false
puts ((10**9942066)*10) == (unbounded_pow(10,9942067)) # => true
But how would I know the cutoff for a specific base?
My math is not exactly great, but I can tell a way to approximate where the cutoff value will be. If you check the above calls you can see the conversion between Fixnum and Bignum happens when the intermediate base reaches the limit of Fixnum. The intermediate base at this stage will always have an exponent which is a power of 2, so you just have to maximize this value. For example let's try to figure out the maximum cutoff value for 12.
First we have to check what is the highest base we can store in a Fixnum:
ceil(log2(12^1)) = 4
ceil(log2(12^2)) = 8
ceil(log2(12^4)) = 15
ceil(log2(12^8)) = 29
ceil(log2(12^16)) = 58
ceil(log2(12^32)) = 115
We can see 12^16 is the max we can store in 62 bits, or if we're using a 32 bit machine 12^8 will fit into 30 bits (ruby's Fixnums can store values up to two bits less than the machine size limit).
For 12^16 we can easily determine the cutoff value. It will be 32*1024*1024 / ceil(log2(12^16)), which is 33554432 / 58 ~= 578525. We can easily check this in ruby now:
irb(main):004:0> ((12**16)**578525).class
=> Bignum
irb(main):005:0> ((12**16)**578526).class
(irb):5: warning: in a**b, b may be too big
=> Float
Now we hate to go back to our original base of 12. There the cutoff will be around 578525*16 (16 being the exponent of the new base), which is 9256400. If you check in ruby, the values are actually quite close to this number:
irb(main):009:0> (12**9256401).class
=> Bignum
irb(main):010:0> (12**9256402).class
(irb):10: warning: in a**b, b may be too big
=> Float

Note that the problem is not with the number but with the operation, as told by the warning you get.
$ ruby -e 'puts (1.0/0) > 10**9942067'
-e:1: warning: in a**b, b may be too big
false
The problem is 10**9942067 breaks Ruby's power function. Instead of throwing an exception, which would be a better behavior, it erroneously results in infinity.
$ ruby -e 'puts 10**9942067'
-e:1: warning: in a**b, b may be too big
Infinity
The other answer explains why this happens near 10e9942067.
10**9942067 is not greater than infinity, it is erroneously resulting in infinity. This is a bad habit of a lot of math libraries that makes mathematicians claw their eyeballs out in frustration.
Infinity is not greater than infinity, they're equal, so your greater than check is false. You can see this by checking if they're equal.
$ ruby -e 'puts (1.0/0) == 10**9942067'
-e:1: warning: in a**b, b may be too big
true
Contrast this with specifying the number directly using scientific notation. Now Ruby doesn't have to do math on huge numbers, it just knows that any real number is less than infinity.
$ ruby -e 'puts (1.0/0) > 10e9942067'
false
Now you can put on as big an exponent as you like.
$ ruby -e 'puts (1.0/0) > 10e994206700000000000000000000000000000000'
false

Related

How to compute and store the digits of sqrt(n) up to 10^6 decimal places?

I am doing research work. for which I need to compute and store the square root of 2 up to 10^6 places. I have googled for this but I got only a NASA page but how they computed that I don't know. I used set_precision of c++. but that is giving the result up to around 50 places only.what should I do?
NASA page link: https://apod.nasa.gov/htmltest/gifcity/sqrt2.1mil
I have tried binary search also but not fruitful.
long double ans = sqrt(n);
cout<<fixed<<setprecision(50)<<ans<<endl;
You have various options here. You can work with an arbitrary-precision floating-point library (for example MPFR with C or C++, or mpmath or the built-in decimal library in Python). Provided you know what error guarantees that library gives, you can ensure that you get the correct decimal digits. For example, both MPFR and Python's decimal guarantee correct rounding here, but MPFR has the disadvantage (for your particular use-case of getting decimal digits) that it works in binary, so you'd also need to analyse the error induced by the binary-to-decimal conversion.
You can also work with pure integer methods, using an arbitrary-precision integer library (like GMP), or a language that supports arbitrary-precision integers out of the box (for example, Java with its BigInteger class: recent versions of Java provide a BigInteger.sqrt method): scale 2 by 10**2n, where n is the number of places after the decimal point that you need, take the integer square root (i.e., the integer part of the exact mathematical square root), and then scale back by 10**n. See below for a relatively simple but efficient algorithm for computing integer square roots.
The simplest out-of-the-box option here, if you're willing to use another language, is to use Python's decimal library. Here's all the code you need, assuming Python 3 (not Python 2, where this will be horribly slow).
>>> from decimal import Decimal, getcontext
>>> getcontext().prec = 10**6 + 1 # number of significant digits needed
>>> sqrt2_digits = str(Decimal(2).sqrt())
The str(Decimal(2).sqrt()) operation takes less than 10 seconds on my machine. Let's check the length, and the first and last hundred digits (we obviously can't reproduce the whole output here):
>>> len(sqrt2_digits)
1000002
>>> sqrt2_digits[:100]
'1.41421356237309504880168872420969807856967187537694807317667973799073247846210703885038753432764157'
>>> sqrt2_digits[-100:]
'2637136344700072631923515210207475200984587509349804012374947972946621229489938420441930169048412044'
There's a slight problem with this: the result is guaranteed to be correctly rounded, but that's rounded, not truncated. So that means that that final "4" digit could be the result of a final round up - that is, the actual digit in that position could be a "3", with an "8" or "9" (for example) following it.
We can get around this by computing a couple of extra digits, and then truncating them (after double checking that rounding of those extra digits doesn't affect the truncation).
>>> getcontext().prec = 10**6 + 3
>>> sqrt2_digits = str(Decimal(2).sqrt())
>>> sqrt2_digits[-102:]
'263713634470007263192351521020747520098458750934980401237494797294662122948993842044193016904841204391'
So indeed the millionth digit after the decimal point is a 3, not a 4. Note that if the last 3 digits computed above had been "400", we still wouldn't have known whether the millionth digit was a "3" or a "4", since that "400" could again be the result of a round up. In that case, you could compute another two digits and try again, and so on, stopping when you have an unambiguous output. (For further reading, search for "The table maker's dilemma".)
(Note that setting the decimal module's rounding mode to ROUND_DOWN does not work here, since the Decimal.sqrt method ignores the rounding mode.)
If you want to do this using pure integer arithmetic, Python 3.8 offers a math.isqrt function for computing exact integer square roots. In this case, we'd use it as follows:
>>> from math import isqrt
>>> sqrt2_digits = str(isqrt(2*10**(2*10**6)))
This takes a little longer: around 20 seconds on my laptop. Half of that time is for the binary-to-decimal conversion implicit in the str call. But this time, we got the truncated result directly, and didn't have to worry about the possibility of rounding giving us the wrong final digit(s).
Examining the results again:
>>> len(sqrt2_digits)
1000001
>>> sqrt2_digits[:100]
'1414213562373095048801688724209698078569671875376948073176679737990732478462107038850387534327641572'
>>> sqrt2_digits[-100:]
'2637136344700072631923515210207475200984587509349804012374947972946621229489938420441930169048412043'
This is a bit of a cheat, because (at the time of writing) Python 3.8 hasn't been released yet, although beta versions are available. But there's a pure Python version of the isqrt algorithm in the CPython source, that you can copy and paste and use directly. Here it is in full:
import operator
def isqrt(n):
"""
Return the integer part of the square root of the input.
"""
n = operator.index(n)
if n < 0:
raise ValueError("isqrt() argument must be nonnegative")
if n == 0:
return 0
c = (n.bit_length() - 1) // 2
a = 1
d = 0
for s in reversed(range(c.bit_length())):
# Loop invariant: (a-1)**2 < (n >> 2*(c - d)) < (a+1)**2
e = d
d = c >> s
a = (a << d - e - 1) + (n >> 2*c - e - d + 1) // a
return a - (a*a > n)
The source also contains an explanation of the above algorithm and an informal proof of its correctness.
You can check that the results by the two methods above agree (modulo the extra decimal point in the first result). They're computed by completely different methods, so that acts as a sanity check on both methods.
You could use big integers, e.g. BigInteger in Java. Then you calculate the square root of 2e12 or 2e14. Note that sqrt(2) = 1.4142... and sqrt(200) = 14.142... Then you can use the Babylonian method to get all the digits: E.g. S = 10^14. x(n+1) = (x(n) + S / x(n)) / 2. Repeat until x(n) doesn't change. Maybe there are more efficient algorithms that converge faster.
// Input: a positive integer, the number of precise digits after the decimal point
// Output: a string representing the long float square root
function findSquareRoot(number, numDigits) {
function get_power(x, y) {
let result = 1n;
for (let i = 0; i < y; i ++) {
result = result * BigInt(x);
}
return result;
}
let a = 5n * BigInt(number);
let b = 5n;
const precision_digits = get_power(10, numDigits + 1);
while (b < precision_digits) {
if (a >= b) {
a = a - b;
b = b + 10n;
} else {
a = a * 100n;
b = (b / 10n) * 100n + 5n;
}
}
let decimal_pos = Math.floor(Math.log10(number))
if (decimal_pos == 0) decimal_pos = 1
let result = (b / 100n).toString()
result = result.slice(0, decimal_pos) + '.' + result.slice(decimal_pos)
return result
}

Can we write an algorithm which gives me two whole numbers X and Y when I want to get a desired fraction F such that F= X/Y?

I am working to prepare a test data set in which I have to check rounding. Suppose I want to check round, round_up and round_down is working correctly at 10 th decimal place or not.
Then
if, X=100 and Y = 54 so, X/Y = 1.8518518518518518518518518518519 (test round equidistant)
if, X= 10 and Y = 7 so, 1.4285714285714285714285714285714 (test round_up)
if, X= 10 and Y = 3 so, 3.3333333333333333333333333333333 (test round_down)
Can we write an algorithm in which
input will be rounding mode (round_up, round, round_down) and decimal place I want to round at(in our example 10)
output will be X and Y like above?
If the required location is p (=10 in your example), then y=10^p and then you can choose any x you want.
Depending on the language you are using, p might be too big for you to do 10^p, so in the worst case just divide the result from x/y by 10, 100 or whatever is necessary.
Or you can do like this
# n = number of fraction you want to return
def getFraction(a, b, n):
result = ""
for i in range(n):
f = int((a % b) * 10 / b)
result += str(f)
a = a * 10 - b * f
return result
getFraction(10, 7, 11) # return 42857142857 which 10/7 = 1.42857142857...
What I do is like what you have learnt in elementary school on how to do division by pen and paper.
Actually, if the required digit is d, then if d is not 9, the answer would be x=d,y=9 regardless of p which is the position of the digit. If d is 9, then if p is odd, the answer is x=10,y=11 and if p is even, x=1,y=11. If a trivial answer for d=0 won't do, the mirror answer for d=9 is suitable, that is, if d=0 and p is odd, the answer is x=1,y=11, and if p is even, x=10,y=11. A lot shorter than an answer with y=10^p and certainly fitting in nearly any architecture.

multiplying floating point numbers produces zero

the code below outputs 0.0. is this because of the overflow? how to avoid it? if not, why?
p ((1..100000).map {rand}).reduce :*
I was hoping to speed up this code:
p r.reduce(0) {|m, v| m + (Math.log10 v)}
and use this instead:
p Math.log10 (r.reduce :*)
but apparently this is not always possible...
The values produced by rand are all between 0.0 and 1.0. This means that on each multiplication, your number gets smaller. So by the time you have multiplied 1000 of them, it is probably indistinguishable from 0.
At some point, ruby will take your number to be so small that it is 0. for instance: 2.0e-1000 # => 0
Every multiplication reduces your number by about 1/21, so after about 50 of them, you are down 1/250, and after 100000 (actually, after about 700) you have underflowed the FP format itself, see here.
Ruby provides the BigDecimal class, which implements accurate floating point arithmetic.
require 'bigdecimal'
n = 100
decimals = n.times.map { BigDecimal.new rand.to_s }
result = decimals.reduce :*
result.nonzero?.nil? # returns nil if zero, self otherwise
# => false
result.precs # [significant_digits, maximum_significant_digits]
# => [1575, 1764]
Math.log10 result
# => -46.8031931083014
It is a lot slower than native floating point numbers, however. With n = 100_000, the decimals.reduce :* call went on for minutes on my computer before I finally interrupted it.

Determining Floating Point Square Root

How do I determine the square root of a floating point number? Is the Newton-Raphson method a good way? I have no hardware square root either. I also have no hardware divide (but I have implemented floating point divide).
If possible, I would prefer to reduce the number of divides as much as possible since they are so expensive.
Also, what should be the initial guess to reduce the total number of iterations???
Thank you so much!
When you use Newton-Raphson to compute a square-root, you actually want to use the iteration to find the reciprocal square root (after which you can simply multiply by the input--with some care for rounding--to produce the square root).
More precisely: we use the function f(x) = x^-2 - n. Clearly, if f(x) = 0, then x = 1/sqrt(n). This gives rise to the newton iteration:
x_(i+1) = x_i - f(x_i)/f'(x_i)
= x_i - (x_i^-2 - n)/(-2x_i^-3)
= x_i + (x_i - nx_i^3)/2
= x_i*(3/2 - 1/2 nx_i^2)
Note that (unlike the iteration for the square root), this iteration for the reciprocal square root involves no divisions, so it is generally much more efficient.
I mentioned in your question on divide that you should look at existing soft-float libraries, rather than re-inventing the wheel. That advice applies here as well. This function has already been implemented in existing soft-float libraries.
Edit: the questioner seems to still be confused, so let's work an example: sqrt(612). 612 is 1.1953125 x 2^9 (or b1.0011001 x 2^9, if you prefer binary). Pull out the even portion of the exponent (9) to write the input as f * 2^(2m), where m is an integer and f is in the range [1,4). Then we will have:
sqrt(n) = sqrt(f * 2^2m) = sqrt(f)*2^m
applying this reduction to our example gives f = 1.1953125 * 2 = 2.390625 (b10.011001) and m = 4. Now do a newton-raphson iteration to find x = 1/sqrt(f), using a starting guess of 0.5 (as I noted in a comment, this guess converges for all f, but you can do significantly better using a linear approximation as an initial guess):
x_0 = 0.5
x_1 = x_0*(3/2 - 1/2 * 2.390625 * x_0^2)
= 0.6005859...
x_2 = x_1*(3/2 - 1/2 * 2.390625 * x_1^2)
= 0.6419342...
x_3 = 0.6467077...
x_4 = 0.6467616...
So even with a (relatively bad) initial guess, we get rapid convergence to the true value of 1/sqrt(f) = 0.6467616600226026.
Now we simply assemble the final result:
sqrt(f) = x_n * f = 1.5461646...
sqrt(n) = sqrt(f) * 2^m = 24.738633...
And check: sqrt(612) = 24.738633...
Obviously, if you want correct rounding, careful analysis needed to ensure that you carry sufficient precision at each stage of the computation. This requires careful bookkeeping, but it isn't rocket science. You simply keep careful error bounds and propagate them through the algorithm.
If you want to correct rounding without explicitly checking a residual, you need to compute sqrt(f) to a precision of 2p + 2 bits (where p is precision of the source and destination type). However, you can also take the strategy of computing sqrt(f) to a little more than p bits, square that value, and adjust the trailing bit by one if necessary (which is often cheaper).
sqrt is nice in that it is a unary function, which makes exhaustive testing for single-precision feasible on commodity hardware.
You can find the OS X soft-float sqrtf function on opensource.apple.com, which uses the algorithm described above (I wrote it, as it happens). It is licensed under the APSL, which may or not be suitable for your needs.
Probably (still) the fastest implementation for finding the inverse square root and the 10 lines of code that I adore the most.
It's based on Newton Approximation, but with a few quirks. There's even a great story around this.
Easiest to implement (you can even implement this in a calculator):
def sqrt(x, TOL=0.000001):
y=1.0
while( abs(x/y -y) > TOL ):
y= (y+x/y)/2.0
return y
This is exactly equal to newton raphson:
y(new) = y - f(y)/f'(y)
f(y) = y^2-x and f'(y) = 2y
Substituting these values:
y(new) = y - (y^2-x)/2y = (y^2+x)/2y = (y+x/y)/2
If division is expensive you should consider: http://en.wikipedia.org/wiki/Shifting_nth-root_algorithm .
Shifting algorithms:
Let us assume you have two numbers a and b such that least significant digit (equal to 1) is larger than b and b has only one bit equal to (eg. a=1000 and b=10). Let s(b) = log_2(b) (which is just the location of bit valued 1 in b).
Assume we already know the value of a^2. Now (a+b)^2 = a^2 + 2ab + b^2. a^2 is already known, 2ab: shift a by s(b)+1, b^2: shift b by s(b).
Algorithm:
Initialize a such that a has only one bit equal to one and a^2<= n < (2*a)^2.
Let q=s(a).
b=a
sqra = a*a
For i = q-1 to -10 (or whatever significance you want):
b=b/2
sqrab = sqra + 2ab + b^2
if sqrab > n:
continue
sqra = sqrab
a=a+b
n=612
a=10000 (16)
sqra = 256
Iteration 1:
b=01000 (8)
sqrab = (a+b)^2 = 24^2 = 576
sqrab < n => a=a+b = 24
Iteration 2:
b = 4
sqrab = (a+b)^2 = 28^2 = 784
sqrab > n => a=a
Iteration 3:
b = 2
sqrab = (a+b)^2 = 26^2 = 676
sqrab > n => a=a
Iteration 4:
b = 1
sqrab = (a+b)^2 = 25^2 = 625
sqrab > n => a=a
Iteration 5:
b = 0.5
sqrab = (a+b)^2 = 24.5^2 = 600.25
sqrab < n => a=a+b = 24.5
Iteration 6:
b = 0.25
sqrab = (a+b)^2 = 24.75^2 = 612.5625
sqrab < n => a=a
Iteration 7:
b = 0.125
sqrab = (a+b)^2 = 24.625^2 = 606.390625
sqrab < n => a=a+b = 24.625
and so on.
A good approximation to square root on the range [1,4) is
def sqrt(x):
y = x*-0.000267
y = x*(0.004686+y)
y = x*(-0.034810+y)
y = x*(0.144780+y)
y = x*(-0.387893+y)
y = x*(0.958108+y)
return y+0.315413
Normalise your floating point number so the mantissa is in the range [1,4), use the above algorithm on it, and then divide the exponent by 2. No floating point divisions anywhere.
With the same CPU time budget you can probably do much better, but that seems like a good starting point.

Large Exponents in Ruby?

I'm just doing some University related Diffie-Hellman exercises and tried to use ruby for it.
Sadly, ruby doesn't seem to be able to deal with large exponents:
warning: in a**b, b may be too big
NaN
[...]
Is there any way around it? (e.g. a special math class or something along that line?)
p.s. here is the code in question:
generator = 7789
prime = 1017473
alice_secret = 415492
bob_secret = 725193
puts from_alice_to_bob = (generator**alice_secret) % prime
puts from_bob_to_alice = (generator**bob_secret) % prime
puts bobs_key_calculation = (from_alice_to_bob**bob_secret) % prime
puts alices_key_calculation = (from_bob_to_alice**alice_secret) % prime
You need to do what is called, modular exponentiation.
If you can use the OpenSSL bindings then you can do rapid modular exponentiation in Ruby
puts some_large_int.to_bn.mod_exp(exp,mod)
There's a nice way to compute a^b mod n without getting these huge numbers.
You're going to walk through the exponentiation yourself, taking the modulus at each stage.
There's a trick where you can break it down into a series of powers of two.
Here's a link with an example using it to do RSA, from a course I took a while ago:
Specifically, on the second page, you can see an example:
http://www.math.uwaterloo.ca/~cd2rober/Math135/RSAExample.pdf
More explanation with some sample pseudocode from wikipedia: http://en.wikipedia.org/wiki/Modular_exponentiation#Right-to-left_binary_method
I don't know ruby, but even a bignum-friendly math library is going to struggle to evaluate such an expression the naive way (7789 to the power 415492 has approximately 1.6 million digits).
The way to work out a^b mod p without blowing up is to do the mod ping at every exponentiation - I would guess that the language isn't working this out on its own and therefore must be helped.
I've made some attempts of my own. Exponentiation by squaring works well so far, but same problem with bigNum. such a recursive thing as
def exponentiation(base, exp, y = 1)
if(exp == 0)
return y
end
case exp%2
when 0 then
exp = exp/2
base = (base*base)%##mod
exponentiation(base, exp, y)
when 1 then
y = (base*y)%##mod
exp = exp - 1
exponentiation(base, exp, y)
end
end
however, it would be, as I'm realizing, a terrible idea to rely on ruby's prime class for anything substantial. Ruby uses the Sieve of Eratosthenes for it's prime generator, but even worse, it uses Trial division for gcd's and such....
oh, and ##mod was a class variable, so if you plan on using this yourselves, you might want to add it as a param or something.
I've gotten it to work quite quickly for
puts a.exponentiation(100000000000000, 1222555345678)
numbers in that range.
(using ##mod = 80233)
OK, got the squaring method to work for
a = Mod.new(80233788)
puts a.exponentiation(298989898980988987789898789098767978698745859720452521, 12225553456987474747474744778)
output: 59357797
I think that should be sufficient for any problem you might have in your Crypto course
If you really want to go to BIG modular exponentiation, here is an implementation from the wiki page.
#base expantion number to selected base
def baseExpantion(number, base)
q = number
k = ""
while q > 0 do
a = q % base
q = q / base
k = a.to_s() + k
end
return k
end
#iterative for modular exponentiation
def modular(n, b, m)
x = 1
power = baseExpantion(b, 2) #base two
i = power.size - 1
if power.split("")[i] == "1"
x = x * n
x = x % m
end
while i > 0 do
n *= n
n = n % m
if power.split("")[i-1] == "1"
x *= n
x = x % m
end
i -= 1
end
return x
end
Results, where tested with wolfram alpha
This is inspired by right-to-left binary method example on Wikipedia:
def powmod(base, exponent, modulus)
return modulus==1 ? 0 : begin
result = 1
base = base % modulus
while exponent > 0
result = result*base%modulus if exponent%2 == 1
exponent = exponent >> 1
base = base*base%modulus
end
result
end
end

Resources