@@ -552,6 +552,11 @@ namespace com::saxbophone::arby {
552552 _digits = product._digits ;
553553 return *this ; // return the result by reference
554554 }
555+ private: // private helper methods for multiplication operator
556+ constexpr bool is_power_of_2 () const {
557+ return *this == Nat (1 ) << (bit_length () - 1 );
558+ }
559+ public:
555560 /* *
556561 * @brief Multiplication operator for Nat
557562 * @param lhs,rhs operands for the multiplication
@@ -562,35 +567,43 @@ namespace com::saxbophone::arby {
562567 // init product to zero
563568 Nat product;
564569 // either operand being zero always results in zero, so only run the algorithm if they're both non-zero
565- if (not (lhs._digits .front () == 0 or rhs._digits .front () == 0 )) {
566- // multiply each digit from lhs with each digit from rhs
567- std::size_t l = 0 ; // manual indices to track which digit we are on,
568- std::size_t r = 0 ; // as codlili's iterators are not random-access
569- for (auto lhs_digit : lhs._digits ) {
570- // reset r index as it cycles through multiple times
571- r = 0 ;
572- for (auto rhs_digit : rhs._digits ) {
573- // cast lhs to OverflowType to make sure both operands get promoted to avoid wrap-around overflow
574- OverflowType multiplication = (OverflowType)lhs_digit * rhs_digit;
575- // create a new Nat with this intermediate result and add trailing places as needed
576- Nat intermediate = multiplication;
577- // we need to remap the indices as the digits are stored big-endian
578- std::size_t shift_amount = (lhs._digits .size () - 1 - l) + (rhs._digits .size () - 1 - r);
579- // add that many trailing zeroes to intermediate's digits
580- intermediate._digits .push_back (shift_amount, 0 );
581- // finally, add it to lhs as an accumulator
582- product += intermediate;
583- // increment manual indices
584- r++;
585- }
586- l++;
570+ if (lhs._digits .front () == 0 or rhs._digits .front () == 0 ) {
571+ return product;
572+ }
573+ // optimisation using bitshifting when multiplying by binary powers
574+ if (rhs.is_power_of_2 ()) {
575+ return lhs << (rhs.bit_length () - 1 );
576+ } else if (lhs.is_power_of_2 ()) {
577+ return rhs * lhs;
578+ }
579+ // multiply each digit from lhs with each digit from rhs
580+ std::size_t l = 0 ; // manual indices to track which digit we are on,
581+ std::size_t r = 0 ; // as codlili's iterators are not random-access
582+ for (auto lhs_digit : lhs._digits ) {
583+ // reset r index as it cycles through multiple times
584+ r = 0 ;
585+ for (auto rhs_digit : rhs._digits ) {
586+ // cast lhs to OverflowType to make sure both operands get promoted to avoid wrap-around overflow
587+ OverflowType multiplication = (OverflowType)lhs_digit * rhs_digit;
588+ // create a new Nat with this intermediate result and add trailing places as needed
589+ Nat intermediate = multiplication;
590+ // we need to remap the indices as the digits are stored big-endian
591+ std::size_t shift_amount = (lhs._digits .size () - 1 - l) + (rhs._digits .size () - 1 - r);
592+ // add that many trailing zeroes to intermediate's digits
593+ intermediate._digits .push_back (shift_amount, 0 );
594+ // finally, add it to lhs as an accumulator
595+ product += intermediate;
596+ // increment manual indices
597+ r++;
587598 }
599+ l++;
588600 }
589601 product._validate_digits ();
590602 return product;
591603 }
592604 private: // private helper methods for Nat::divmod()
593605 // function that shifts up rhs to be just big enough to be smaller than lhs
606+ // TODO: rewrite this to use bit-shifting for speed
594607 static constexpr Nat get_max_shift (const Nat& lhs, const Nat& rhs) {
595608 // how many places can we shift rhs left until it's the same width as lhs?
596609 std::size_t wiggle_room = lhs._digits .size () - rhs._digits .size ();
@@ -638,6 +651,18 @@ namespace com::saxbophone::arby {
638651 if (rhs._digits .front () == 0 ) {
639652 throw std::domain_error (" division by zero" );
640653 }
654+ if (lhs._digits .front () == 0 ) { return {lhs, lhs}; } // zero shortcut
655+ // optimisation using bitshifting when dividing by binary powers
656+ if (rhs.is_power_of_2 ()) {
657+ auto width = rhs.bit_length ();
658+ // the remainder is the digits that are shifted out, so bitmask for them
659+ auto bitmask = (Nat (1 ) << (width - 1 )) - 1 ;
660+ Nat quotient = lhs >> (width - 1 );
661+ Nat remainder = lhs & bitmask;
662+ quotient._validate_digits ();
663+ remainder._validate_digits ();
664+ return {quotient, remainder};
665+ }
641666 // this will gradually accumulate the calculated quotient
642667 Nat quotient;
643668 // this will gradually decrement with each subtraction
@@ -912,7 +937,9 @@ namespace com::saxbophone::arby {
912937 if (_digits.empty ()) {
913938 _digits = {0 };
914939 }
915- _validate_digits (); // TODO: remove when satisfied not required
940+ // needed in some cases, probably when the intial whole-digit shift leaves a small value which then turns 0
941+ _remove_leading_zeroes ();
942+ _validate_digits ();
916943 return *this ;
917944 }
918945 /* *
0 commit comments