watermark.py

class Watermark:
	"""Watermark is a sequence that may contain sub-sequences
	that match patterns."""

	def __init__(self, watermark):
		"""Initialize a Watermark instance.
		
		'watermark'
			value representing a watermark, typically a string.
		"""
		raise NotImplementedError("Watermark.__init__")

	def pattern_start_indices(self, pattern_length):
		"""Return range of start indices that may match
		a pattern of length 'pattern_length'.
		
		'pattern_length'
			length of pattern, integer.
		"""
		raise NotImplementedError("Watermark.start_indices")

	def decode_start_indices(self):
		"""Return range of start indices where decoding can begin.
		"""
		raise NotImplementedError("Watermark.start_indices")

	def values(self, start, count=None):
		"""Return watermark values as a sequence.

		'start'
			start index, an element of range returned by
			'pattern_start_indices' or 'decode_start_indices'.
		'count'
			number of values to return, integer.
			'None' means all.
		"""
		raise NotImplementedError("Watermark.value")

class DNAWatermark(Watermark):
	"""DNAWatermark is a DNA sequence where starting position
	may be any base (thereby handling frame shifts) and values
	are codons."""

	def __init__(self, watermark):
		self.watermark = watermark

	def pattern_start_indices(self, pattern_length):
		codon_length = pattern_length * 3
		return range(0, len(self.watermark) - codon_length + 1)

	def decode_start_indices(self):
		return range(0, 3)

	def values(self, start, count=None):
		if count is None:
			left = len(self.watermark) - start
			end = start + left - (left % 3)
		else:
			end = start + count * 3
			if end > len(self.watermark):
				raise IndexError("watermark too short")
		return [ self.watermark[i:i+3] for i in range(start, end, 3) ]

if __name__ == "__main__":
	import unittest
	class TestWatermark(unittest.TestCase):

		def setUp(self):
			self.w = DNAWatermark("abcdefghi")

		def test_decode_indices(self):
			indices = self.w.decode_start_indices()
			self.assertEqual(len(indices), 3)

		def test_decode(self):
			self.assertEqual(len(self.w.values(0)), 3)
			self.assertEqual(self.w.values(1), ["bcd", "efg"])
			self.assertEqual(len(self.w.values(2)), 2)
	unittest.main()