/* ============================================================
 * File  : sequence.h
 * Description : 
 *   A set of classes providing a sequence of numbers within a 
 *   specified range allowing navigation in the sequence.
 *     SequenceBase       - Base for all Sequence classes.
 *     SequenceInc        - An incrementing sequence.
 *     SequenceDec        - A decrementing sequence.
 *     SequenceRandomBase - Base for all 'random' Sequence classes.
 *     SequenceRandom     - A random sequence (can have duplicates).
 *     SequenceShuffle    - A random sequence (no duplicates).
 * 

 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General
 * Public License as published bythe Free Software Foundation;
 * either version 2, or (at your option)
 * any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * ============================================================ */

#ifndef SEQUENCE_H
#define SEQUENCE_H

#include <stdlib.h>

class SequenceBase  
{
public:
	SequenceBase(int _len, bool _reset = true) 
	: len(_len), idx(0) { 
		if(_reset) { reset(_len); }
	};

	virtual ~SequenceBase() {
	};

	virtual void reset(int _len) {
		len = _len; 
        idx = 0;
	};

	virtual void extend(int _len) {
		len = _len; 
	};

	int index(int _idx) { idx = _idx; return index(); };
	int next(void)      { idx++; return index(); };
	int prev(void)      { idx--; return index(); };
	
	
protected:
	int index(void)     {
        if( idx < 0 ) {
            idx += len; 
    	} 
        idx %= len; 
        int retval = get(); 
        return retval;
    };

	virtual int get(void) = 0;

	int  len;
	int  idx;
};

class SequenceInc : public SequenceBase
{
public: 
	SequenceInc(int _len) 
	: SequenceBase(_len) {
	};

protected:
	virtual int get(void) { return idx; };
};

class SequenceDec : public SequenceBase
{
public: 
	SequenceDec(int _len) 
	: SequenceBase(_len) {
	};

protected:
	virtual int get(void) { 
		return len - idx - 1; 
	};
};

class SequenceRandomBase : public SequenceBase
{
public:
	SequenceRandomBase(int _len, bool _reset = true)
	: SequenceBase(_len, _reset), seq(0) { 
		if( _reset ) { reset(_len); } 
	};

	virtual ~SequenceRandomBase() { 
		if( seq ) { 
			delete seq; 
		}
	};

	virtual void reset(int _len) { 
		SequenceBase::reset(_len); 
		if( seq ) { 
			delete seq; 
		} 
		seq = new(int[len]); 
		for( int i = 0 ; i < len ; i++ ) { 
			seq[i] = -1; 
		} 
	};

protected:
	virtual int get(void) { 
		if( seq[idx] == -1 ) { 
			seq[idx] = create(); 
		} 
		return seq[idx]; 
	};

	virtual int create(void) = 0;

	int *seq;
};

class SequenceRandom : public SequenceRandomBase
{
public:
	SequenceRandom(int _len) 
	: SequenceRandomBase(_len) {};

protected:
	virtual int create(void) { 
		return (int)(((double)rand()) * len / RAND_MAX); 
	};
};

#define MAP_IDX(idx)     map[((idx) / sizeof(int))]
#define MAP_MSK(idx)     (1 << ((idx) % sizeof(int)))
#define MAP_SET(map,idx) MAP_IDX(idx) |= MAP_MSK(idx)
#define MAP_CLR(map,idx) MAP_IDX(idx) &= ~MAP_MSK(idx)
#define MAP(map,idx)     (MAP_IDX(idx) & MAP_MSK(idx))

class SequenceShuffle : public SequenceRandomBase
{
public:
	SequenceShuffle(int _len)
	  : SequenceRandomBase(_len, false), map(0), used(0)
	{ 
		reset(_len); 
	};

	virtual ~SequenceShuffle() { 
		if( map ) { 
			delete map; 
		} 
	};

	virtual void reset(int _len) { 
		SequenceRandomBase::reset(_len); 
		if( map ) { 
		   delete map; 
		} 
		map = new(int[(len / sizeof(int)) + 1]); 
		for( int i = 0 ; i < len ; i++ ) { 
		   MAP_CLR(map,i); 
		} 
	}; 

protected:
	virtual int create(void) { 
		while(1) { 
			int i = (int)(((double)rand()) * len / RAND_MAX); 
			if( !MAP(map,i) ) { 
				MAP_SET(map,i); 
				return i; 
			} 
		} 
	};

	int *map;
	int used;
};

#endif // ndef SEQUENCE_H
