# Path: discodop/util.py
# def slice_bounds(seq, slice_obj, allow_step=False):
# 	"""Calculate the effective (start, stop) bounds of a slice.
# 
# 	Takes into account ``None`` indices and negative indices.
# 
# 	:returns: tuple ``(start, stop, 1)``, s.t. ``0 <= start <= stop <= len(seq)``
# 	:raises ValueError: if slice_obj.step is not None.
# 	:param allow_step: If true, then the slice object may have a non-None step.
# 		If it does, then return a tuple (start, stop, step)."""
# 	start, stop = (slice_obj.start, slice_obj.stop)
# 	if allow_step:
# 		slice_obj.step = 1 if slice_obj.step is None else slice_obj.step
# 		# Use a recursive call without allow_step to find the slice
# 		# bounds. If step is negative, then the roles of start and
# 		# stop (in terms of default values, etc), are swapped.
# 		if slice_obj.step < 0:
# 			start, stop, _ = slice_bounds(seq, slice(stop, start))
# 		else:
# 			start, stop, _ = slice_bounds(seq, slice(start, stop))
# 		return start, stop, slice_obj.step
# 	elif slice_obj.step not in (None, 1):
# 		raise ValueError('slices with steps are not supported by %s' %
# 				seq.__class__.__name__)
# 	start = 0 if start is None else start
# 	stop = len(seq) if stop is None else stop
# 	start = max(0, len(seq) + start) if start < 0 else start
# 	stop = max(0, len(seq) + stop) if stop < 0 else stop
# 	if stop > 0:  # Make sure stop doesn't go past the end of the list.
# 		try:  # Avoid calculating len(seq), may be expensive for lazy sequences
# 			seq[stop - 1]
# 		except IndexError:
# 			stop = len(seq)
# 	start = min(start, stop)
# 	return start, stop, 1
# 
# ANSICOLOR = {
# 		'black': 30,
# 		'red': 31,
# 		'green': 32,
# 		'yellow': 33,
# 		'blue': 34,
# 		'magenta': 35,
# 		'cyan': 36,
# 		'white': 37,
# }
# 
# def graphemelength(text):
# 	"""Return number of graphemes in string."""
# 	return grapheme.length(text)
# 
# def graphemecenter(text, width, fillchar=' '):
# 	"""Return text centered in a string of *grapheme* length width (not len()).
# 
# 	Padding is done using the specified fillchar (default is an ASCII space).
# 	The original string is returned if width is less than or equal to len(s).
# 	"""
# 	length = grapheme.length(text)
# 	if length > width:
# 		return text
# 	a = (width - length) // 2
# 	b = a + (width - length) % 2
# 	return (a * fillchar) + text + (b * fillchar)


import re
import random
from itertools import count
from operator import itemgetter
from collections import defaultdict, OrderedDict
from html import escape as htmlescape
from .util import slice_bounds, ANSICOLOR, graphemelength, graphemecenter
	from .bit import fanout as bitfanout
	# Every method that adds or removes a child must make
	# appropriate calls to _setparent() and _delparent().
	def __delitem__(self, index):
		if isinstance(index, slice):  # del ptree[start:stop]
			start, stop, _ = slice_bounds(self, index)
			# Clear all the children pointers.
			for i in range(start, stop):
				if isinstance(self[i], Tree):
					self._delparent(self[i], i)
			# Delete the children from our child list.
			super().__delitem__(index)
		elif isinstance(index, int):  # del ptree[i]
			if index < 0:
				index += len(self)
			if index < 0:
				raise IndexError('index out of range')
			# Clear the child's parent pointer.
			if isinstance(self[index], Tree):
				self._delparent(self[index], index)
			# Remove the child from our child list.
			super().__delitem__(index)
		elif index == ():  # del ptree[()]
			raise IndexError('The tree position () may not be deleted.')
		elif len(index) == 1:  # del ptree[(i, )]
			del self[index[0]]
		else:  # del ptree[i1, i2, i3]
			del self[index[0]][index[1:]]

	def __setitem__(self, index, value):
		if isinstance(index, slice):  # ptree[start:stop] = value

# Path: discodop/util.py
# def slice_bounds(seq, slice_obj, allow_step=False):
# 	"""Calculate the effective (start, stop) bounds of a slice.
# 
# 	Takes into account ``None`` indices and negative indices.
# 
# 	:returns: tuple ``(start, stop, 1)``, s.t. ``0 <= start <= stop <= len(seq)``
# 	:raises ValueError: if slice_obj.step is not None.
# 	:param allow_step: If true, then the slice object may have a non-None step.
# 		If it does, then return a tuple (start, stop, step)."""
# 	start, stop = (slice_obj.start, slice_obj.stop)
# 	if allow_step:
# 		slice_obj.step = 1 if slice_obj.step is None else slice_obj.step
# 		# Use a recursive call without allow_step to find the slice
# 		# bounds. If step is negative, then the roles of start and
# 		# stop (in terms of default values, etc), are swapped.
# 		if slice_obj.step < 0:
# 			start, stop, _ = slice_bounds(seq, slice(stop, start))
# 		else:
# 			start, stop, _ = slice_bounds(seq, slice(start, stop))
# 		return start, stop, slice_obj.step
# 	elif slice_obj.step not in (None, 1):
# 		raise ValueError('slices with steps are not supported by %s' %
# 				seq.__class__.__name__)
# 	start = 0 if start is None else start
# 	stop = len(seq) if stop is None else stop
# 	start = max(0, len(seq) + start) if start < 0 else start
# 	stop = max(0, len(seq) + stop) if stop < 0 else stop
# 	if stop > 0:  # Make sure stop doesn't go past the end of the list.
# 		try:  # Avoid calculating len(seq), may be expensive for lazy sequences
# 			seq[stop - 1]
# 		except IndexError:
# 			stop = len(seq)
# 	start = min(start, stop)
# 	return start, stop, 1
# 
# ANSICOLOR = {
# 		'black': 30,
# 		'red': 31,
# 		'green': 32,
# 		'yellow': 33,
# 		'blue': 34,
# 		'magenta': 35,
# 		'cyan': 36,
# 		'white': 37,
# }
# 
# def graphemelength(text):
# 	"""Return number of graphemes in string."""
# 	return grapheme.length(text)
# 
# def graphemecenter(text, width, fillchar=' '):
# 	"""Return text centered in a string of *grapheme* length width (not len()).
# 
# 	Padding is done using the specified fillchar (default is an ASCII space).
# 	The original string is returned if width is less than or equal to len(s).
# 	"""
# 	length = grapheme.length(text)
# 	if length > width:
# 		return text
# 	a = (width - length) // 2
# 	b = a + (width - length) % 2
# 	return (a * fillchar) + text + (b * fillchar)


# Path: discodop/tree.py
import re
import random
from itertools import count
from operator import itemgetter
from collections import defaultdict, OrderedDict
from html import escape as htmlescape
from .util import slice_bounds, ANSICOLOR, graphemelength, graphemecenter
	from .bit import fanout as bitfanout

	# Every method that adds or removes a child must make
	# appropriate calls to _setparent() and _delparent().
	def __delitem__(self, index):
		if isinstance(index, slice):  # del ptree[start:stop]
			start, stop, _ = slice_bounds(self, index)
			# Clear all the children pointers.
			for i in range(start, stop):
				if isinstance(self[i], Tree):
					self._delparent(self[i], i)
			# Delete the children from our child list.
			super().__delitem__(index)
		elif isinstance(index, int):  # del ptree[i]
			if index < 0:
				index += len(self)
			if index < 0:
				raise IndexError('index out of range')
			# Clear the child's parent pointer.
			if isinstance(self[index], Tree):
				self._delparent(self[index], index)
			# Remove the child from our child list.
			super().__delitem__(index)
		elif index == ():  # del ptree[()]
			raise IndexError('The tree position () may not be deleted.')
		elif len(index) == 1:  # del ptree[(i, )]
			del self[index[0]]
		else:  # del ptree[i1, i2, i3]
			del self[index[0]][index[1:]]

	def __setitem__(self, index, value):
		if isinstance(index, slice):  # ptree[start:stop] = value

			start, stop, _ = slice_bounds(self, index)