From a1997776c93904f0f4d66c1a802cd1429975bcf2 Mon Sep 17 00:00:00 2001 From: Krishna Vedala Date: Sat, 2 May 2020 15:57:12 -0400 Subject: [PATCH] split large_number class to a separate header file --- others/large_factorial.cpp | 114 ++----------- others/large_number.h | 323 +++++++++++++++++++++++++++++++++++++ 2 files changed, 336 insertions(+), 101 deletions(-) create mode 100644 others/large_number.h diff --git a/others/large_factorial.cpp b/others/large_factorial.cpp index 2e7d8bb66..dccdcaeab 100644 --- a/others/large_factorial.cpp +++ b/others/large_factorial.cpp @@ -1,97 +1,10 @@ #include +#include #include -#include +#include "large_number.h" using namespace std; -/** - * Store large unsigned numbers as a C++ vector - * The class provides convenience functions to add a - * digit to the number, perform multiplication of - * large number with long unsigned integers. - **/ -class large_number -{ -public: - large_number() /**< initializer */ - { - _digits.push_back(1); - } - - /** - * add a digit to the large number - **/ - void add_digit(unsigned int value) - { - if (value > 9) - { - fprintf(stderr, "digit > 9!!\n"); - exit(EXIT_FAILURE); - } - - _digits.push_back(value); - } - - /** - * Get number of digits in the number - **/ - const size_t num_digits() const - { - return _digits.size(); - } - - /** - * operator over load to access the - * i^th digit conveniently and also - * assign value to it - **/ - unsigned char &operator[](size_t n) - { - return this->_digits[n]; - } - - /** - * multiply large number with another integer and - * store the result in the same large number - **/ - void multiply(const unsigned long n) - { - size_t i; - unsigned long long carry = 0, temp; - for (i = 0; i < this->num_digits(); i++) - { - temp = (*this)[i] * n; - temp += carry; - if (temp < 10) - carry = 0; - else - { - carry = temp / 10; - temp = temp % 10; - } - (*this)[i] = temp; - } - - while (carry != 0) - { - this->add_digit(carry % 10); - carry /= 10; - } - }; - - /** - * print the large number - **/ - void print() - { - for (size_t i = num_digits(); i > 0; i--) - putchar(_digits[i - 1] + '0'); - }; - -private: - vector _digits; /**< where individual digits are stored */ -}; - bool test1() { cout << "---- Check 1\t"; @@ -99,7 +12,7 @@ bool test1() large_number result; for (i = 2; i <= number; i++) /* Multiply every number from 2 thru N */ - result.multiply(i); + result *= i; const char *known_reslt = "3628800"; @@ -110,12 +23,12 @@ bool test1() return false; } - size_t N = result.num_digits(); + const size_t N = result.num_digits(); for (i = 0; i < N; i++) { - if (known_reslt[i] != (result[N - i - 1] + '0')) + if (known_reslt[i] != result.digit_char(i)) { - cerr << i << "^th digit mismatch! " << known_reslt[i] << " != " << result[N - i - 1] << endl; + cerr << i << "^th digit mismatch! " << known_reslt[i] << " != " << result.digit_char(i) << endl; return false; } } @@ -131,7 +44,7 @@ bool test2() large_number result; for (i = 2; i <= number; i++) /* Multiply every number from 2 thru N */ - result.multiply(i); + result *= i; const char *known_reslt = "93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000"; @@ -142,12 +55,12 @@ bool test2() return false; } - size_t N = result.num_digits(); + const size_t N = result.num_digits(); for (i = 0; i < N; i++) { - if (known_reslt[i] != (result[N - i - 1] + '0')) + if (known_reslt[i] != result.digit_char(i)) { - cerr << i << "^th digit mismatch! " << known_reslt[i] << " != " << result[N - i - 1] << endl; + cerr << i << "^th digit mismatch! " << known_reslt[i] << " != " << result.digit_char(i) << endl; return false; } } @@ -177,19 +90,18 @@ int main(int argc, char *argv[]) auto start_time = chrono::high_resolution_clock::now(); for (i = 2; i <= number; i++) /* Multiply every number from 2 thru N */ - result.multiply(i); + result *= i; auto end_time = chrono::high_resolution_clock::now(); chrono::duration time_taken = end_time - start_time; - cout << number << "! = "; - result.print(); - cout << endl + cout << number << "! = " << result << endl << "Number of digits: " << result.num_digits() << endl << "Time taken: " << time_taken.count() << " s" << endl; test1(); test2(); + result.test(); return 0; } diff --git a/others/large_number.h b/others/large_number.h new file mode 100644 index 000000000..0494a540b --- /dev/null +++ b/others/large_number.h @@ -0,0 +1,323 @@ +/** + * @author Krishna Vedala + * @email krishna (dot) vedala (at) ieee (dot) org + **/ + +#ifndef __LARGE_NUMBER__ +#define __LARGE_NUMBER__ +#include +#include +#include +#include + +/** + * Store large unsigned numbers as a C++ vector + * The class provides convenience functions to add a + * digit to the number, perform multiplication of + * large number with long unsigned integers. + **/ +class large_number +{ +public: + large_number() /**< initializer */ + { + _digits.push_back(1); + } + + large_number(unsigned long n) /**< initializer */ + { + unsigned long carry = n; + do + { + add_digit(carry % 10); + carry /= 10; + } while (carry != 0); + } + + large_number(const large_number &a) /**< initializer */ + { + _digits = a._digits; + } + + large_number(const std::vector &vec) /**< initializer */ + { + _digits = vec; + } + + /** + * Function to check implementation + **/ + static bool test() + { + std::cout << "------ Checking `large_number` class implementations\t" << std::endl; + large_number a(40); + a *= 10; + if (a != large_number(400)) + { + std::cerr << "\tFailed 1/5 (" << a << "!=400)" << std::endl; + return false; + } + std::cout << "\tPassed 1/5..."; + a += 120; + if (a != large_number(520)) + { + std::cerr << "\tFailed 2/5 (" << a << "!=520)" << std::endl; + return false; + } + std::cout << "\tPassed 2/5..."; + a *= 10; + if (a != large_number(5200)) + { + std::cerr << "\tFailed 3/5 (" << a << "!=5200)" << std::endl; + return false; + } + std::cout << "\tPassed 3/5..."; + ++a; + if (a != large_number(5201)) + { + std::cerr << "\tFailed 4/5 (" << a << "!=5201)" << std::endl; + return false; + } + std::cout << "\tPassed 4/5..."; + a++; + if (a != large_number(5202)) + { + std::cerr << "\tFailed 5/5 (" << a << "!=5202)" << std::endl; + return false; + } + std::cout << "\tPassed 5/5..." << std::endl; + return true; + } + + /** + * add a digit at MSB to the large number + **/ + void add_digit(unsigned int value) + { + if (value > 9) + { + std::cerr << "digit > 9!!\n"; + exit(EXIT_FAILURE); + } + + _digits.push_back(value); + } + + /** + * Get number of digits in the number + **/ + const size_t num_digits() const + { + return _digits.size(); + } + + /** + * operator over load to access the + * i^th digit conveniently and also + * assign value to it + **/ + inline unsigned char &operator[](size_t n) + { + return this->_digits[n]; + } + + inline const unsigned char &operator[](size_t n) const + { + return this->_digits[n]; + } + + /** + * operator overload to compare two numbers + **/ + friend std::ostream &operator<<(std::ostream &out, const large_number &a) + { + for (size_t i = a.num_digits(); i > 0; i--) + out << a[i - 1] + '0'; + return out; + } + + /** + * operator overload to compare two numbers + **/ + friend bool operator==(const large_number &a, const large_number b) + { + size_t N = a.num_digits(); + if (N != b.num_digits()) + return false; + for (size_t i = 0; i < N; i++) + if (a[i] != b[i]) + return false; + return true; + } + + /** + * operator overload to compare two numbers + **/ + friend bool operator!=(const large_number &a, const large_number b) + { + return !(a == b); + } + + /** + * operator overload to increment (prefix) + **/ + large_number &operator++() + { + this->add((unsigned int)1); + return *this; + } + + /** + * operator overload to increment (postfix) + **/ + large_number &operator++(int) + { + large_number tmp(_digits); + ++(*this); + return tmp; + } + + /** + * operator overload to add + **/ + template + large_number &operator+=(T &n) + { + if (std::is_same::value) // if adding with another large_number + { + large_number *b = &n; + const size_t max_L = std::max(this->num_digits(), b->num_digits()); + unsigned int carry = 0, temp; + for (size_t i = 0; i < max_L; i++) + { + temp += carry; + if (i < b->num_digits()) + temp += (*b)[i]; + if (i < this->num_digits()) + temp += (*this)[i]; + if (temp < 10) + carry = 0; + else + { + carry = temp / 10; + temp = temp % 10; + } + if (i < this->num_digits()) + (*this)[i] = temp; + else + this->add_digit(temp); + } + while (carry != 0) + { + if (i < this->num_digits()) + (*this)[i] = carry % 10; + else + this->add_digit(carry % 10); + carry /= 10; + } + + return *this; + } + + static_assert(std::is_integral::value, "Must be integer addition unsigned integer types."); + // typedef typename std::make_unsigned::type T2; + this->add(b); + return *this; + } + + // /** + // * operator overload to increment + // **/ + // friend large_number &operator+(large_number a, const large_number b) + // { + // const size_t max_L = std::max(a.num_digits(), b.num_digits()); + + // return a; + // } + + /** + * operator overload to increment + **/ + template + large_number &operator*=(const T n) + { + static_assert(std::is_integral::value, "Must be integer addition unsigned integer types."); + this->multiply(n); + return *this; + } + + /** + * returns i^th digit as an ASCII character + **/ + const char digit_char(size_t i) const + { + return _digits[num_digits() - i - 1] + '0'; + } + +private: + /** + * multiply large number with another integer and + * store the result in the same large number + **/ + template + void multiply(const T n) + { + static_assert(std::is_integral::value, "Can only have integer types."); + // assert(!(std::is_signed::value)); //, "Implemented only for unsigned integer types."); + + size_t i; + unsigned long long carry = 0, temp; + for (i = 0; i < this->num_digits(); i++) + { + temp = (*this)[i] * n; + temp += carry; + if (temp < 10) + carry = 0; + else + { + carry = temp / 10; + temp = temp % 10; + } + (*this)[i] = temp; + } + + while (carry != 0) + { + this->add_digit(carry % 10); + carry /= 10; + } + }; + + /** + * add large number with another integer and + * store the result in the same large number + **/ + template + void add(const T n) + { + static_assert(std::is_integral::value, "Can only have integer types."); + // static_assert(!(std::is_signed::value), "Implemented only for unsigned integer types."); + + size_t i = 0; + long long carry = n; + + while (carry != 0) + { + if (i < this->num_digits()) + { + carry += (*this)[i]; + (*this)[i] = carry % 10; + i++; + } + else + this->add_digit(carry % 10); + carry /= 10; + if (carry == 0) + return; + } + }; + + std::vector _digits; /**< where individual digits are stored */ +}; + +#endif