Chapter 15. The LINT Public Interface: Members and Friends

Please accept my resignation. I don't want to belong to any club that will accept me as a member.

—Groucho Marx

Every time I paint a portrait I lose a friend

—John Singer Sargent

In addition to the constructor functions and operators already discussed, there exist further LINT functions that make the C functions developed in Part I available to LINT objects. In the following discussion we make a rough separation of the functions into the categories "arithmetic" and "number-theoretic." The implementation of the functions will be discussed together with examples; otherwise, we shall restrict ourselves to a table of information needed for their proper use. We shall give more extensive treatment in the following sections to the functions for the formatted output of LINT objects, for which we shall make use of the properties of the stream classes contained in the C++ standard library. Possible applications, in particular for formatted output of objects of user-defined classes, are given rather short shrift in many C++ textbooks, and we are going to take the opportunity to explicate the construction of the functions needed to output our LINT objects.

Arithmetic

The following member functions implement the fundamental arithmetic operations as well as modular operations for calculation in residue class rings over the integers as accumulator operations: The object to which a called function belongs contains the function result as implicit argument after its termination. Accumulator functions are efficient, since they operate to the greatest extent without internal auxiliary objects and thus save unnecessary assignments and calls to constructors.

For the cases in which a free assignment of the results of calculations is unavoidable, or in which the automatic overwriting of the implicit argument of the member functions with the result is not desired, the member functions were extended by means of like-named analogous friend functions together with additional friend functions. These are not discussed further here, but are recorded in Appendix B. The treatment of possible error situations in LINT functions that can arise from the use of CLINT functions will be discussed in full in Chapter 16.

Before we list the public member functions, we consider first as an example of their implementation the functions

LINT& LINT::mexp (const LINT& e, const LINT& m );

and

LINT& LINT::mexp (USHORT e, const LINT& m);

for exponentiation, an operation for which C++, alas, offers no operator. The functions mexp() were constructed in such a way that the functions used are, according to the type of the operands, the C functions mexpk_l(), mexpkm_l(), umexp_l(), and umexpm_l(), optimized for this purpose (with the corresponding arithmetic friend functions we are likewise dealing with the exponentiation functions wmexp_l() and wmexpm_l() with USHORT base).

Function:

Modular exponentiation with automatic use of Montgomery exponentiation if the modulus is odd.

Syntax:

const LINT&
LINT::mexp (const LINT& e, const LINT& m);

Input:

implicit argument (base) e (exponent) m (modulus)

Return:

pointer to the remainder

Example:

a.mexp (e, m);

const LINT& LINT::mexp (const LINT& e, const LINT& m)
{
  int error;
  if (status == E_LINT_INV) panic (E_LINT_VAL, "mexp", 0, __LINE__);
if (status == E_LINT_INV) panic (E_LINT_VAL, "mexp", 1, __LINE__);
  if (status == E_LINT_INV) panic (E_LINT_VAL, "mexp", 2, __LINE__);

  err = mexp_l (n_l, e.n_l, n_l, m.n_l);
  /* mexp_l() uses mexpk_l() or mexpkm_l() */
  switch (error)
    {
      case 0:
        status = E_LINT_OK;
        break;
      case E_CLINT_DBZ:
        panic (E_LINT_DBZ, "mexp", 2, __LINE__);
        break;
      default:
        panic (E_LINT_ERR, "mexp", error, __LINE__);
    }
  return *this;
}

Function:

Modular exponentiation

Syntax:

const LINT&
LINT::mexp (USHORT e, const LINT& m);

Example:

a.mexp (e, m);

const LINT& LINT::mexp (USHORT e, const LINT& m)
{
  int err;
  if (status == E_LINT_INV) panic (E_LINT_VAL, "mexp", 0, __LINE__);
  if (status == E_LINT_INV) panic (E_LINT_VAL, "mexp", 1, __LINE__);

  err = umexp_l (n_l, e, n_l, m.n_l);

  switch (err)
    {
      // Code as above with mexp (const LINT& e, const LINT& m)
    }
  return *this;
}

We now present a collection of additional arithmetic and number-theoretic member functions.

Function:

addition

Syntax:

const LINT&
LINT::add(const LINT& s);

Input:

implicit argument (summand) s (summand)

Return:

pointer to the sum

Example:

a.add (s); executes the operation a += s;

Function:

subtraction

Syntax:

const LINT&
LINT::sub (const LINT& s);

Input:

implicit argument (minuend) s (subtrahend)

Return:

pointer to the difference

Example:

a.sub (s); executes the operation a -= s;

Function:

multiplication

Syntax:

const LINT&
LINT::mul (const LINT& s);

Input:

implicit argument (factor) s (factor)

Return:

pointer to the product

Example:

a.mul (s); executes the operation a *= s;

Function:

squaring

Syntax:

const LINT&
LINT::sqr (void);

Input:

implicit argument (factor)

Return:

pointer to the implicit argument, which contains the square

Example:

a.sqr (); executes the operation a *= a;

Function:

division with remainder

Syntax:

const LINT&
LINT::divr(const LINT& d, LINT& r);

Input:

implicit argument (dividend) d (divisor)

Output

r (remainder of the division modulo d)

Return:

pointer to the implicit argument, which contains the quotient

Example:

a.divr (d, r); executes the operation a /= d; r = a % d;

Function:

residue

Syntax:

const LINT&
LINT::mod(const LINT& d);

Input:

implicit argument (dividend) d (divisor)

Return:

pointer to the implicit argument, which contains the remainder of the division modulo d

Example:

a.mod (d); executes the operation a %= d;

Function:

residue modulo a power of 2

Syntax:

const LINT&
LINT::mod2 (USHORT e)

Input:

implicit argument (dividend) e (exponent of the power of 2 divisor)

Return:

pointer to the implicit argument, which contains the remainder of the division modulo 2e

Example:

a.mod 2(e); executes the operation a %= d;, where d =2e

Note:

mod2 cannot be created by overloading the previously presented function mod(), since mod() also accepts a USHORT argument, which is changed automatically into a LINT object by means of the appropriate constructor. Since it cannot be determined from the arguments which function is meant, mod2() is given its own name.

Function:

test for equality modulo m

Syntax:

int
LINT::mequ (const LINT& b, const LINT& m);

Input:

implicit argument a second argument b modulus m

Return:

1 if a ≡ b mod m, 0 otherwise

Example:

if (a.mequ (b, m)) // ...

Function:

modular addition

Syntax:

const LINT&
LINT::madd(const LINT& s, const LINT& m);

Input:

implicit argument (summand) s (summand) m (modulus)

Return:

pointer to the implicit argument, which contains the sum modulo m

Example:

a.madd (s, m);

Function:

modular subtraction

Syntax:

const LINT& LINT::msub(const LINT& s,
const LINT& m);

Input:

implicit argument (minuend) s (subtrahend) m (modulus)

Return:

pointer to the implicit argument, which contains the difference modulo m

Example:

a.msub (s, m);

Function:

modular multiplication

Syntax:

const LINT& LINT::mmul (const LINT& s,
const LINT& m);

Input:

implicit argument (factor) s (factor) m (modulus)

Return:

pointer to the implicit argument, which contains the product modulo m

Example:

a.mmul (s, m);

Function:

modular squaring

Syntax:

const LINT& LINT::msqr (const LINT& m);

Input:

implicit argument (factor) m (modulus)

Return:

pointer to the implicit argument, which contains the square modulo m

Example:

a.msqr (m);

Function:

modular exponentiation with exponent a power of 2

Syntax:

const LINT& LINT::mexp2 (USHORT e,
const LINT& m);

Input:

implicit argument (base) e (power to which 2 is to be raised) m (modulus)

Return:

pointer to the implicit argument, which contains the power modulo m

Example:

a.mexp2 (e, m);

Function:

modular exponentiation (2k-ary method, Montgomery reduction)

Syntax:

const LINT& LINT::mexpkm (const LINT& e,
const LINT& m);

Input:

implicit argument (base) e (exponent) m (odd modulus)

Return:

pointer to the implicit argument, which contains the power modulo m

Example:

a.mexpkm (e, m);

Function:

modular exponentiation (25-ary method, Montgomery reduction)

Syntax:

const LINT& LINT::mexp5m (const LINT& e,
const LINT& m);

Input:

implicit argument (base) e (exponent) m (odd modulus)

Return:

pointer to the implicit argument, which contains the power modulo m

Example:

a.mexp5m (e, m);

Function:

left/right shift

Syntax:

const LINT& LINT::shift (int noofbits);

Input:

implicit argument (multiplicand/dividend) (+/-) noofbits (number of bit positions to be shifted)

Return:

pointer to the implicit argument, which contains the result of the shift operation

Example:

a.shift (512); executes the operation a <<= 512;

Function:

test for divisibility by 2 of a LINT object

Syntax:

int
LINT::iseven (void);

Input:

test candidate a as implicit argument

Return:

1 if a is odd, 0 otherwise

Example:

if(a.iseven ()) //...

Function:

set a binary digit of a LINT object to 1

Syntax:

const LINT&
LINT::setbit (unsigned int pos);

Input:

implicit argument a position pos of the bit to be set (counted from 0)

Return:

pointer to a with the set bit at position pos

Example:

a.setbit (512);

Function:

test a binary digit of a LINT object

Syntax:

int
LINT::testbit (unsigned int pos);

Input:

implicit argument a position pos of the bit to be tested (counted from 0)

Return:

1 if the bit at position pos is set, 0 otherwise

Example:

if(a.testbit (512)) // ...

Function:

set a binary digit of a LINT object to 0

Syntax:

const LINT&
LINT::clearbit (unsigned int pos);

Input:

implicit argument a position pos of the bit to be cleared (counted from 0)

Return:

pointer to a with the cleared bit at position pos

Example:

a.clearbit (512);

Function:

exchange values of two LINT objects

Syntax:

const LINT&
LINT::fswap (LINT& b);

Input:

implicit argument a position b (the value to be swapped for a)

Return:

pointer to the implicit argument with the value b

Example:

a.fswap (b); exchanges the values a and b

Number Theory

In contrast to the arithmetic functions, the following number-theoretic member functions do not overwrite the implicit first argument with the result. The reason for this is that with more complex functions it has been shown in practice not to be practical to overwrite, as is the case with simple arithmetic functions. The results of the following functions are thus returned as values rather than as pointers.

Function:

calculate the greatest integer less than or equal to the base-2 logarithm of a LINT object

Syntax:

unsigned int
LINT::ld (void);

Input:

implicit argument a

Return:

integer part of the base-2 logarithm of a

Example:

i = a.ld ();

Function:

calculate the greatest common divisor of two LINT objects

Syntax:

LINT
LINT::gcd (const LINT& b);

Input:

implicit argument a second argument b

Return:

gcd (a, b) of the input values

Example:

c = a.gcd (b);

Function:

calculate the multiplicative inverse modulo n

Syntax:

LINT
LINT::inv (const LINT& n);

Input:

implicit argument a modulus n

Return:

multiplicative inverse of a modulo n (if the result is equal to zero, then gcd(a, n) > 1 and the inverse does not exist)

Example:

c = a.inv (n);

Function:

calculate the greatest common divisor of a and b as well as its representation g = ua+vb as a linear combination of a and b

Syntax:

LINT
LINT::xgcd(const LINT& b,
               LINT& u, int& sign_u,
               LINT& v, int& sign_v);

Input:

implicit argument a, second argument b

Output:

Factor u of the representation of gcd (a, b) sign of u in sign_u

factor v of the representation of gcd (a, b) sign of v in sign_v

Return:

gcd(a, b) of the input values

Example:

g = a.xgcd (b, u, sign_u, v, sign_v);

Function:

calculate the least common multiple (lcm) of two LINT objects

Syntax:

LINT
LINT::lcm (const LINT& b);

Input:

implicit argument a factor b

Return:

lcm(a, b) of the input values

Example:

c = a.lcm (b);

Function:

solution of a system of linear congruences xa mod m, xb mod n,

Syntax:

LINT
LINT::chinrem(const LINT& m, const LINT& b,
               const LINT& n);

Input:

implicit argument a, modulus m, argument b, modulus n

Return:

solution x of the congruence system if all is ok (Get_Warning_Status() == E_LINT_ERR indicates that an overflow has occurred or that the congruences have no common solution)

Example:

x = a.chinrem (m, b, n);

The friend function chinrem(int noofeq, LINT** coeff) accepts a vector coeff of pointers to LINT objects, which are passed as coefficients a1,m1,a2,m2,a3,m3,... of a system of linear congruences with "arbitrarily" many equations xai mod mi, i = 1,..., noofeq (see Appendix B).

Function:

calculation of the Jacobi symbol of two LINT objects

Syntax:

int
LINT::jacobi (const LINT& b);

Input:

implicit argument a, second argument b

Return:

Jacobi symbol of the input values

Example:

i = a.jacobi (b);

Function:

calculation of the integer part of the square root of a LINT object

Syntax:

LINT
LINT::introot (void);

Input:

implicit argument a

Return:

integer part of the square root of the input value

Example:

c = a.root ();

Function:

calculation of the integer part of the bth root of a LINT object

Syntax:

LINT
LINT::introot (const USHORT b);

Input:

implicit argument a, root exponent b

Return:

integer part of the bth root of the input value

Example:

c = a.root (b);

Function:

calculation of the square root modulo a prime p of a LINT object

Syntax:

LINT
LINT::root (const LINT& p);

Input:

implicit argument a, prime modulus p > 2

Return:

square root of a if a is a quadratic residue modulo p otherwise 0 (Get_Warning_Status() == E_LINT_ERR indicates that a is not a quadratic residue modulo p)

Example:

c = a.root (p);

Function:

calculation of the square root of a LINT object modulo a prime product p • q

Syntax:

LINT
LINT::root (const LINT& p, const LINT& q);

Input:

implicit argument a prime modulus p > 2, prime modulus q > 2

Return:

square root of a if a is a quadratic residue modulo pq otherwise 0 (Get_Warning_Status() == E_LINT_ERR indicates that a is not a quadratic residue modulo p*q)

Example:

c = a.root (p, q);

Function:

test of whether a LINT object is a square

Syntax:

int
LINT::issqr(void);,

Input:

test candidate a as implicit argument

Return:

square root of a if a is a square otherwise 0 if a == 0 or a not a square

Example:

if(0 == (r = a.issqr ())) // ...

Function:

probabilistic primality test of a LINT object

Syntax:

int
LINT::isprime (int nsp, int rnds);

Input:

test candidate p as implicit argument nsp (number of primes for the division test; default is 302)

rnds (number of passes through test; default is zero for automatic optimization via the function prime_l())

Return:

1 if p is a "probable" prime 0 otherwise

Example:

if(p.isprime ()) // ...

Function:

calculate the two-part of a LINT object

Syntax:

int
LINT::twofact (LINT& b);

Input:

implicit argument a

Output:

b (odd part of a)

Return:

exponent of the even part of a

Example:

e = a.twofact (b);

Stream I/O of LINT Objects

The classes contained in the C++ standard library such as istream and ostream are abstractions of input and output devices derived from the base class ios. The class iostream is in turn derived from istream and ostream, and it enables both writing and reading of its objects.[47] Input and output take place with the help of the insert and extract operators "<<" and ">>" (cf. [Teal], Chapter 8). These arise through overloading the shift operators, for example in the form

ostream& ostream::operator<< (int i);
istream& istream::operator>> (int& i);

in which they enable output, respectively input, of integer values through expressions of the form

cout << i;
cin >> i;

As special objects of the classes ostream and istream, cout and cin represent the same abstract files as the objects stdout and stdin of the standard C library.

The use of the stream operators "<<" and ">>" for input and output makes it unnecessary to consider particular properties of the hardware in use. In and of itself this is nothing new, for the C function printf() behaves the same way: A printf() instruction should always, regardless of platform, lead to the same result. However, above and beyond the altered syntax, which is oriented to the metaphorical image of the insertion of objects into a stream, the advantages of the C++ implementation of streams lie in the strict type checking, which in the case of printf() is possible only to a limited degree, and in its extensibility. In particular, we make use of the latter property by overloading the insert and extract operators so that they support input and output of LINT objects. To this end the class LINT defines the following stream operators:

friend ostream& operator<< (ostream& s, const LINT& ln);
friend fstream& operator<< (fstream& s, const LINT& ln);
friend ofstream& operator<< (ofstream& s, const LINT& ln);
friend fstream& operator>> (fstream& s, LINT& ln);
friend ifstream& operator>> (ifstream& s, LINT& ln);

A simple formulation of the overloaded insert operators for the output of LINT objects might look something like the following:

#include <iostream.h>

ostream& operator<< (ostream& s, const LINT& ln)
{
  if (ln.status == E_LINT_INV)
    LINT::panic (E_LINT_VAL, "ostream operator <<", 0, __LINE__);

  s << xclint2str (ln.n_l, 16, 0) << endl;
  s << ld (ln) << " bit" << endl;
  return s;
}

The operator <<thus defined outputs the digits of a LINT object as hexadecimal values and adds the binary length of the number in a separate line. In the next section we shall consider the possibilities of improving the appearance of the output of LINT objects with the aid of formatting functions, and we shall also use manipulators to make the output customizable.

Formatted Output of LINT Objects

In this section we shall make use of the base class ios of the C++ standard library and of its member functions to define our own LINT-specific formatting functions for the purpose of controlling the output format of LINT objects. Furthermore, we shall create manipulators that will make the customization of the output format for LINT objects as simple as it is for the standard types defined in C++.

The crucial point in the creation of formatted output of LINT objects is the possibility of setting formatting specifications that will be handled by the insert operator. To this end we shall consider the mechanism provided for the class ios (for details see [Teal], Chapter 6, and [Pla2], Chapter 6), whose member function xalloc() in the objects of the classes derived from ios allocates a status variable of type long and returns an index to this status variable also of type long. We store this index in the long variable flagsindex. By means of this index the member function ios::iword() can be used to access reading and writing to the allocated status variable (cf. [Pla2], page 125).

To ensure that this takes place before a LINT object is output, we define, in the file flintpp.h, the class LintInit as follows:

class LintInit
  {
    public:
      LintInit (void);
  };
  LintInit::LintInit (void)
  {
    // get index to long status variable in class ios
    LINT::flagsindex = ios::xalloc();
    // set the default status in cout and in cerr
    cout.iword (LINT::flagsindex) =
    cerr.iword (LINT::flagsindex) =
      LINT::lintshowlength|LINT::linthex|LINT::lintshowbase;
  }

The class LintInit has as its only element the constructor LintInit::LintInit(). Furthermore, in the class LINT we define a member datum setup of type LintInit, which is initialized via the constructor LintInit::LintInit(). The call to xalloc() takes place within this initialization, and the status variable thereby allocated is given the established standard output format for LINT objects. In the following we shall show a section of the LINT class declaration, which contains the declaration of LintInit() as a friend of LINT, the declaration of the variables flagsindex and setup, and various status values as enum types:

class LINT
  {
    public:
      // ...
      enum {
        lintdec = 0x10,
        lintoct = 0x20,
        linthex = 0x40,
        lintshowbase = 0x80,
        lintuppercase = 0x100,
        lintbin = 0x200,
        lintshowlength = 0x400
      };
      // ...
      friend LintInit::LintInit (void);
// ...
  private:
    // ...
    static long flagsindex;
    static LintInit setup;
    // ...
};

Setting the variable setup as static has the effect that this variable exists only once for all LINT objects and thus the associated constructor LintInit() is called only once.

We would like now to pause for a moment and consider what all this effort nets us. Setting the output format could just as well be managed via a status variable, which as a member of LINT would be much simpler to deal with. The decisive advantage of the method that we have chosen is that the output format can be set for each output stream separately and independently of the others (cf. [Pla2], page 125), which could not be accomplished with an internal LINT status variable. This is done through the power of the class ios, whose mechanisms we employ for such purposes.

Now that the preliminaries have been taken care of, we can define the status functions as member functions of LINT. These are displayed in Table 15-1.

We shall consider as an example of the implementation of the status functions the function LINT::setf(), which returns the current value of the status variable as a long with reference to an output stream:

long LINT::setf (ostream& s, long flag)
  {
  long t = s.iword (flagsindex);
  // the flags for the basis of the numerical representation
  // are mutually exclusive
  if (flag & LINT::lintdec)
    {
    s.iword (flagsindex) = (t & ~LINT::linthex & ~LINT::lintoct
                & ~LINT::lintbin) | LINT::lintdec;
    flag ^= LINT::lintdec;
    }

  if (flag & LINT::linthex)
    {
    s.iword (flagsindex) = (t & ~LINT::lintdec & ~LINT::lintoct
                & ~LINT::lintbin) | LINT::linthex;
    flag ^= LINT::linthex;
    }
  if (flag & LINT::lintoct)
    {
s.iword (flagsindex) = (t & ~LINT::lintdec & ~LINT::linthex
                & ~LINT::lintbin) | LINT::lintoct;
    flag ^= LINT::lintoct;
    }
  if (flag & LINT::lintbin)
    {
    s.iword (flagsindex) = (t & ~LINT::lintdec & ~LINT::lintoct
                & ~LINT::linthex) | LINT::lintbin;
    flag ^= LINT::lintbin;
    }
  // all remaining flags are mutually compatible
  s.iword (flagsindex) |= flag;
  return t;
  }

Table 15-1. LINT status functions and their effects

Status Function

Explanation

static long
LINT::flags
(void);

read the status variable with reference to cout

static long
LINT::flags
(ostream&);

read the status variable with reference to an arbitrary output stream

static long
LINT::setf
(long);

set individual bits of the status variable with reference to cout and return the previous value

static long
LINT::setf
(ostream&, long);

set individual bits of the status variable with reference to an arbitrary output stream and return the previous value

static long
LINT::unsetf
(long);

restore individual bits of the status variable with reference with reference to cout and return the previous value

static long
LINT::unsetf
(ostream&, long);

restore individual bits of the status variable with reference to an arbitrary output stream and return the previous value

static long
LINT::restoref
(long);

set the status variable with reference to cout with a value and return the previous value

static long
LINT::restoref
(ostream&, long);

set the status variable with reference to an arbitrary output stream with a value and return the previous value

With the help of these and the remaining functions of Table 15-1 we can determine the output formats in the following. First, the standard output format represents the value of a LINT object as a hexadecimal number in a character string, where the output fills as many lines on the screen as required by the number of digits of the LINT object. In an additional line the number of digits of the LINT object is displayed flush left. The following additional modes for output of a LINT object have been implemented:

  1. Base for the representation of digits

    The standard base for the representation of digits of LINT objects is 16, and for the representation of the length it is 10. This default for LINT objects can be set for the standard output stream cout to a specified base by a call to

    LINT::setf (LINT::base);

    and to

    LINT::setf (ostream, LINT::base);

    for an arbitrary output stream. Here base can assume any one of the values

    linthex, lintdec, lintoct, lintbin,

    which denote the corresponding output format. A call to LINT::setf(lintdec), for example sets the output format to decimal digits. The base for the representation of the length can be set with the function

    ios::setf (ios::iosbase);

    with iosbase = hex, dec, oct.

  2. Display of the prefix for the numerical representation

    The default is for a LINT object to be displayed with a prefix indicating how it is represented. A call to

    LINT::unsetf(LINT::lintshowbase);
    LINT::unsetf (ostream, LINT::lintshowbase);

    changes this setting.

  3. Display of hexadecimal digits in uppercase letters

    The default is the display of hexadecimal digits and the display of the prefix 0x for a hexadecimal representation in lowercase letters a b c d e f. However, a call to

    LINT::setf (LINT::lintuppercase);
    LINT::setf (ostream, LINT::lintuppercase);

    changes this, so that the prefix 0X and uppercase letters A B C D E F are displayed.

  4. Display of the length of a LINT object

    The default is the display of the binary length of a LINT objects. This can be changed by a call to

    LINT::unsetf (LINT::lintshowlength);
    LINT::unsetf (ostream, LINT::lintshowlength);

    so that the length is not displayed.

  5. Restoring the status variable for the numerical representation

    The status variable for the formatting of a LINT object can be restored to a previous value oldflags by a call to the two functions

    LINT::unsetf (ostream, LINT::flags(ostream));
    LINT::setf (ostream, oldflags);

    Calls to these two functions are collected in the overloaded function restoref():

    LINT::restoref (flag);
    LINT::restoref (ostream, flag);

    Flags can be combined, as in the call

    LINT::setf (LINT::bin | LINT::showbase);

This, however, is permitted only for flags that are not mutually exclusive.

The output function that finally generates the requested representational format for LINT objects is an extension of the operator ostream& operator <<(ostream& s, LINT ln) already sketched above, which evaluates the status variables of the output stream and generates the appropriate output. For this the operator uses the auxiliary function lint2str() contained in flintpp.cpp, which in turn calls the function xclint2str_l() to represent the numerical value of a LINT object in a character string:

ostream& operator << (ostream& s, const LINT& ln)
  {
    USHORT base = 16;
    long flags = LINT::flags (s);
    char* formatted_lint;

    if (ln.status == E_LINT_INV)
      LINT::panic (E_LINT_VAL, "ostream operator<<", 0, __LINE__);

    if (flags & LINT::linthex)
      {
        base = 16;
      }
    else
{
    if (flags & LINT::lintdec)
      {
        base = 10;
      }
    else
      {
        if (flags & LINT::lintoct)
          {
            base = 8;
          }
        else
          {
            if (flags & LINT::lintbin)
              {
                  base = 2;
              }
           }
        }
    }
  if (flags & LINT::lintshowbase)
    {
      formatted_lint = lint2str (ln, base, 1);
    }
  else
    {
      formatted_lint = lint2str (ln, base, 0);
    }
  if (flags & LINT::lintuppercase)
    {
      strupr_l (formatted_lint);
    }

  s << formatted_lint << flush;

  if (flags & LINT::lintshowlength)
    {
      long _flags = s.flags (); // get current status
      s.setf (ios::dec);// set flag for decimal display
      s << endl << ld (ln) << " bit" << endl;
      s.setf (_flags); // restore previous status
    }
    return s;
}

Manipulators

Building on the previous mechanisms, we would like in this section to obtain more convenient possibilities for controlling the output format for LINT objects. To this end we use manipulators, which are placed directly into the output stream and thus display the same effects as occur in calling the above status functions. Manipulators are addresses of functions for which there exist special insert operators that on their part accept a pointer to a function as argument. As an example we consider the following function:

ostream& LintHex (ostream& s)
{
  LINT::setf (s, LINT::linthex);
  return s;
}

This function calls the status function setf(s, LINT::linthex) in the context of the specified output stream ostream& s and thereby effects the output of LINT objects as hexadecimal numbers. The name LintHex of the function without parentheses is viewed as a pointer to the function (cf. [Lipp], page 202) and can be set in an output stream as a manipulator with the help of the insert operator

ostream& ostream::operator<< (ostream& (*pf)(ostream&))
{
  return (*pf)(*this);
}

defined in the class ostream:

LINT a ("0x123456789abcdef0");
cout << LintHex << a;

ostream s;
s << LintDec << a;

The LINT manipulator functions operate according to the same pattern as the standard manipulators in the C++ library, for example dec, hex, oct, flush, and endl: The insert operator << simply calls the manipulator function LintHex() or LintDec() at the appropriate place. The manipulators ensure that the status flags belonging to the output streams cout, respectively s, are set. The overloaded operator << for the output of LINT objects takes over the representation of the LINT object a in the requested form.

The format settings for the output of LINT objects can all be carried out with the help of the manipulators presented in Table 15-2.

Table 15-2. LINT manipulators and their effects

Manipulator

Effect: Form of the Output of LINT Values

LintBin

as binary numbers

LintDec

as decimal numbers

LintHex

as hexadecimal numbers

LintOct

as octal numbers

LintLwr

with lowercase letters a,b,c,d,e, f for hexadecimal representation

LintUpr

with uppercase letters A,B,C,D,E,F for hexadecimal representation

LintShowbase

with prefix for the numerical representation (0x or 0X for hexadecimal, 0b for binary)

LintNobase

without prefix for numerical representation

LintShowlength

indicating the number of digits

LintNolength

without indicating the number of digits

In addition to the manipulators of Table 15-2, which require no argument, the manipulators

LINT_omanip<int> SetLintFlags (int flags)

and

LINT_omanip<int> ResetLintFlags (int flags)

are available, which can be used as alternatives to the status functions LINT::setf() and LINT::unsetf():

cout << SetLintFlags (LINT::flag) << ...; // turn on
cout << ResetLintFlags (LINT::flag) << ...; // turn off

For the implementation of these manipulators the reader is referred to the sources (flintpp.h and flintpp.cpp) in connection with the explanation of the template class omanip<T> in [Pla2], Chapter 10. The LINT flags are shown once again in Table 15-3.

Table 15-3. LINT flags for output formatting and their effects

Flag

Value

lintdec

0×010

lintoct

0×020

linthex

0×040

lintshowbase

0×080

lintuppercase

0×100

lintbin

0×200

lintshowlength

0×400

We shall now clarify the use of the format functions and manipulators by means of the following example:

#include "flintpp.h"
#include <iostream.h>
#include <iomanip.h>

main()
{
  LINT n ("0x0123456789abcdef"); // LINT number with base 16
  long deflags = LINT::flags(); // store flags

  cout << "Default representation: " << n << endl;

  LINT::setf (LINT::linthex | LINT::lintuppercase);
  cout << "hex representation with uppercase letters: " << n << endl;
  cout << LintLwr << "hex representation with lowercase letters: " << n << endl;
  cout << LintDec << "decimal representation: " << n << endl;
  cout << LintBin << "binary representation: " << n << endl;
  cout << LintNobase << LintHex;
  cout << "representation without prefix: " << n << endl;
  cerr << "Default representation Stream cerr: " << n << endl;

  LINT::restoref (deflags);
  cout << "default representation: " << n << endl;

  return;
}

File I/O for LINT Objects

Functions for the output of LINT objects to files and functions for reading them are unavoidable for practical applications. The input and output classes of the C++ standard library contain member functions that permit the setting of objects into an input or output stream for file operations, so we are fortunate in that we can use the same syntax as we used above. The operators needed for output to files are similar to those of the last section, where, however, we can do without the formatting.

We define the two operators

friend ofstream& operator<< (ofstream& s, const LINT& ln);
friend fstream& operator<< (fstream& s, const LINT& ln);

for output streams of the class ofstream and for streams of the class fstream, which supports both directions, that is, both input and output. Since the class ofstream is derived from the class ostream, we can use its member function ostream::write() to write unformatted data to a file. Since only the digits of a LINT object that are actually used are stored, we can deal sparingly with the storage space of the data medium. Here the USHORT digits of the LINT object are actually written as a sequence of UCHAR values. To ensure that this always occurs in the correct order, independent of the numerical representation scheme of a particular platform, an auxiliary function is defined that writes a USHORT value as a sequence of two UCHAR types. This function neutralizes the platform-specific ordering of the digits to base 256 in memory and thereby allows data that were written on one computer type to be read on another that possibly orders the digits of a number differently or perhaps interprets them differently when they are read from mass storage. Relevant examples in this connection are the little-endian and big-endian architectures of various processors, which in the former case order consecutive increasing memory addresses in increasing order, and in the latter case do so in decreasing order.[48]

template <class T>
  int write_ind_ushort (T& s, clint src)
  {
    UCHAR buff[sizeof(clint)];
    unsigned i, j;

    for (i = 0, j = 0; i < sizeof(clint); i++, j = i << 3)
      {
        buff[i] = (UCHAR)((src & (0xff << j)) >> j);
      }

    s.write (buff, sizeof(clint));
if (!s)
  {
     return −1;
   }
 else
   {
     return 0;
   }
}

The function write_ind_ushort() returns in the case of error the value −1, while it returns 0 if the operation is successful. It is implemented as a template, so that it can be used with both ofstream objects and fstream objects. The function read_ind_ushort() is created as its counterpart:

template <class T>
int read_ind_ushort (T& s, clint *dest)
{
  UCHAR buff[sizeof(clint)];
  unsigned i;     s.read (buff, sizeof(clint));

  if (!s)
    {
      return −1;
    }
  else
    {
      *dest = 0;
      for (i = 0; i < sizeof(clint); i++)
        {
          *dest |= ((clint)buff[i]) << (i << 3);
        }
      return 0;
   }
}

The output operators now use this neutral format to write from a LINT object to a file. To elucidate the situation we shall present the implementation of the operator for the class ofstream.

ofstream& operator<< (ofstream& s, const LINT& ln)
  {
    if (ln.status == E_LINT_INV)
      LINT::panic (E_LINT_VAL, "ofstream operator<<", 0, __LINE__);
for (int i = 0; i <= DIGITS_L (ln.n_l); i++)
    {
      if (write_ind_ushort (s, ln.n_l[i]))
        {
          LINT::panic (E_LINT_EOF, "ofstream operator<<", 0, __LINE__);
        }
    }
  return s;
}

Before a LINT object is written to a file, the file must be opened for writing, for which one could use the constructor

ofstream::ofstream (const char *, openmode)

or the member function

ofstream::open (const char *, openmode)

In each case the ios flag ios::binary must be set, as in the following example:

LINT r ("0x0123456789abcdef");
// ...
ofstream fout ("test.io", ios::out | ios::binary);
fout << r << r*r;
// ...
fout.close();

The importation of a LINT object from a file is effected in a complementary way, with analogous operators, to that of output of a LINT object to a file.

friend ifstream& operator >> (ifstream& s, LINT& ln);
friend fstream& operator >> (fstream& s, LINT& ln);

Both operators first read a single value, which specifies the number of digits of the stored LINT object. Then the corresponding number of digits are read in. The USHORT values are read according to the above description under the action of the function read_ind_ushort():

ifstream& operator>> (ifstream& s, LINT& ln)
{
  if (read_ind_ushort (s, ln.n_l))
    {
LINT::panic (E_LINT_EOF, "ifstream operator>>", 0, __LINE__);
    }
  if (DIGITS_L (ln.n_l) < CLINTMAXSHORT)
    {
      for (int i = 1; i <= DIGITS_L (ln.n_l); i++)
        {
          if (read_ind_ushort (s, &ln.n_l[i]))
            {
              LINT::panic (E_LINT_EOF, "ifstream operator>>", 0, __LINE__);
            }
         }
     }


  // No paranoia! Check the imported value!
  if (vcheck_l (ln.n_l) == 0)
    {
      ln.status = E_LINT_OK;
    }
  else
    {
      ln.status = E_LINT_INV;
    }
return s;
}

To open a file from which the LINT object is to be read it is again necessary to set the ios flag ios::binary:

LINT r, s;
// ...
ifstream fin;
fin.open ("test.io", ios::in | ios::binary);
fin >> r >> s;
// ...
fin.close();

In the importation of LINT objects the insert operator >> checks whether the values read represent the numerical representation of a valid LINT object. If this is not the case, the member datum status is set to E_LINT_INV, and the specified target object is thereby marked as "uninitialized." On the next operation on this object the LINT error handler is invoked, which is what we shall study in more detail in the next chapter.



[47] We use this name of the stream classes as a synonym for the terms now used in the C++ standard library, with which the class names known up to now are prefixed with basic_. The justification for this comes from the standard library itself, where the class names known hitherto are provided with corresponding typedefs (cf. [KSch], Chapter 12).

[48] Two bytes Bi and Bi+1 with addresses i and i + 1 are interpreted in the little-endian representation as USHORT value w = 28 Bi+1 + Bi and in the big-endian representation as w = 28 Bi + Bi+1. The analogous situation holds for the interpretation of ULONG values.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.123.126