Working With Number – Infinity Multiplication – Optimised The Code – Ruby

In the previous two chapters of the “Infinity Multiplication” series for Ruby, I had demonstrated the methods and the programming codes for multiplying together two numbers that were stored as string type data, and the equation was processed at the rate of one digit at a time. If you need a reference on the procedures for the multiplication equation, please refer to chapter one and two of the “Infinity Multiplication” series. The chapters’ names are “Infinity Multiplication – Beyond integer” and “Infinity Multiplication – Decimal, Precise Float Calculation”.

The previous two chapters’ programming codes are error proof. This is because the programming codes for the previous chapters were built to process the equation at the rate of only one digit at a time. Besides that, we were also storing the answer for each cycle of multiplication on a separate temporary answer string before joining the temporary answer string to the final output string. By having a temporary answer string, it is easier to inspect if there are any errors in the formula or in the code. Nonetheless, the programming codes of the previous chapters are inefficient and should not be used in a production environment.

In this chapter, I will demonstrate what in my opinion consider to be the most optimized way for optimizing the previous chapters’ programming codes and methods. By optimizing the methods and the codes, our program can execute efficiently, and therefore, we can utilize the program for the production environment. Same as the previous chapters, the source code of this chapter was tested against hundred of thousand plus test cases. With this article, I also included the source code for testing all the previous chapters’ programming codes and including the code of this chapter. I also did a benchmark test for this production version of infiX versus the previous versions of infiX. Note that infiX is the name of the program that is the programming code examples for this chapter and the all the previous chapters.

Previous Chapter: Decimal, Precise Float CalculationNext Chapter: Chapter 3B

Optimizing The Code

Before further discussion in regard to how to optimize the program to operate more efficiently, I will first present the results of the benchmark test for this version of infiX versus the previous version of infiX. When testing for how fast the program executes, I produced two strings of digits, each string contained forty digits. The strings are then passed into each version of infiX 100,000 times, and the time it took for each version of infiX to complete the calculation was recorded as the benchmark score. This test ran twice and the average result between the two tests is used as the final benchmark score. Note that both versions of infiX were calculating the same two strings of digits 100,000 times.

This version of infiX for Ruby took 22.300 seconds to compute the results for 100,000 cases, and the test strings didn’t have a fractional part. With the decimal, the program took 24.158 seconds to compute the results 100,000 times. For a comparison of the execution time, the previous version of infiX took 258.338 seconds to compute the results for 100,000 cases, and the test string didn’t have a fractional part. When there is a fractional part, the previous version took 259.239 seconds to compute the result for 100,000 test cases.

What are the changes in the methods and in the programming code that give the program the ability to operate more than ten times faster? Instead of processing one digit at a time, the program was converted to process the equation at the rate of five digits at a time. For a summary of each multiplication cycle, the previous version of infiX had to convert a digit that was stored as string type data to an integer type data twice, convert the result value to string type data, stores the result value of the multiplication procedure to a temporary answer string, add the temporary answer string to the final output string, and calculate the carry-over values for both, the addition and the multiplication procedures. By converting the program to compute the equation at the rate of five digits at a time, we would still have the same procedures for each multiplication cycle. Nonetheless, instead of only consuming a single digit from both numbers per multiplication cycle, each of our multiplication cycles would now consume five digits. Thus, any single process in the multiplication cycle would be applied to a block of five digits and not just one. By processing more digits per cycle, the program saves processing time from all its sub-process of a multiplication cycle.

Besides processing more digits per cycle, the temporary answer string is now removed and the program would only use the single output string for storing and processing all the result values. By removing the temporary answer string, the program would also save memory usage, and therefore, requires far less memory to operate.

Besides the above changes, the “for” loop was converted into the “while” loop. The “while” loop was benchmarked to execute faster than the “for” loop. For extracting characters from the string, I had a choice between Ruby’s slice() function and Ruby’s string square bracket method. When it comes to speed and execution time, after testing them with ten million execution cycles, I found that they were operated at almost the same speed. Since they were operating at the same speed, it only comes to the matter of taste in style. Therefore, I chose the “[]” method as the style for this version of infiX. For the Ruby version of infiX, I do not have an alternative option for trimming the strings. Therefore, the gsub() function that utilizes regex patterns was the only choice.

Although this version of infiX only processes five digits at a time, this number may have to be configurated to a lower amount if we are truly working in a 32 bits environment. This is because the maximum value that a 32 bits integer can hold is 2,147,483,647. Which is about 10 digits in length. For a multiplication equation, five digits multiply by another five digits have the potential to produce a maximum result value that is ten digits in length and that value is 9,999,999,999. This is a value that is a little bit larger than the maximum value that a 32 bits integer can hold. Thus, if truly working in a 32 bits environment, the number of digits to be processed per cycle should be configurated at the rate of four digits at a time.

When it comes to the formula for multiplying five digits at a time, the formula is similar to the formula of multiplying one digit at a time. Nonetheless, when writing the result to the output string it can be a complicated task. This simply because the program would now operate without a temporary answer string. Before going into the examples and the explanations of the programming code, let’s first look at the new formula for the multiplication procedures.

*** Carry-overM = the carry-over value for the multiplication procedure.
    Carry-overA = the carry-over value for the addition procedure.

Example: 92345 67890 x 12545 89085
                                                         92345  67890
                                                       x 12545  89085
                                                        --------------
Multiply 67890 to 89085                                       (80650)  | carry-overM = 60479 and carry-overA = 0
  
Multiply 92345 to 89085                                 54325          | carry-overM = 82265
Add carry-overA and carry-overM                            +0
                                                       +60479          
Value of this position                                 (14804)         | carry-overA = 1
Add carry-overA and carry-overM                      1
                                                +82265
Value of this position                          (82266)                | 0 carry-overM and 0 carry-overA

Current answer                                   82266  14804  80650                 
                                                          
Multiply 67890 to 12545                                 80050          | carry-overM = 8516
Add current position value in answer string            +14804               
Value of this position                                 (94854)         | carry-overA = 0

Multiply 92345 to 12545                          68025                 | carry-overM = 15584
Add current position value in answer string     +82266
Add carry-overA and carry-overM                  +8516 
                                                     0
Value of this position                          (58807)                | carry-overA = 1
Add carry-overA and carry-overM               1
                                         +15584
Value of this position                   (15585)
                                         ----------------------------
Add the previous results together         15585  58807  94854  80650                                                              

With the formula above in context, we can see that we are multiplying all the digits in A string to the digits of B string in a block of five digits at a time. Each block of five digits in A string is multiplied by the first block of five digits in B string, then to the second block, then to third and so on if there is. For each time when we multiplied a block of five digits from A to the first block of five digits in B, starting from right to left of the answer string, we would only store a block of five digits at a time to the answer string. When there are more than five digits in the result value then all the digits after the fifth digits is to be kept as the carry-over value for the next equation. When storing the result into the answer string, the first five digits that are the rightmost in the result value would be the digits that are to be stored into the answer string. The sixth digits and beyond would be kept as the carry-over value.

When it comes to the procedure of when we are adding the carry-over value to the result value of the equation. Similar to the above, the first five most digits that are the rightmost in the result is the result value, digits that are beyond that is the carry-over value.

After multiplying all the digits in A string to the first block of five digits in B string, we would then multiply all the digits in A string to the next block of five digits in B string. When we are moving over to the next block of five digits in B string, our starting position for adding the equation’s result value into the answer string would move over to the left by one block that is five digits in length. Every time when the equation is moving to another block of five digits in B string, the starting position in the answer string would also move over to the left by one block of digits. For the procedure of adding the result value of an equation into the answer string, the rules from above that are in regard to the carry-over values, and how many digits are the result and how many digits are the carry-over value would also apply to this procedure.

When it comes to the variables that we need in the program, the first variable our program needs is a variable that store a value that represents how many digits that the program is processing per multiplication cycle. This value can extend the flexibility of the program. If we were to work in an environment where the integer data type is stored in larger bit blocks then we can increase this value so that the program can process more digits per cycle of calculation. On the other hand, in the event where we have to work in a more restricted environment and integer values are stored in smaller bit blocks then we can decrease this value to adapt the program to the environment. For the naming context, the mentioned variable is named “digit”.

The second variable we would need is a variable that will store a value that is the index count of how many times we had read from B string. In the circumstance of this article, we know that each time we read a string, we are reading a block of five digits. When it comes to the ending of the strings, the amount of how many digits that are left for the program to read from can vary. Nonetheless, this program was built to considers the variation on the number of digits in the final block of digits for both strings. For the naming context, the variable for storing the value of the read count for B string is named “posr”.

Besides the above variables, the program would also need a variable that stores a value that represents the position that the program is working on in the answer string. That variable is named “posw”. For a fourth new variable, the variable is named “prepos”. When utilizing the “[]” method, the behavior of the “[]” method is that when a negative value is passed into the “[]” method, then the Ruby will treat that value as a position in the string that is counting from right to left, with negative one being the rightmost position. This is the opposite of when a positive value is passed into the “[]”. Since we are working from right to left in the strings, for some procedures, it is easier to use negative values. Therefore, a negative version of the value that is stored in the “digit” variable is assigned to the “prepos” variable to enhance the simplicity for the equation. This below is the variables that we need to declare outside of the calculation loop.

_temp = 0
_alen = _a.length
_blen = _b.length
_bi = _blen
_posr = 0
_posw = 0
_digit = 5
_prepos = _digit * -1

For every time the program process a position in B string, the program would have to process all the positions in A string. Thus, the outer loop will stay at a position in B string until all the position in A string is read, and after each cycle, the outer loop will move the read point for B string by five positions. For the inner loop, after processing a position in A string, the loop will also move the read point for A string by five positions.

To get five digits from B string, I utilized the “[]” bracket method on string B. The syntax of the “[]” method are string_variable_name[argument_1], string_variable_name[argument_1..argument_2], and string_variable_name[argument_1…argument_2]. For this method, argument one and two accepts integer values. Argument two is an optional value. If only one argument was defined then Ruby will use the value that was passed into the argument as a position in the string and return a single character that is in that position. For Ruby’s “[]” method, a positive value or a value of zero are positions that are counting from left to right, with zero being the leftmost position. The “[]” method treats negative values as positions that are counting from right to left, with negative one being the rightmost position. If the values that passed into the “[]” method are positions that are out of range then Ruby will return a nil value.

If the second argument for the “[]” method was defined then Ruby will treat the values that passed into both the arguments as two positions. Ruby will extract all the characters between these two positions. Ruby will always extract the character that is in the position that was defined by the first argument. Ruby will only extract the character that is in the position that was defined by the second argument if we used a double decimal point as the separator between the values that were passed into the two arguments. If a triple decimal point separator was used, then Ruby will not extract the character that is in the position that was defined by the second argument, but just the character that is before that position. These two positions that are defined by values that were passed to the first and the second argument are called the position “from” and the position “to”. The “from” position is defined by the first argument’s value and the “to” position is defined by the second argument’s value.

As the time of this writing, Ruby counts the “from” and the “to” position from left to right. For example, if the “to” position is on the left of the “from” position, then Ruby will return a nil value. Another behavior to the square bracket method is that if the “to” position is larger than the string’s length, then Ruby will return all the characters from the “from” position to the end of the string. Nonetheless, if the “from” position is somehow out of range to the left of the zero position, then a nil value will be return. For example, if the string is 25 characters in length, giving the first argument a value of -28 would make the “from” position being the position that is out of range to the left of the zero position.

When it comes to utilizing the “[]” method, The first value that we are passing as the first argument is the value that is the current position of where we are at in B string subtract to the value of the number of digits that the program is processing per cycle. The value that we want to pass as the second argument for the “[]” method is how many digits we want to extract. This value is defined by the “digit” variable.

Notice that we do not subtract the string’s length to a value of one to get the current position. This is because if we were to extract five rightmost digits in a string that is 20 positions long, although the last position is position 19 if to extract five digits, we would have to start at position 15. Therefore, in the example, we can just utilize the length of the string, which is a value of 20, and subtract that value to a value of 5 to get the the starting position, which would be position 15. Also, we do not want a negative value as the first argument for the “[]” method. This event occurs when the last block of digits contains less than the number of digits that the program is processing per cycle. Thus, if the value that we are going to pass as the first argument is a negative value then we would convert that value to a value of zero, which is the leftmost and is the first position in a string. Also, I used the triple dot separator between the values that were passed into the two arguments. Therefore, Ruby will only extract all the digits that are up to the “to” position but not the digit that is in the to position.

When we are executing the inner loop for the calculating procedures and when the answer string did not yet contain a value, we would simply place the result value of the procedures into the output string bases on the following conditions. When it comes to processing the result value of the current procedure, one of the values that we have to evaluate for is the carry-over value. To get the carry-over value from the result value, we can utilize the “[]” method on the result value to get the character from the zero position to five positions away from the rightmost of the result value.

To do this, we pass a value of zero as the first argument for the “[]” method, then we pass the “prepos” variable’s value as the second argument for the “[]” method, and we separate the values with a triple dot separator. If the previous statement fails to produce any digits, Ruby would return a nil value. When it comes to the “to_i” option and nil values, the “to_i” option will convert a nil value to an integer value of zero. When it comes to storing the carry-over value, the carry-over value for the multiplication procedure is assigned to a variable that is named “carryOverM”.

When it comes to appending the result value to the answer string, we would always append the result value in the format of a digits block that is five digits in length. Therefore, we would have to append to the result a number of zeroes for the event of when the result value contains less than five digits. The following examples will demonstrate the circumstance of how the result value can have less than five digits. For the first example, we have an equation of 1 00125 x 1 00001. In that equation, when executing the equation, the first block of five digits from A multiply by the first block of five digits of B would translate into an equation of 1 x 125. The result value of the equation would be a value of 125. If we do not append zeroes to the beginning of the result value to make up for the missing length then when it comes to the next calculation, there will only be a value of one that is going to be added to the beginning of the result value. Which will give us a value of 1125 as the result value for the equation instead of the correct answer of 1 00125.

To be able to always consistently produce a result value that is five digits in length, we add four zeroes to the left side of the result value. We then utilize the “[]” method to extract the five rightmost digits from the just modified result value. For adding zeroes to the result value, we would only execute this procedure after when we had completed the evaluation of the result value for any carry-over value.

After multiplying all the digits block of A string to the first block of digits in B string, we then append any carry-over value to the leftmost of the answer string. The principle of a consistent number of digits in a block of digits would also apply to this procedure. Take note here that when I subtract the index count for A string, of which the value is held by a variable that named “ai”, it can also be done by subtracting “ai” to the value of the “digit” variable as mentioned earlier. This code here is just for the demonstration purpose of which showed that we are moving by five positions per cycle. Nonetheless, for easy customization, this can be changed to “ai – digit”, and when modifying the value of “bi”, it can also be changed to “bi – digit”.

This code block below demonstrate the mentioned procedures in Ruby.

while (_bi > 0) do
     _z = _b[(_bi - _digit > -1? _bi - _digit : 0)...(_bi - 0 < 5? _bi - 0: _bi)].to_i
     _outlen = _output.length
     _ai = _alen
     _carryOverM = 0

     if (_outlen < 1)        
          while (_ai > 0) do
               _temp = (_a[(_ai - _digit > -1? _ai - _digit : 0)...(_ai - 0 < 5? _ai - 0: _ai)].to_i * _z + _carryOverM).to_s
               _carryOverM = _temp[0..._prepos].to_i
               _temp = "0000" + _temp
               _output = _temp[_prepos..-1] + _output
               _ai = _ai - 5
          end
          if (_carryOverM > 0)
               _output = ("0000" + _carryOverM.to_s)[_prepos..-1] + _output 
          end

The previous procedures are for when the answer string did not yet contain a value. However, it can get a bit complicated for when the answer string does contain a value. Each time we read a digits block in A string, we would have to calculate the positions of where we are working on in the answer string. Besides the working on position, the starting position for the answer string would move over to the left by one digits block for each digits block that we read in B string. Also, the position that we are working on in the answer string would also move over to the left by one digits block after we process a digits block from A string.

To calculate the position that we are working on in the answer string, we would first calculate the starting position of the answer string. To get the starting position of the answer string, we multiply how many times we had read from B string to the value of the “prepos” variable, which is just the negative version of the “digit” variable. After getting the starting position of the answer string, we then multiply the number of times we had read from A string to the value of the “prepos” variable. Then we add the result value of that equation to the value of the starting position of the answer string.

For this time of multiplying a digits block from A string to the digits block in B string, we have to add the result value of the equation to a value that exists in the position of where we are working on in the answer string. The result value for this equation is stored in a temporary placeholder, the temporary placeholder is the variable with the name “tempadd”. To get the digits from the answer string, we will utilize the “[]” method. For the “[]” method, we use a value that is the value of the current position that we are working on in the answer string in negative and add that value to the value of the “prepos” variable. The previous value is then passed to the “[]” method as the first argument. For the second argument, we use the value that is the position of where we are currently at in the answer string.

If the tempadd variable holds a value that is more than five digits in length then we would produce a carry-over value for the addition procedure. The carry-over value for our addition procedure is held by a variable that is named carryOverA. For an addition equation, the carry-over value can never be more than a value of one if we are only adding two sets of digits together. Thus, our carry-over value our addition procedure can only be a value of one when the result value contains a value that is more than five digits in length. Else, we will assign a zero value to the carry-over value. The value of the carryOverM and the carryOverA variable will be added to the result value of the equation of next loop execution.

After producing a value for the tempadd variable, we would then place the value of the tempadd variable into our answer string. When it comes to placing the tempadd’s value into the answer string, we first get all the digits from the beginning of the answer string to the position that is before the position of where we grabbed the first digit from the answer string. We then place our tempadd’s value to the right side of the previous value. Then we get the digits from the position that is after the position of the last digit that we grabbed from the answer string to the end of the answer string. We place this value on the right side of where we placed the tempadd’s value. All the digits that we pieced up together with the tempadd’s value are assigned as a new value to the answer string.

After completely multiplying all the digits in A string to a set of digits in B string, we would add the value of the carryOverM and carryOverA variable together, and place this value to the left of the answer string. The below code block demonstrate the above procedures into programming statements for Ruby.

while (_ai > 0) do
     _posw = (_posr * _prepos) + (_loopidx * _prepos);
     _temp = (_a[(_ai - _digit > -1? _ai - _digit : 0)...(_ai - 0 < 5? _ai - 0: _ai)].to_i * _z + _carryOverM + _carryOverA).to_s
     _carryOverM = _temp[0..._prepos].to_i
     _tempadd = ( (_temp[_prepos..-1] || _temp[0..-1]).to_i + _output[(_posw + _prepos)..._posw].to_i).to_s   
     _carryOverA = _tempadd[0..._prepos].to_i
     _tempadd = "0000" + (_tempadd[_prepos..-1] || _tempadd[0..-1] ) 
     _output = _output[0...(_posw + _prepos)] + _tempadd[_prepos..-1] + (_output[_posw..-1] || _output[0..-1])
     _loopidx = _loopidx + 1
     _ai = _ai - 5
end
if (_carryOverA + _carryOverM > 0)
     _output = ("0000" + (_carryOverM + _carryOverA).to_s)[_prepos..-1] + _output 
end

As for testing and debugging the infiX program, the code block below is what I used to test the previous version of InfiX against the native built-in multiplication feature of the Ruby’s platform. For the previous version of infiX, the program was built to process the multiplication equation at the rate of only one digit at a time. Therefore, if the program always correctly produces result values for equations that involve smaller strings of digits, then the program would also be able to consistently produces correct result values for larger strings of digits. I also tested the infiX program against Ruby’s native multiplication feature. As it seems, Ruby’s native multiplication feature can also calculate very large numbers.

When it comes to numbers that do have a fractional part, do take into consideration that the native built-in calculating feature would not be able to provide the actual precise floating point calculation. The tester code below will produce two random numbers for each test case. The tester will then multiplying the numbers together using the native multiplication feature of Ruby. The tester will then pass the numbers as string type data into the infiX function. If there is a mismatch between the two result values, then the tester will print the test case information.

To use this tester, simply look for version two of infiX, which included in the chapter that is called “Decimal, Precise Float Calculation” for Ruby. Then place this tester code with the program. The number of test cases that the tester will execute was preset at a value of 5000. This value can be increased or decreased as necessary. For Ruby, it is safe to set the number of cases to run at less than or equal to 100,000 cases at a time. Otherwise, it might take sometimes to get back the results value. This code block below is the tester code for the version two of infiX.

um = 0
for id in 0..5000
    a = rand(1...999)
    ac = rand(0...99)
    b = rand(1...999)
    bc = rand(0...99)
    padA = ""
    padB = ""
    ind = 0
    ind2 = 0
    
    while (ind < rand(0...3))
        padA = "0" + padA
        ind = ind + 1
    end
    
    while (ind2 < rand(0...3))
        padA = "0" + padA
        ind2 = ind2 + 1
    end
    
    ta = ("+" + a.to_s + "." + padA + ac.to_s).to_f
    tb = ("+" + b.to_s + "." + padB + bc.to_s).to_f
    
    test1 = ta * tb;
    test2 = LibZ.infiX(ta.to_s, tb.to_s)
    test1 = test1.to_s
    
    
    if (test1 != test2)
        um = um + 1
        puts "Unmatch: #{ um }"
        puts ("Question: " + ta.to_s + " * " + tb.to_s + "\n")
        puts ("Native Multiply: " + test1.to_s + " | infiX:" + test2.to_s + "\n\n")
    end

end

When it comes to testing this version of infiX, I tested the result values of this version of infiX to the result values of the previous version of infiX. Since the previous version of infiX was already thoroughly tested, this version’s result value would be correct if matches the result values of the previous version. The previous version of infiX does take a long time to produce result values. Nonetheless, as a prototype, I built the previous version as a demonstrator for the articles that I wrote and also as a tester for testing future releases of the infiX program.

To use the tester code below, simply place this version of infiX and the version two of infiX in the same file and with the tester code. Rename one of the infiX function to infiX2. This tester will produce five blocks of random digits that are before the decimal for each string of digits. The random blocks of digits can be anywhere between one or five digits. The tester will then pad 0 to 15 zeroes behind the decimal, and then the tester will place three blocks of random numbers to the right of the zeroes that were just added to the string. After that, the tester will place another 0 to 15 zeroes and then the last two blocks of random digits to the right side of the previous 3 block of random digits. It is a recommendation that this tester should not be set to execute more than 50,000 cases at a time. It can take a very long time to produce the results. This is because the previous version of InfiX does take a long time to produce an answer.

The below code block is the tester code for Ruby.

um = 0
m = 0
for id in 0...10000
    a1 = rand(0...99999)
    a2 = rand(0...99999)
    a3 = rand(0...99999)
    a4 = rand(0...99999)
    a5 = rand(0...99999)
    ac1 = rand(0...99999)
    ac2 = rand(0...99999)
    ac3 = rand(0...99999)
    ac4 = rand(0...99999)
    ac5 = rand(0...99999)
    
    b1 = rand(0...99999)
    b2 = rand(0...99999)
    b3 = rand(0...99999)
    b4 = rand(0...99999)
    b5 = rand(0...99999)
    bc1 = rand(0...99999)
    bc2 = rand(0...99999)
    bc3 = rand(0...99999)
    bc4 = rand(0...99999)
    bc5 = rand(0...99999)
    padA1 = ""
    padA2 = ""
    padB1 = ""
    padB2 = ""
    idn = 0
    idn2 = 0
   	while (idn < rand(0...15)) do
    	padA1 = "0" + padA1
      idn = idn + 1
    end
    while (idn < rand(15...30)) do
    	padA2 = "0" + padA2
      idn = idn + 1
    end
    while (idn2 < rand(0...15)) do
    	padB1 = "0" + padB1
      idn2 = idn2 + 1 
    end
    while (idn2 < rand(15...30)) do
    	padB2 = "0" + padB2
      idn2 = idn2 + 1
    end
    
    ta = "+" + a1.to_s + a2.to_s + a3.to_s + a4.to_s + a5.to_s + "." + padA1 + ac1.to_s + ac2.to_s + ac3.to_s + padA2 + ac4.to_s + ac5.to_s;  
    tb = "-" + b1.to_s + b2.to_s + b3.to_s + b4.to_s + b5.to_s + "." + padB1 + bc1.to_s + bc2.to_s + bc3.to_s + padB2 + bc4.to_s + bc5.to_s;
    
    test1 = LibZ.infiX(ta, tb)
    test2 = LibZ.infiX2(ta, tb) 
    puts "Cases #" + id.to_s
    if (test1 != test2)
        um = um + 1
        puts ("Unmatched : ##{ um }")
        puts ("Question" +  " : " + ta + " * " + tb )
        puts ("Version 3: " + test1 + " | Version 2: " + test2 )
    end
    if (test1 == test2)
        m = m + 1
        puts "Matched: ##{ m }"
    end
    puts        
end

The below code snippet is the infiX program. This version is usable for a production environment.


Advertisement

# "Copyright Notice", please do not remove.
# Written by Kevin Ng
# The full tutorial on this subject can be found @ http://kevinhng86.iblog.website or http://programming.world.edu.
# Release date to http://programming.world.edu will lag one week after release on http://kevinhng86.iblog.website 
# This source code file is a part of Kevin Ng's Z library.
# This source code is licenses under CCDL-1.0  A copy of CDDL1.0 can be found at https://opensource.org/licenses/CDDL-1.0
# End "Copyright Notice" 

# Notice: This is the normal usage version of infinity multiplication written for Ruby and is designed for a production environmen.
#         The first limitation of this version is how much memory Ruby allow the string to be store.
#         The second limitation is how much memory Ruby can assign to the program.
#         If memory allow, for this version a string can only be less than 1,073,741,823 digits
#
#    Example of memory usage calculation:
#         500 megabyte string of digits multiply to another 500 megabyte string of digits.
#         1000 Mb of memory to store the strings outside of the function.
#         1000 Mb of memory for when the strings are passing into the function. 
#         1000 Mb for the answer string.
#          100 Mb (this can vary but most likely to be less than this) for script operation.
#         3100 Mb of memory is requires for multiplying two strings of digits where each string is 500MB in size.

class LibZ
    def self.infiX(_a, _b)
    
        _isaNeg = _a[0] == "-"? 1 : 0
        _isbNeg = _b[0] == "-"? 1 : 0       
        _a = _a.gsub(/^[+-]/,"")
        _b = _b.gsub(/^[+-]+/,"")    
        _a = _a.gsub(/^0+/, "")
        _b = _b.gsub(/^0+/, "") 
        
        _aDecPos = _a.index(".") || -1
        _bDecPos = _b.index(".") || -1
        _oDecPos = 0
        
        if (_aDecPos > -1)
            _a = _a.gsub(/0+$/, "")
            _aDecPos = (_a.length - 1 - _aDecPos) > 0? (_a.length - 1 - _aDecPos) : 0
            _a = _a.gsub(/[.]/, "")
        end
    
        if (_bDecPos > -1)    
            _b = _b.gsub(/0+$/, "")
            _bDecPos = (_b.length - 1 - _bDecPos) > 0? (_b.length - 1 - _bDecPos) : 0
            _b = _b.gsub(/[.]/, "")
        end
        
        if (_a.length < 1 or _b.length < 1)
            return "0"    
        end

        if (_a == "1" && _aDecPos < 1)
            if (_bDecPos > 0)
                _b = _b[0...(_b.length - _bDecPos )] + "." + _b[(_b.length - _bDecPos)..._b.length]
                _b = _b[0] == "."? "0" + _b : _b
                return ((_isaNeg != _isbNeg)? "-" + _b : _b)
            end
            return ((_isaNeg != _isbNeg)? "-" + _b : _b)
        end
        
        if (_b == "1" && _bDecPos < 1)
            if (_aDecPos > 0)
                _a = _a[0...(_a.length - _aDecPos )] + "." + _a[(_a.length - _aDecPos)..._a.length]
                _a = _a[0] == "."? "0" + _a : _a
                return ((_isaNeg != _isbNeg)? "-" + _a : _a)
            end
            return ((_isaNeg != _isbNeg)? "-" + _a : _a)
        end
        
        if (_aDecPos > -1 || _bDecPos > -1)
            _aDecPos = (_aDecPos > -1)? _aDecPos : 0
            _bDecPos = (_bDecPos > -1)? _bDecPos : 0        
            _oDecPos = _aDecPos + _bDecPos
        end
        
        _temp = 0
        _alen = _a.length
        _blen = _b.length
        _output = ""
        _bi = _blen
        _posr = 0
        _posw = 0
        _digit = 5
        _prepos = _digit * -1
    
        while (_bi > 0) do
            _z = _b[(_bi - _digit > -1? _bi - _digit : 0)...(_bi - 0 < 5? _bi - 0: _bi)].to_i
            _outlen = _output.length
            _ai = _alen
            _carryOverM = 0
            
            if (_outlen < 1)        
                while ( _ai > 0) do
                _temp = (_a[(_ai - _digit > -1? _ai - _digit : 0)...(_ai - 0 < 5? _ai - 0: _ai)].to_i * _z + _carryOverM).to_s
                _carryOverM = _temp[0..._prepos].to_i
                _temp = "0000" + _temp
                _output = _temp[_prepos..-1] + _output
                _ai = _ai - 5
                end
                if (_carryOverM > 0)
                    _output = ("0000" + _carryOverM.to_s)[_prepos..-1] + _output 
                end
            else 
                _carryOverA = 0
                _tempadd = 0
                _loopidx = 0
                while (_ai > 0) do
                    _posw = (_posr * _prepos) + (_loopidx * _prepos);
                    _temp = (_a[(_ai - _digit > -1? _ai - _digit : 0)...(_ai - 0 < 5? _ai - 0: _ai)].to_i * _z + _carryOverM + _carryOverA).to_s
                    _carryOverM = _temp[0..._prepos].to_i
                    _tempadd = ( (_temp[_prepos..-1] || _temp[0..-1]).to_i + _output[(_posw + _prepos)..._posw].to_i).to_s   
                    _carryOverA = _tempadd[0..._prepos].to_i
                    _tempadd = "0000" + (_tempadd[_prepos..-1] || _tempadd[0..-1] ) 
                    _output = _output[0...(_posw + _prepos)] + _tempadd[_prepos..-1] + (_output[_posw..-1] || _output[0..-1])
                    _loopidx = _loopidx + 1
                    _ai = _ai - 5
                end
                if ( _carryOverA + _carryOverM > 0 )
                    _output = ("0000" + (_carryOverM + _carryOverA).to_s)[_prepos..-1] + _output 
                end
            end
            _posr = _posr + 1
            _bi = _bi - 5
        end
    
        if (_oDecPos > 0)
            while (_output.length < _oDecPos) do
                _output = "0" + _output
            end
            _output = _output[0...(_output.length - _oDecPos)] + "." + _output[(_output.length - _oDecPos)..._output.length]
            _output = _output.gsub(/0+$/,"")
            _output = _output.gsub(/[.]$/, "")
            _output = _output.gsub(/^0+/, "")
            _output = _output[0] == "."? "0" + _output : _output
            return ((_isaNeg != _isbNeg)? "-" + _output : _output)
        end
        _output = _output.gsub(/^0+/, "")
        return ((_isaNeg != _isbNeg)? "-" + _output : _output)
    end
    
end

x = ".999999999999990000000000000000000000000000"
y = "-0000000000000000000000000000.99999999999999"

puts LibZ.infiX(x,y)

Digiprove sealCopyright secured by Digiprove © 2017

This post was written by Kevin and was first post @ http://kevinhng86.iblog.website.
Original Post Name: "Working With Number – Infinity Multiplication – Optimised The Code – Ruby".
Original Post Link: http://kevinhng86.iblog.website/2017/02/20/working-with-number-infinity-multiplication-optimised-the-code-ruby/.

Advertisement
Premium WordPress products


Random Article You May Like

Leave a Reply

Your email address will not be published. Required fields are marked *

*
*