docs: def smooth_lines(lns, n=1, inertia=0.5):
      '''
      smooth_lines(matrix) runs one smoothing iteration on the lines implied by the {2,3}xN matrix.
      smooth_lines(collection) iteratively descends lists, sets, and the values of maps, converting
        all valid matrices that it finds; note that if it encounters anything that cannot be
        interpreted as a line matrix (such as a string), an error is raised.
      
      Smoothing is performed by adding to the coordinates of each vertex with two neighbors half of
      the coordinates of each of its neighbors then dividing that sum by 2; i.e.:
        x_new[i] == x_old[i]/2 + x_old[i-1]/4 + x_old[i+1]/4.
  
      The following options may be given:
        * n (default: 1) indicates the number of times to perform the above smoothing operation.
        * inertia (default: 0.5) specifies the fraction of the new value that is derived from the
          old value of a node as opposed to the old values of its neighbors. If intertia is m, then
          x_new[i] == m*x_old[i] + (1-m)/2*(x_old[i-1] + x_old[i+1]).
          A higher intertia will smooth the line less each iteration.
      '''
      if pimms.is_lazy_map(lns):
          return pimms.lazy_map({k:curry(lambda k: smooth_lines(lns[k], n=n, inertia=inertia), k)
                                 for k in six.iterkeys(lns)})
      elif pimms.is_map(lns):
          m = {k:smooth_lines(v,n=n,inertia=inertia) for (k,v) in six.iteritems(lns)}
          return pyr.pmap(m) if pimms.is_pmap(lns) else m
      elif pimms.is_matrix(lns, 'number'):
          (p,q) = (inertia, 1 - inertia)
          l = lns if lns.shape[0] in (2,3) else lns.T
          if np.allclose(l[:,0], l[:,-1]):
              for ii in range(n):
                  lns = p*l + q/2*(np.roll(l, -1, 1) + np.roll(l, 1, 1))
          else:
              l = np.array(l)
              for ii in range(n):
                  l[1:-1] = p*l[1:-1] + q/2*(l[:-2] + l[2:])
          if lns.shape != l.shape: l = l.T
          l.setflags(write=lns.flags['WRITEABLE'])
          return l
      elif not pimms.is_scalar(lns):
          u = [smooth_lines(u, n=n, inertia=inertia) for u in lns]
          return (tuple(u)       if is_tuple(lns)                else
                  pyr.pvector(u) if isinstance(lns, pyr.PVector) else
                  np.asarray(u)  if pimms.is_nparray(lns)        else
                  type(lns)(u))
      else: raise ValueError('smooth_lines could not interpret object: %s' % lns)