import collections

class Vector:
  """Represent a vector in a multidimensional space."""

  def __init__(self, d):
    if isinstance(d, (int, float)):
      self._coords = [0] * d
    else:                                  
      try:                              # we test if param is iterable
        self._coords = [val for val in d]
      except TypeError:
        raise TypeError('invalid parameter type')

  def __len__(self):
    """Return the dimension of the vector."""
    return len(self._coords)

  def __getitem__(self, j):
    """Return jth coordinate of vector."""
    return self._coords[j]

  def __setitem__(self, j, val):
    """Set jth coordinate of vector to given value."""
    self._coords[j] = val

  def __add__(self, other):
    """Return sum of two vectors."""
    if len(self) != len(other):          # relies on __len__ method
      raise ValueError('dimensions must agree')
    result = Vector(len(self))           # start with vector of zeros
    for j in range(len(self)):
      result[j] = self[j] + other[j]
    return result

  def __eq__(self, other):
    """Return True if vector has same coordinates as other."""
    return self._coords == other._coords

  def __ne__(self, other):
    """Return True if vector differs from other."""
    return not self == other             # rely on existing __eq__ definition

  def __str__(self):
    """Produce string representation of vector."""
    return '<' + str(self._coords)[1:-1] + '>'  # adapt list representation

  def __neg__(self):
    """Return copy of vector with all coordinates negated."""
    result = Vector(len(self))           # start with vector of zeros
    for j in range(len(self)):
      result[j] = -self[j]
    return result

  def __lt__(self, other):
    """Compare vectors based on lexicographical order."""
    if len(self) != len(other):
      raise ValueError('dimensions must agree')
    return self._coords < other._coords

  def __le__(self, other):
    """Compare vectors based on lexicographical order."""
    if len(self) != len(other):
      raise ValueError('dimensions must agree')
    return self._coords <= other._coords

  # my test
  def __sub__(self, other):
    """return the difference between two vectors"""
    if len(self) != len(other):
      raise ValueError('dimensions must agree')
    result = Vector(len(self))
    for j in range(len(self)):
      result[j] = self[j] - other[j]
    return result
  
  def __radd__(self, other):
    """Return sum of two vectors."""
    return self + other  # rely on existing __add__ definition

  def __mul__(self, other):
    """return dot product or n times respective"""
    result = Vector(len(self))
    if isinstance(other, (int, float)):
      for i in range(len(self)):
        result[i] = self[i] * other
    else:
      if len(self) != len(other):
        raise ValueError('dimensions must agree')
      for i in range(len(self)):
        result[i] = self[i] * other[i]
    return result

  def __rmul__(self, other):
    """return dot product or n times respective"""
    return self * other

if __name__ == '__main__':
  # the following demonstrates usage of a few methods
  v = Vector(5)              # construct five-dimensional <0, 0, 0, 0, 0>
  v[1] = 23                  # <0, 23, 0, 0, 0> (based on use of __setitem__)
  v[-1] = 45                 # <0, 23, 0, 0, 45> (also via __setitem__)
  print(v[4])                # print 45 (via __getitem__)
  u = v + v                  # <0, 46, 0, 0, 90> (via __add__)
  print(u)                   # print <0, 46, 0, 0, 90>
  total = 0
  for entry in u:            # implicit iteration via __len__ and __getitem__
    total += entry
  
  # my test
  u = Vector(5)
  for i in range(5):
    u[i] = i + 1

  v = Vector(5)
  for i in range(5):
    v[i] = len(v) - i

  print(u)
  print(v)

  # test u + v and v + u
  print(u + [1,1,1,1,1])
  print([2,2,2,2,2] + u)

  # test u * v
  print(u * 3)
  print(3 * v)
  print(u * v)

  print(Vector([8,8,8,8]))