/* {\documentstyle[ascmac,fleqn,epsf,rt0,rt1,rtraku,lab]{jarticle}%
   \pagestyle{rt}}
   {\
   \section{コンテナクラス {\tt NoRefTable}}
   \label{NoRefTable.hh}
   
   あらゆるオブジェクトを配列として取り込めるコンテナクラスで、
   C++ の「テンプレート機能」を使用して記述している。
   テンプレート機能を使う関係上、
   このファイルには関数の定義も一緒に記述してある。%\}

   */

#ifndef ___NOREFTABLE_HH
#define ___NOREFTABLE_HH


#include <cstdio>
#include <cstdlib>
#include <iostream>

using std :: cerr ;
using std :: endl ;

#ifndef GETMEMORYNUMBER
#define GETMEMORYNUMBER		40
#endif	/* {\tt GETMEMORYNUMBER %\} おわり */

template < class TP >
class NoRefTable
{

  // ____ 変数 ____

protected :
  int num ;		// 配列に格納しているオブジェクト数
  TP * data ;		// その配列へのポインタ
  int mmax ;		// 配列に格納可能な最大のオブジェクト数

  TP * bxold ;		// テンポラリな格納用スペース
  int bxoldm ;		/* {\tt bxold} 用に確保したメモリの最大数
                           ({\tt mmax} と同じ) */

  // ____ 関数 ____

public :

  // コンストラクタ
  NoRefTable () ;

    virtual ~NoRefTable();			// デストラクタ
    NoRefTable(const NoRefTable<TP>&);		// X(X&)
    NoRefTable<TP>& operator=
    ( const NoRefTable<TP>& x);		// 代入

    TP operator[] ( int i ) const ;		// 配列データの参照

    TP last () const {return operator[]( num - 1 );}

    inline void renew();		// 配列の中身を空にする
    void renew(int max);		/* 同上、同時に
                                           最大オブジェクト数を変更 */
    void setnum(int nm);		/* 格納オブジェクト数を
                                           この値にセットする */

    inline TP shift();			// {\ Perl の {\tt shift}}
    inline void unshift(TP);		// {\ Perl の {\tt unshift}}
    inline TP pop();			// {\ Perl の {\tt pop}}
    inline void push(TP);		// {\ Perl の {\tt push}}

    TP remove( int );			// int 番目のデータを取り除く

    NoRefTable<TP>& operator+=( const TP );	// データを加える
    NoRefTable<TP>& operator+=
    ( const NoRefTable<TP>& );		// 配列をくっつける
    NoRefTable<TP>& operator*=		// データを「先頭に」加える
    ( const TP x ) {unshift( x );}
    inline int number() const
        {return num;}			// 配列の最大数を返す

  protected:
    void membercopy
    ( const NoRefTable<TP>& x );		// メンバコピー

  private:
    virtual void error26_name() const;		/* 配列範囲外参照
                                                   エラーメッセージ */

    inline void bxold_resize();			/* {\tt bxold} 配列のリサイズ
                                                   {\tt mmax} の値で条件づけ */
    inline void bxold_resize_core();		// 同上・条件なし


    inline void num_incrementation(int);	// {\tt num} のインクリメント
    inline bool					// {\tt num} インクリメントの
    num_incrementation_backup(int);		// 際のデータバックアップ
    inline void					// 上記関数でバックアップした
    num_incrementation_restore(int);		// データを元に戻す
};



// 配列の中身を空にする
template <class TP>
inline void
NoRefTable<TP>::renew
() {
    num = 0;
    /* {\ 実はたったこれだけである。一度確保したメモリ領域を
       いちいち解放するとオーバヘッドが大きくなるため、
       中身を考慮せず {\tt num} を 0 とすることで「誤魔化す」。
       従って、格納するオブジェクトは当然代入やいわゆる X(X\&) が
       正しくできるように設計しておく必要がある。
       %\}
       */
}

// {\tt shift} (Perl 言語を参照)
// 配列の先頭の要素を取り除き、その値を返す。
template <class TP>
inline TP
NoRefTable<TP>::shift
() {
    bxold_resize();			// {\tt bxold} 領域を確保
    bxold [ 0 ] = data [ 0 ];		// 取り除く要素を退避
    for ( register int i = 1;		// 配列を1つずつ前にずらす
          i < num; i++ )
        data [ i - 1 ] = data [ i ];
    num--;
    return bxold [ 0 ];			// 取り除いた要素を {\tt return}
}

// {\tt pop} (Perl 言語を参照)
// 配列の末尾の要素を取り除き、その値を返す。
template <class TP>
inline TP
NoRefTable<TP>::pop
() {
    if ( num == 0 ) {			// {\tt num} がゼロなら error
        cerr << "Error: tried to pop empty table\n";
        error26_name();			// エラーメッセージ（流用）
        exit(27);			// Exit コード 27
    }
    return data [ --num ];		// 要素を {\tt return}。これだけ！
}

// {\tt unshift} (Perl 言語を参照)
// 配列の先頭に要素を付け加える。
template <class TP>
inline void
NoRefTable<TP>::unshift
( TP x ) {
    register int i;
    if ( num_incrementation_backup( 1 ) )
        for ( i = 0; i < num; i++ )
             data[ i + 1 ] = bxold[ i ];
    else
        for ( i = num - 1; i >= 0; i-- )
             data[ i + 1 ] = data[ i ];
    num++;
    data[ 0 ] = x;
}

// {\tt push} (Perl 言語を参照)
// 配列の末尾に要素を付け加える。
template <class TP>
inline void
NoRefTable<TP>::push
( TP x ) {
    operator+=( x );	// 実に簡単！
}

// {\tt bxold} 配列のリサイズ・ {\tt mmax} の値で条件づけ
template <class TP>
inline void
NoRefTable<TP>::bxold_resize
() {
    if ( bxoldm < mmax ) bxold_resize_core();
}

// {\tt bxold} 配列のリサイズ・条件なし
template <class TP>
inline void
NoRefTable<TP>::bxold_resize_core
() {
    if ( bxold ) delete [] bxold;
    bxold = new TP[ bxoldm = mmax + GETMEMORYNUMBER ];
}

// {\tt num} インクリメントの際のデータバックアップ
template <class TP>
inline bool
NoRefTable<TP>::num_incrementation_backup
( int incr ) {
    bool ret_val = false;
    int size = num + incr;
    if ( size > mmax ) {
	if ( mmax > 0 ) {
            bxold_resize();
	    for ( register int j = 0; j < num; j++ )
		bxold[ j ] = data[ j ];
	    if ( data ) delete [] data;
            ret_val = true;
        }
        data = new TP[ mmax = size + GETMEMORYNUMBER ];
    }
    return ret_val;
}

// バックアップした {\tt data} を元に戻す
template <class TP>
inline void
NoRefTable<TP>::num_incrementation_restore
( int incr ) {
    if ( num_incrementation_backup( incr ) )
        for ( register int j = 0; j < num; j++ )
            data[ j ] = bxold[ j ];
}

// {\tt num} のインクリメント
template <class TP>
inline void
NoRefTable<TP>::num_incrementation
( int incr ) {
    num_incrementation_restore( incr );
    num += incr;
}
    

// GCC 2.7.2.2 ではこのようなことをしてはいけないらしい
/* #ifdef __MAIN_ROUTINE */
#include "NoRefTable-def.hh"
/* #endif */

#endif /* ! ___NOREFTABLE_HH */
