########################################
# The contents of this file are subject to the MLX PUBLIC LICENSE version
# 1.0 (the "License"); you may not use this file except in
# compliance with the License.
# 
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
# the License for the specific language governing rights and limitations
# under the License.
# 
# The Original Source Code is "compClust", released 2003 September 03.
# 
# The Original Source Code was developed by the California Institute of
# Technology (Caltech).  Portions created by Caltech are Copyright (C)
# 2002-2003 California Institute of Technology. All Rights Reserved.
########################################
#
#        Author: Benjamin J. Bornstein
# Last Modified: 24-Apr-2001, 3:07p
#

"""
A generic mixed-base counter.
"""

class Counter:

  def __init__(self, maximums):
    """Counter(maximums)

    Counter(maximums) defines a new Counter.  Maximums is a vector
    whose length indicates the number of places in the counter.  Each
    entry in maximums corresponds to the maximum value allowed at that
    place before overflow occurs.  Counting starts at zero (0, ..., 0)
    and proceeds from left to right.

    Note that since counting starts at zero you may want each entry in
    maximums to be one less than you had planned.

    To see how Counter works, initialize a new one and call
    Counter.test().

    See also increment(), get_count(), reset(), test()

    """

    self.maximums   = maximums
    self.num_places = len(maximums)
    self.overflow   = 0
    self.count      = [0] * self.num_places


  def get_count(self):
    """Counter.get_count()

    Counter.get_count() returns a tuple containing the current count.
    """

    return tuple(self.count)


  def increment(self):
    """Counter.increment()

    Counter.increment() increments the current count by one.  In the
    case of an overflow, Counter.overflow will be set to 1 and each
    place in the count will contain a -1.
    """

    pos             = 0
    self.count[pos] = self.count[pos] + 1

    while (self.count[pos] > self.maximums[pos]):
      self.count[pos] = 0
      pos             = pos + 1

      if (pos == self.num_places):
        self.__set_overflow()
        break
      else:
        self.count[pos] = self.count[pos] + 1


  def reset(self):
    """Counter.reset()

    Counter.reset() resets the counter to zero.
    """

    self.overflow = 0
    for i in range(self.num_places):
      self.count[i] = 0


  def test(self):
    """Counter.test()

    Counter.test() resets the counter and then continually prints and
    increments the state of the counter until overflow is reached.
    The overflow state is printed.  The counter is reset at the end of
    this method.
    """

    self.reset()

    while (not self.overflow):
      print repr( self.get_count() )
      self.increment()

    print repr( self.get_count() )
    self.reset()


  def __set_overflow(self):
    """__set_overflow()

    Counter.__set_overflow() sets the overflow condition of the counter.
    """

    self.overflow = 1
    for i in range(self.num_places):
      self.count[i] = -1
