# Example of a generator: re-implement the built-in range function
# without actually constructing the list of values.  (It turns out
# that the built-in function is about 20 times faster -- that's why
# it's built-in. :-)


# Wrapper function to emulate the complicated range() arguments

def range(*a):
	if len(a) == 1:
		start, stop, step = 0, a[0], 1
	elif len(a) == 2:
		start, stop = a
		step = 1
	elif len(a) == 3:
		start, stop, step = a
	else:
		raise TypeError, 'range() needs 1-3 arguments'
	return Range(start, stop, step)
	

# Class implementing a range object.
# To the user the instances feel like immutable sequences
# (and you can't concatenate or slice them)

class Range:

	# initialization -- should be called only by range() above
	def __init__(self, start, stop, step):
		if step == 0:
			raise ValueError, 'range() called with zero step'
		self.start = start
		self.stop = stop
		self.step = step
		self.len = max(0, int((self.stop - self.start) / self.step))

	# implement `x` and is also used by print x
	def __repr__(self):
		return 'range' + `self.start, self.stop, self.step`

	# implement len(x)
	def __len__(self):
		return self.len

	# implement x[i]
	def __getitem__(self, i):
		if 0 <= i < self.len:
			return self.start + self.step * i
		else:
			raise IndexError, 'range[i] index out of range'


# Small test program

def test():
	import time, __builtin__
	print range(10), range(-10, 10), range(0, 10, 2)
	for i in range(100, -100, -10): print i,
	print
	t1 = time.millitimer()
	for i in range(1000):
		pass
	t2 = time.millitimer()
	for i in __builtin__.range(1000):
		pass
	t3 = time.millitimer()
	print t2-t1, 'msec (class)'
	print t3-t2, 'msec (built-in)'


test()
