// // Copyright (c) 2003 by Stephen C. Dewhurst // Permission to use, copy, modify, distribute and sell this software for any purpose is // hereby granted without fee, provided that the above copyright notice appears in all copies // and that both that copyright notice and this permission notice appear in supporting documentation. // The author makes no representations about the suitability of this software for any purpose. // It is provided "as is" without express or implied warranty. // // Unoptimized and optimized typeint multiplication implementations // // Described in CUJ online experts column, April 2003 // #ifndef TYPEINT_MUL_H #define TYPEINT_MUL_H #include "utils.h" #include // // Number of bits // template struct Len { enum { r = Len::R>::r + 1 }; }; template <> struct Len { enum { r = 0 }; }; // // Addition // // Technique: use a compile time switch template struct Add; template struct AddImpl; template struct AddImpl<3,A,B> { // neither A nor B is 0 // Approach: find the sum of more significant bits // then modify to take into account least significant bit pos typedef typename Deref::R DA; typedef typename Deref::R DB; typedef typename Add::R Temp; typedef typename Select< IsConst::r&&IsConst::r, // if both bits are 1 typename Add::R *, // add one to Temp, lower bit is 0 typename Select< IsConst::r||IsConst::r, // else if one bit is 1 Temp *const, // lower bit is one Temp * // else it's 0 >::R >::R R; }; template struct AddImpl<2,A,B> { typedef A R; // A+0=A }; template struct AddImpl<1,A,B> { typedef B R; // 0+B=B }; template struct AddImpl<0,A,B> { typedef char R; // 0+0=0 }; template struct Add { enum { switchval = IsPtr::r*2 + IsPtr::r }; typedef typename AddImpl::R R; }; // // Left shift // template struct LShift { typedef typename LShift::R *R; }; template struct LShift { typedef T R; }; // need to avoid shifting zeros template struct LShift { typedef char R; }; template <> struct LShift { typedef char R; }; // // Right shift // template struct RShift // Note: Deref has no effect if not a ref, so OK if n>#refs { typedef typename Deref::R>::R R; }; template struct RShift { typedef T R; }; // // Translating type-integers that represent small integers to integers // typedef unsigned long Int; const Int IntLen = std::numeric_limits::digits; template struct Value { enum { r = 0 }; }; template struct Value { enum { r = Value::r*2 }; }; template struct Value { enum { r = Value::r*2 + 1 }; }; // // Translating small integers to type-integers // template struct TypeInt { typedef typename Select>1>::R *const, typename TypeInt>1>::R *>::R R; }; template <> struct TypeInt<0> { typedef char R; }; // // Printing big typeints // // Just a handy utility for debugging, and is not more generally useful. // Prints a binary representation, but always prints a leading zero. template struct PrintTypeint; template <> struct PrintTypeint { static void print() { std::cout << '0'; } }; template struct PrintTypeint { static void print() { PrintTypeint::print(); std::cout << '0'; } }; template struct PrintTypeint { static void print() { PrintTypeint::print(); std::cout << '1'; } }; // // Simple and Slow Multiplication // // As noted in the article, this multiplication algorithm is simple, but // has quadratic complexity. template struct Mul; // no definition of primary template struct Mul // A * 0 == 0 { typedef char R; }; template struct Mul // 0 digit, just shift: A*B0 == (A*B)<<1 { typedef typename Mul::R *R; }; template struct Mul { // 1 digit, shift and add: A*B1 == ((A*B)<<1)+A typedef typename Mul::R *Tmp; typedef typename Add::R R; }; template struct Mul // for high-order bit, to avoid a char * typeint { typedef A R; }; // // Subtract // // Note: This is unsigned subtraction with the assumption // that A >= B!!! template struct Sub; template struct Sub { typedef typename Sub::R *R; // A1-B1 == (A-B)0 }; template struct Sub { typedef typename Sub::R *const R; // A1-B0 == (A-B)1 }; template struct Sub { typedef typename Sub::R *R; // A0-B0 == (A-B)0 }; template struct Sub { typedef A R; // A-0 == A }; template <> struct Sub { typedef char R; // 0-0 == 0 }; // Borrow scans back to find a 1 bit, zeros it, // and sets all the preceding 0 bits to 1. template struct Borrow; template struct Borrow { // make sure there's no leading 0 typedef typename Select::r,T *,T>::R R; }; template struct Borrow { typedef typename Borrow::R *const R; }; template struct Sub { // Need to borrow. typedef typename Borrow::R T1; typedef typename Sub::R *const R; }; // // Scouts that are used to look ahead for runs // of like bits. // template struct Count0s { enum { r = 0 }; }; template struct Count0s { enum { r = Count0s::r + 1 }; }; template struct Count1s { enum { r = 0 }; }; template struct Count1s { enum { r = Count1s::r + 1 }; }; // // An optimized multiplication algorithm. // The complexity is still quadratic, but the actual // compile time is typically much better than that of // the unoptimized algorithm. // template struct Mulopt; template struct M4; template struct M4<0,n,A,B> { // B has run of 1s typedef typename RShift::R T1; typedef typename Mulopt::R T2; typedef typename LShift::R T3; typedef typename LShift::R Ax2n; typedef typename Sub::R Ax2n_1; typedef typename Add::R R; }; template struct M4 { // B has run of 0s typedef typename RShift::R T1; typedef typename Mulopt::R T2; typedef typename LShift::R R; }; template struct M4<0,0,A,B> { // nothing left of B // specialization of Mulopt assures us no zero args // so we can return 1 here typedef char * const R; }; // M3 counts runs of 0s and 1s and goes to the appropriate place. template struct M3 { enum { ones = Count1s::r, zeros = Count0s::r }; typedef typename M4::R R; }; // M2 switches the args, if necessary, so the longer arg is second arg. template struct M2 { typedef typename M3::R R; }; template struct M2 { typedef typename M3::R R; }; template struct Mulopt { // the second arg should be the larger of the two enum { alen = Len::r, blen = Len::r }; typedef typename M2<(alen<=blen),A,B>::R R; }; // three specializations for zero args template struct Mulopt { typedef char R; }; template struct Mulopt { typedef char R; }; template <> struct Mulopt { typedef char R; }; #endif