Karatsuba Multiplication in C++

Code for ADSA Assignment1

Don’t copy it, or I can ensure you that you’ll get caught. 

Change some variable names ,swap order of some codes won’t help.

I wrote two version just hope these will inspire your own coding.

This program is designed for digits longer than what  uint128_t can handle. (In this case, 100 digits, but can be extended easily).


Requirement:

Your submission should contain exactly one file: main.cpp. You do not need to submit a design.

Your program takes one line as input. The input line contains three integers separated by spaces. Let the three integers be I1, I2, and B. I1 and I2 are both nonnegative integers up to 100 digits long (there are no leading 0s, except when the value itself is 0). B is I1 and I2’s base (B is from 2 to 10).

Your program should output the sum of I1 and I2, using the school method, then the product of I1 and I2, using the Karatsuba algorithm.

The results should still use base B. Please separate the results using one space.

Sample input 1: 101 5 10
Sample output 1: 106 505
Sample input 2: 10 111 2
Sample output 2: 1001 1110
Sample input 3: 111 10 2
Sample output 2: 1001 1110


updated on March 2020
requested by a junior student
Static Array version (with adjustable input size)

#include 
#include 

//Static Array size
#define MAX_LENGTH 100
#define ARR_MAX_SIZE MAX_LENGTH*MAX_LENGTH
#define ARR_IDX(arr,n) (&arr[n*ARR_MAX_SIZE])

void school_add(int* arr1, int* arr2, int* result, int base);
void school_sub(int* arr1, int* arr2, int* result, int base);
void leftshift(int* arr);
void copy_arr(int* arr1, int* arr2);
void school_mul(int* arr1, int* arr2, int* result, int base);
void stringtoarray(std::string str, int* arr);
void print_arr(int* arr);
long long int slice(int* arr1, int* arr2, int* high_1, int* low_1, int* high_2, int* low_2);
void Karatsuba_Multiplication(int* arr1, int* arr2, int* result, int base);

int main()
{
    std::string I1, I2, base_str;
    std::cin >> I1 >> I2 >> base_str;
    auto base = std::stoi(base_str);

    int arr[ARR_MAX_SIZE << 1] = { 0 };
    auto arr1 = ARR_IDX(arr, 0);
    auto arr2 = ARR_IDX(arr, 1);

    stringtoarray(I1, arr1);
    stringtoarray(I2, arr2);

    int result[ARR_MAX_SIZE << 1] = { 0 };
    auto result_add = ARR_IDX(result, 0);
    auto result_mul = ARR_IDX(result, 0);

    school_add(arr1, arr2, result_add, base);
    print_arr(result_add);
    std::cout << " ";
    Karatsuba_Multiplication(arr1, arr2, result_mul, base);
    print_arr(result_mul);
    std::cout << " ";
    return 0;
}

void school_add(int* arr1, int* arr2, int* result, int base)
{
    int carry = 0, bit = 0;
    for (auto i = ARR_MAX_SIZE - 1; i > -1; i--)
    {
        *(result + i) = (*(arr1 + i) + *(arr2 + i) + carry >= base) ? *(arr1 + i) + *(arr2 + i) + carry - base : *(arr1 + i) + *(arr2 + i) + carry;
        carry = !!(*(arr1 + i) + *(arr2 + i) + carry >= base);
    }
}

void school_sub(int* arr1, int* arr2, int* result, int base)
{
    int carry = 0, bit = 0;
    for (auto i = ARR_MAX_SIZE - 1; i > -1; i--)
    {
        *(result + i) = (*(arr1 + i) - *(arr2 + i) - carry < 0) ? *(arr1 + i) - *(arr2 + i) - carry + base : *(arr1 + i) - *(arr2 + i) - carry;
        carry = !!(*(arr1 + i) - *(arr2 + i) - carry < 0);
    }
}

void leftshift(int* arr)
{
    for (auto i = 0; i < ARR_MAX_SIZE - 1; i++)
        *(arr + i) = *(arr + i + 1);
    *(arr + ARR_MAX_SIZE - 1) = 0;
}

void copy_arr(int* arr1, int* arr2)
{
    for (auto i = 0; i < ARR_MAX_SIZE; i++)
        *(arr1 + i) = *(arr2 + i);
}

void school_mul(int* arr1, int* arr2, int* result, int base)
{
    int last_result[ARR_MAX_SIZE] = { 0 };
    int this_result[ARR_MAX_SIZE] = { 0 };
    int sum_result[ARR_MAX_SIZE] = { 0 };
    int carry = 0;
    for (auto j = ARR_MAX_SIZE - 1; j > ARR_MAX_SIZE - MAX_LENGTH - 1; j--)
    {
        memset(this_result, 0, sizeof(this_result));
        memset(sum_result, 0, sizeof(sum_result));
        for (auto i = ARR_MAX_SIZE - 1; i > ARR_MAX_SIZE - MAX_LENGTH - 1; i--)
            if (carry != 0 || *(arr1 + i) != 0 || *(arr2 + j) != 0)
            {
                *(this_result + i) = (*(arr1 + i) * *(arr2 + j) + carry >= base) ? (*(arr1 + i) * *(arr2 + j) + carry) % base : *(arr1 + i) * *(arr2 + j) + carry;
                carry = (*(arr1 + i) * *(arr2 + j) + carry >= base) ? ((*(arr1 + i) * *(arr2 + j) + carry - ((*(arr1 + i) * *(arr2 + j) + carry) % base)) / base) : 0;
            }
        for(auto k=0;k ARR_MAX_SIZE - MAX_LENGTH - 1; i--)
        *(high_1 + iter--) = *(arr1 + i);
    //low_1
    iter = ARR_MAX_SIZE - 1;
    for (auto i = ARR_MAX_SIZE - 1; i > ARR_MAX_SIZE - 1-bit; i--)
        *(low_1 + iter--) = *(arr1 + i);
    //high_2
    iter = ARR_MAX_SIZE - 1;
    for (auto i = ARR_MAX_SIZE - 1 - bit; i > ARR_MAX_SIZE - MAX_LENGTH - 1; i--)
        *(high_2 + iter--) = *(arr2 + i);
    //low_2
    iter = ARR_MAX_SIZE - 1;
    for (auto i = ARR_MAX_SIZE - 1; i > ARR_MAX_SIZE - 1 - bit; i--)
        *(low_2 + iter--) = *(arr2 + i);
    return bit;
}

void Karatsuba_Multiplication(int* arr1, int* arr2, int* result, int base)
{
    int high_1[ARR_MAX_SIZE] = { 0 };
    int low_1[ARR_MAX_SIZE] = { 0 };
    int high_2[ARR_MAX_SIZE] = { 0 };
    int low_2[ARR_MAX_SIZE] = { 0 };
    long long int m = slice(arr1, arr2, high_1, low_1, high_2, low_2);
    if (m <= 10)
        school_mul(arr1, arr2, result, base);

    int z2[ARR_MAX_SIZE] = { 0 };
    int z1[ARR_MAX_SIZE] = { 0 };
    int z0[ARR_MAX_SIZE] = { 0 };
    int temp_1[ARR_MAX_SIZE] = { 0 };
    int temp_2[ARR_MAX_SIZE] = { 0 };
    int temp_3[ARR_MAX_SIZE] = { 0 };
    int temp_4[ARR_MAX_SIZE] = { 0 };
    int temp_5[ARR_MAX_SIZE] = { 0 };
    int final[ARR_MAX_SIZE] = { 0 };
    //z2 = high_1 * high_2
    school_mul(high_1, high_2, z2, base);
    
    //z0 = low_1 * low_2
    school_mul(low_1, low_2, z0, base);
    
    //z1 = (high_1 + low_1) * (high_2 + low_2) - z2 - z0
    school_add(high_1, low_1, temp_1, base);
    school_add(high_2, low_2, temp_2, base);
    school_mul(temp_1, temp_2, temp_3, base);
    school_sub(temp_3, z2, temp_4,base);
    school_sub(temp_4, z0, z1, base);
    
    //(xy = z2 * base^(2m) + z1 * base^(m) + z0)
    for (long long int i = 0; i < 2 * m; i++)
        leftshift(z2);
    for (long long int i = 0; i < m; i++)
        leftshift(z1);
    school_add(z2, z1, temp_5, base);
    school_add(temp_5, z0, final, base);
    copy_arr(result, final);
}

void stringtoarray(std::string str, int* arr)
{
    long long int iter = 0;
    for (long long int i = str.size() - 1; i > -1; i--)
        *(arr + ARR_MAX_SIZE - 1 - iter++) = int(str[i]) - 48;
}

void print_arr(int* arr)
{
    bool digit_start = false;
    for (auto i = 0; i < ARR_MAX_SIZE; i++)
    {
        if (*(arr + i))
            digit_start = true;
        if (digit_start)
            std::cout << *(arr + i);
    }
}

Original vector version in 2017 (code works but not elegant)

#include<iostream>
#include<string>
#include<sstream>
#include<vector>
#include <algorithm>
#include <math.h>

using namespace std;

void printvector(vector<int> vec)
{
  for (int i = 0;i < vec.size();i++)
  {
    cout << vec[i];
  }
  //cout << endl;
}

vector<int> school_type_addition(vector<int> vec1, vector<int> vec2, int *base)
{
  vector<int> carry,result;
  reverse(vec1.begin(), vec1.end());
  reverse(vec2.begin(), vec2.end());
  carry.push_back(0);
  for (int i = 0;i < max(vec1.size(), vec2.size());i++)
  {
    //In the bounders of vector
    if (i < min(vec1.size(), vec2.size()))
    {
      
      if (vec1[i] + vec2[i] + carry[i] >= *base)
      {
        result.push_back(vec1[i] + vec2[i] - *base + carry[i]);
        carry.push_back(1);
      }
      else
      {
        result.push_back(vec1[i] + vec2[i] + carry[i]);
        carry.push_back(0);
      }
        
    }
    if (i >= min(vec1.size(), vec2.size()))
    {
      if (vec1.size() < vec2.size())
      {
        if (vec2[i] + carry[i] >= *base)
        {
          result.push_back(vec2[i] - *base + carry[i]);
          carry.push_back(1);
        }
        else
        {
          result.push_back(vec2[i] + carry[i]);
          carry.push_back(0);
        }		
      }
      else
      {
        if (vec1[i] + carry[i] >= *base)
        {
          result.push_back(vec1[i] - *base + carry[i]);
          carry.push_back(1);
        }
        else
        {
          result.push_back(vec1[i] + carry[i]);
          carry.push_back(0);
        }	
      }
    }
  }
  if (carry.back() != 0)
    result.push_back(carry.back());
  reverse(result.begin(), result.end());
  return result;
}


vector<int> school_type_subtraction(vector<int> vec1, vector<int> vec2, int *base)
{
  vector<int> carry, result;
  reverse(vec1.begin(), vec1.end());
  reverse(vec2.begin(), vec2.end());
  carry.push_back(0);
  for (int i = 0;i < max(vec1.size(), vec2.size());i++)
  {
    //In the bounders of vector
    if (i < min(vec1.size(), vec2.size()))
    {

      if (vec1[i] - vec2[i] - carry[i] < 0)
      {
        result.push_back(vec1[i] - vec2[i] + *base - carry[i]);
        carry.push_back(1);
      }
      else
      {
        result.push_back(vec1[i] - vec2[i] - carry[i]);
        carry.push_back(0);
      }

    }
    if (i >= min(vec1.size(), vec2.size()))
    {
      
      if (vec1[i] - carry[i] < 0)
      {
        result.push_back(vec1[i] + *base - carry[i]);
        carry.push_back(1);
      }
      else
      {
        result.push_back(vec1[i] - carry[i]);
        carry.push_back(0);
      }
    }
  }
  reverse(result.begin(), result.end());
  return result;
}

vector<int> school_type_Multiplication(vector<int> vec1, vector<int> vec2, int *base)
{
  vector<int> carry, result,temp;
  reverse(vec1.begin(), vec1.end());
  reverse(vec2.begin(), vec2.end());
  
  if (vec2.size() > vec1.size())
  {
    vec1.swap(vec2);
  }
  for (int i = 0; i < vec2.size();i++)
  {
    carry.clear();
    carry.push_back(0);
    for (int j = 0; j < vec1.size();j++)
    {
      if (vec1[j] * vec2[i] + carry[j] >= *base)
      {
        temp.push_back((vec1[j] * vec2[i] + carry[j]) % *base);
        carry.push_back((vec1[j] * vec2[i] + carry[j] - ((vec1[j] * vec2[i] + carry[j]) % *base)) / *base);
      }
      else
      {
        temp.push_back(vec1[j] * vec2[i] + carry[j]);
        carry.push_back(0);
      }
    }
    if (carry.back() != 0)
      temp.push_back(carry.back());
    for (int k = 0; k < i;k++)
    {
      temp.insert(temp.begin(), 0);	
    }
    reverse(temp.begin(), temp.end());
    result = school_type_addition(temp, result, base);
    temp.clear();
  }
  return result;
}

int vector_to_int(vector<int> vec, int *base)
{
  // square root of 2147483647 (size of int)
  if (vec.size() > 46340)
    return -1;
  int intvalue = 0;
  for (int i = 0;i < vec.size();i++)
  {
    intvalue = intvalue + int(pow(*base, (vec.size() - 1 - i)))*vec[i];
  }
}

vector<int> int_to_vector(int value, int *base)
{
  vector<int> vec;
  for (; value > 0; value /= *base)
    vec.push_back(value % *base);
  reverse(vec.begin(), vec.end());
  return vec;
}

vector<int> Karatsuba_Multiplication(vector<int> vec1, vector<int> vec2,int *base)
{
  vector<int> a1,a0,b1,b0,c0,c1,c2,d;
  int n = max(vec1.size(),vec2.size());

  if (n = 1)
  {
    return school_type_Multiplication(vec1, vec2, base);
  }

  //Separation point
  if (n % 2 == 1)
  {
    n = (n + 1) / 2;
  }
  else
    n = n / 2;

  //Vec1 Separation
  if (vec1.size() <= n)
  {
    a1.push_back(0);
    a0 = vec1;
  }
  else
  {
    for (int i = 0;i < vec1.size() - n;i++)
    {
      a1.push_back(vec1[i]);
      reverse(a1.begin(), a1.end());
    }
    for (int i = vec1.size();i < n;i++)
    {
      a0.push_back(vec1[i]);
      reverse(a0.begin(), a0.end());
    }
  }
  //Vec2 Separation
  if (vec2.size() <= n)
  {
    b1.push_back(0);
    b0 = vec2;
  }
  else
  {
    for (int i = 0;i < vec2.size() - n;i++)
    {
      b1.push_back(vec2[i]);
      reverse(b1.begin(), b1.end());
    }
    for (int i = vec2.size();i < n;i++)
    {
      b0.push_back(vec2[i]);
      reverse(b0.begin(), b0.end());
    }
  }

  c0 = school_type_Multiplication(a0, b0, base);
  c1 = school_type_Multiplication(a1, b1, base);
  c2 = school_type_Multiplication(school_type_subtraction(a0, a1, base), school_type_subtraction(b0, b1, base), base);

  //(c1*base^2n)+((c1-c2-c0)*base^(n))+(c0)
  return school_type_addition(school_type_Multiplication(school_type_subtraction(school_type_addition(school_type_addition(school_type_Multiplication(c1, int_to_vector(int(pow(*base, 2 * n)), base), base), c0, base), c1, base), c2, base), int_to_vector(int(pow(*base, n)), base), base), c0, base);
  
}

int main()
{
  int base, numbers = 0;
  vector<int> digit_1, digit_2;
  string input, temp;

  getline(cin, temp);
  stringstream ss(temp);
  while (ss >> input)
  {
    //I1 into vector
    if (numbers == 0)
    {
      
      if (input.size() > 100)
      {
        //digits over 100
        cout << "Error. Over 100 digits." << endl;
        exit(1);
      }
      
      for (int i = 0; i < input.size(); i++)
      {
        digit_1.push_back(input[i] - 48);
      }
    }

    //I2 into vector
    if (numbers == 1)
    {
      
      if (input.size() > 100)
      {
        //digits over 100
        cout << "Error. Over 100 digits." << endl;
        exit(1);
      }
      
      for (int i = 0; i < input.size(); i++)
      {
        digit_2.push_back(input[i] - '0');
      }
    }

    //Base
    if (numbers == 2)
    {
      istringstream buffer(input);
      buffer >> base;

      //Error check
      
      if (base > 10 || base < 2)
      {
        cout << "Error. Base should be between 2 and 10." << endl;
        exit(1);
      }
      
    }

    //loop progrress
    numbers++;
  }

  if (numbers != 3)
  {
    cout << "Error. You should input 3 digits." << endl;
    exit(1);
  }
  printvector(school_type_addition(digit_1, digit_2, &base));
  cout << " ";
  printvector(Karatsuba_Multiplication(digit_1, digit_2, &base));
    cout << " ";
    cout << "0";
  //DEBUG
  /*
  cout << endl;
  cout << "Karatsuba_Multiplication:" << endl;
  printvector(Karatsuba_Multiplication(digit_1, digit_2, &base));
  cout << "------------CROSS CHECK------------" << endl;
  cout << "School_Type_Multiplication:" << endl;
  printvector(school_type_Multiplication(digit_1, digit_2, &base));
  */
  return 0;
}


/*
References:
http://www.cplusplus.com/reference/string/stoi/
https://stackoverflow.com/questions/439573/how-to-convert-a-single-char-into-an-int
https://en.wikipedia.org/wiki/Karatsuba_algorithm
http://www.cplusplus.com/reference/vector/vector/swap/
http://media.henanliren.com/upload/pdf/20150227/20153427093441_648.pdf
*/

2 thoughts on “Karatsuba Multiplication in C++”

Leave a Reply

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