// Semantics Consulting's Tyr Library // http://www.semantics.org // // 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. // "typeint" implementation // // Described in Typeints. C/C++ Users Journal Experts Forum, 21, 2 (February 2003) #ifndef TYPEINT_H #define TYPEINT_H #include "utils.h" #include #include namespace Tyr { // // 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; }; // // Subtract 1 // template struct Sub1; template struct Sub1 { typedef T *R; }; template struct Sub1 { typedef typename Sub1::R *const R; }; template <> struct Sub1 { typedef char 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; }; // // 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; }; // // Bitwise Or // // Technique: use exhaustive specialization instead of switch. template struct Or { typedef char R; }; template struct Or { typedef typename Or::R *R; }; template struct Or { typedef typename Or::R *const R; }; template struct Or { typedef typename Or::R *const R; }; template struct Or { typedef typename Or::R *const R; }; template struct Or { typedef B *R; }; template struct Or { typedef B *const R; }; template struct Or { typedef A *R; }; template struct Or { typedef A *const R; }; // // Bitwise And // // Technique: use exhaustive specialization instead of switch. template struct And // at least one of A or B is 0 { typedef char R; }; template struct And { typedef typename And::R *R; }; template struct And { typedef typename And::R *R; }; template struct And { typedef typename And::R *R; }; template struct And { typedef typename And::R *const R; }; // // Bitwise Xor // // Technique: use exhaustive specialization instead of switch. template struct Xor { typedef char R; }; template struct Xor { typedef typename Xor::R *R; }; template struct Xor { typedef typename Xor::R *const R; }; template struct Xor { typedef typename Xor::R *const R; }; template struct Xor { typedef typename Xor::R *R; }; template struct Xor { typedef B *R; }; template struct Xor { typedef B *const R; }; template struct Xor { typedef A *R; }; template struct Xor { typedef A *const R; }; // // Bitwise not (~) // // The semantics of this bitwise not are a little unconventional, // since inversion doesn't make a lot of sense in a variable length // integer such as a typeint. For example, applying Not to a typeint // of a given length always results in a shorter typeint. (Since this // typeint representation has no leading zeros.) Therefore inverting a // typint twice will not restore the original value. Repeated application // of Not will produce a zero (char) result. (This might be useful as a means // of counting runs of ones and zeros in the binary analog of a particular typeint.) // Note: for those who care about such things, the above is what's // known as (or should be known as) a rhophalic comment. template struct Not { typedef char R; }; template struct Not { typedef typename Not::R *const R; }; template struct Not { typedef typename Not::R Temp; typedef typename Select::r, Temp *, char >::R R; }; // // Comparing typeints. // // Technique: Using the primary template to catch meaningless expansions, // to avoid having to go indirect with implementation of a compile time if. template struct Comp { enum { // These results mean nothing, because Comp compares only // equal-length typeints, and the specializations already // handle all legal possibilities. // This will be instantiated, but the results will never be used. ls = 0xDEAD, eq = 0xBEEF }; }; template <> struct Comp { enum { ls = false, eq = true }; }; template struct Comp { enum { ls = Comp::ls, eq = Comp::eq }; }; template struct Comp { enum { ls = Comp::ls || Comp::eq, eq = false }; }; template struct Comp { enum { ls = Comp::ls, eq = false }; }; template struct Comp { enum { ls = Comp::ls, eq = Comp::eq }; }; template struct Less { enum { r = (Len::r < Len::r) || ((Len::r == Len::r) && Comp::ls) }; }; template struct Equal { enum { r = Len::r == Len::r && Comp::eq }; }; template struct Greater { enum { r = Less::r }; }; template struct GreaterEqual { enum { r = !Less::r }; }; template struct LessEqual { enum { r = !Greater::r }; }; template struct NotEqual { enum { r = !Equal::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; }; // // A version for pasting together integers into large typeints // template struct TypeIntN { typedef typename TypeInt::R N8; typedef typename TypeInt::R N7; typedef typename TypeInt::R N6; typedef typename TypeInt::R N5; typedef typename TypeInt::R N4; typedef typename TypeInt::R N3; typedef typename TypeInt::R N2; typedef typename TypeInt::R N1; typedef typename Add::R, N7>::R N87; typedef typename Add::R, N6>::R N876; typedef typename Add::R, N5>::R N8765; typedef typename Add::R, N4>::R N87654; typedef typename Add::R, N3>::R N876543; typedef typename Add::R, N2>::R N8765432; typedef typename Add::R, N1>::R N87654321; typedef N87654321 R; }; template struct TypeInt1 { typedef typename TypeIntN<0,0,0,0,0,0,0,n1>::R R; }; template struct TypeInt2 { typedef typename TypeIntN<0,0,0,0,0,0,n2,n1>::R R; }; template struct TypeInt3 { typedef typename TypeIntN<0,0,0,0,0,n3,n2,n1>::R R; }; template struct TypeInt4 { typedef typename TypeIntN<0,0,0,0,n4,n3,n2,n1>::R R; }; template struct TypeInt5 { typedef typename TypeIntN<0,0,0,n5,n4,n3,n2,n1>::R R; }; template struct TypeInt6 { typedef typename TypeIntN<0,0,n6,n5,n4,n3,n2,n1>::R R; }; template struct TypeInt7 { typedef typename TypeIntN<0,n7,n6,n5,n4,n3,n2,n1>::R R; }; template struct TypeInt8 { typedef typename TypeIntN::R 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'; } }; } // namespace Tyr #endif