code: """Functions to plot on circle as for connectivity
  """
  from __future__ import print_function
  
  # Authors: Alexandre Gramfort <alexandre.gramfort@telecom-paristech.fr>
  #          Denis Engemann <denis.engemann@gmail.com>
  #          Martin Luessi <mluessi@nmr.mgh.harvard.edu>
  #
  # License: Simplified BSD
  
  
  from itertools import cycle
  from functools import partial
  
  import numpy as np
  
  from .utils import plt_show
  from ..externals.six import string_types
  
  
  def circular_layout(node_names, node_order, start_pos=90, start_between=True,
                      group_boundaries=None, group_sep=10):
      """Create layout arranging nodes on a circle.
  
      Parameters
      ----------
      node_names : list of str
          Node names.
      node_order : list of str
          List with node names defining the order in which the nodes are
          arranged. Must have the elements as node_names but the order can be
          different. The nodes are arranged clockwise starting at "start_pos"
          degrees.
      start_pos : float
          Angle in degrees that defines where the first node is plotted.
      start_between : bool
          If True, the layout starts with the position between the nodes. This is
          the same as adding "180. / len(node_names)" to start_pos.
      group_boundaries : None | array-like
          List of of boundaries between groups at which point a "group_sep" will
          be inserted. E.g. "[0, len(node_names) / 2]" will create two groups.
      group_sep : float
          Group separation angle in degrees. See "group_boundaries".
  
      Returns
      -------
      node_angles : array, shape=(len(node_names,))
          Node angles in degrees.
      """
      n_nodes = len(node_names)
  
      if len(node_order) != n_nodes:
          raise ValueError('node_order has to be the same length as node_names')
  
      if group_boundaries is not None:
          boundaries = np.array(group_boundaries, dtype=np.int)
          if np.any(boundaries >= n_nodes) or np.any(boundaries < 0):
              raise ValueError('"group_boundaries" has to be between 0 and '
                               'n_nodes - 1.')
          if len(boundaries) > 1 and np.any(np.diff(boundaries) <= 0):
              raise ValueError('"group_boundaries" must have non-decreasing '
                               'values.')
          n_group_sep = len(group_boundaries)
      else:
          n_group_sep = 0
          boundaries = None
  
      # convert it to a list with indices
      node_order = [node_order.index(name) for name in node_names]
      node_order = np.array(node_order)
      if len(np.unique(node_order)) != n_nodes:
          raise ValueError('node_order has repeated entries')
  
      node_sep = (360. - n_group_sep * group_sep) / n_nodes
  
      if start_between:
          start_pos += node_sep / 2
  
          if boundaries is not None and boundaries[0] == 0:
              # special case when a group separator is at the start
              start_pos += group_sep / 2
              boundaries = boundaries[1:] if n_group_sep > 1 else None
  
      node_angles = np.ones(n_nodes, dtype=np.float) * node_sep
      node_angles[0] = start_pos
      if boundaries is not None:
          node_angles[boundaries] += group_sep
  
      node_angles = np.cumsum(node_angles)[node_order]
  
      return node_angles
  
  
  def _plot_connectivity_circle_onpick(event, fig=None, axes=None, indices=None,
                                       n_nodes=0, node_angles=None,
                                       ylim=[9, 10]):
      """Isolates connections around a single node when user left clicks a node.
  
      On right click, resets all connections."""
      if event.inaxes != axes:
          return
  
      if event.button == 1:  # left click
          # click must be near node radius
          if not ylim[0] <= event.ydata <= ylim[1]:
              return
  
          # all angles in range [0, 2*pi]
          node_angles = node_angles % (np.pi * 2)
          node = np.argmin(np.abs(event.xdata - node_angles))
  
          patches = event.inaxes.patches
          for ii, (x, y) in enumerate(zip(indices[0], indices[1])):
              patches[ii].set_visible(node in [x, y])
          fig.canvas.draw()
      elif event.button == 3:  # right click
          patches = event.inaxes.patches
          for ii in range(np.size(indices, axis=1)):
              patches[ii].set_visible(True)
          fig.canvas.draw()
  
  
  def plot_connectivity_circle(con, node_names, indices=None, n_lines=None,
                               node_angles=None, node_width=None,
                               node_colors=None, facecolor='black',
                               textcolor='white', node_edgecolor='black',
                               linewidth=1.5, colormap='hot', vmin=None,
                               vmax=None, colorbar=True, title=None,
                               colorbar_size=0.2, colorbar_pos=(-0.3, 0.1),
                               fontsize_title=12, fontsize_names=8,
                               fontsize_colorbar=8, padding=6.,
                               fig=None, subplot=111, interactive=True,
                               node_linewidth=2., show=True):
      """Visualize connectivity as a circular graph.
  
      Note: This code is based on the circle graph example by Nicolas P. Rougier
      http://www.labri.fr/perso/nrougier/coding/.
  
      Parameters
      ----------
      con : array
          Connectivity scores. Can be a square matrix, or a 1D array. If a 1D
          array is provided, "indices" has to be used to define the connection
          indices.
      node_names : list of str
          Node names. The order corresponds to the order in con.
      indices : tuple of arrays | None
          Two arrays with indices of connections for which the connections
          strenghts are defined in con. Only needed if con is a 1D array.
      n_lines : int | None
          If not None, only the n_lines strongest connections (strength=abs(con))
          are drawn.
      node_angles : array, shape=(len(node_names,)) | None
          Array with node positions in degrees. If None, the nodes are equally
          spaced on the circle. See mne.viz.circular_layout.
      node_width : float | None
          Width of each node in degrees. If None, the minimum angle between any
          two nodes is used as the width.
      node_colors : list of tuples | list of str
          List with the color to use for each node. If fewer colors than nodes
          are provided, the colors will be repeated. Any color supported by
          matplotlib can be used, e.g., RGBA tuples, named colors.
      facecolor : str
          Color to use for background. See matplotlib.colors.
      textcolor : str
          Color to use for text. See matplotlib.colors.
      node_edgecolor : str
          Color to use for lines around nodes. See matplotlib.colors.
      linewidth : float
          Line width to use for connections.
      colormap : str
          Colormap to use for coloring the connections.
      vmin : float | None
          Minimum value for colormap. If None, it is determined automatically.
      vmax : float | None
          Maximum value for colormap. If None, it is determined automatically.
      colorbar : bool
          Display a colorbar or not.
      title : str
          The figure title.
      colorbar_size : float
          Size of the colorbar.
      colorbar_pos : 2-tuple
          Position of the colorbar.
      fontsize_title : int
          Font size to use for title.
      fontsize_names : int
          Font size to use for node names.
      fontsize_colorbar : int
          Font size to use for colorbar.
      padding : float
          Space to add around figure to accommodate long labels.
      fig : None | instance of matplotlib.pyplot.Figure
          The figure to use. If None, a new figure with the specified background
          color will be created.
      subplot : int | 3-tuple
          Location of the subplot when creating figures with multiple plots. E.g.
          121 or (1, 2, 1) for 1 row, 2 columns, plot 1. See
          matplotlib.pyplot.subplot.
      interactive : bool
          When enabled, left-click on a node to show only connections to that
          node. Right-click shows all connections.
      node_linewidth : float
          Line with for nodes.
      show : bool
          Show figure if True.
  
      Returns
      -------
      fig : instance of matplotlib.pyplot.Figure
          The figure handle.
      axes : instance of matplotlib.axes.PolarAxesSubplot
          The subplot handle.
      """
      import matplotlib.pyplot as plt
      import matplotlib.path as m_path
      import matplotlib.patches as m_patches
  
      n_nodes = len(node_names)
  
      if node_angles is not None:
          if len(node_angles) != n_nodes:
              raise ValueError('node_angles has to be the same length '
                               'as node_names')
          # convert it to radians
          node_angles = node_angles * np.pi / 180
      else:
          # uniform layout on unit circle
          node_angles = np.linspace(0, 2 * np.pi, n_nodes, endpoint=False)
  
      if node_width is None:
          # widths correspond to the minimum angle between two nodes
          dist_mat = node_angles[None, :] - node_angles[:, None]
          dist_mat[np.diag_indices(n_nodes)] = 1e9
          node_width = np.min(np.abs(dist_mat))
      else:
          node_width = node_width * np.pi / 180
  
      if node_colors is not None:
          if len(node_colors) < n_nodes:
              node_colors = cycle(node_colors)
      else:
          # assign colors using colormap
          node_colors = [plt.cm.spectral(i / float(n_nodes))
                         for i in range(n_nodes)]
  
      # handle 1D and 2D connectivity information
      if con.ndim == 1:
          if indices is None:
              raise ValueError('indices has to be provided if con.ndim == 1')
      elif con.ndim == 2:
          if con.shape[0] != n_nodes or con.shape[1] != n_nodes:
              raise ValueError('con has to be 1D or a square matrix')
          # we use the lower-triangular part
          indices = np.tril_indices(n_nodes, -1)
          con = con[indices]
      else:
          raise ValueError('con has to be 1D or a square matrix')
  
      # get the colormap
      if isinstance(colormap, string_types):
          colormap = plt.get_cmap(colormap)
  
      # Make figure background the same colors as axes
      if fig is None:
          fig = plt.figure(figsize=(8, 8), facecolor=facecolor)
  
      # Use a polar axes
      if not isinstance(subplot, tuple):
          subplot = (subplot,)
      axes = plt.subplot(*subplot, polar=True, axisbg=facecolor)
  
      # No ticks, we'll put our own
      plt.xticks([])
      plt.yticks([])
  
      # Set y axes limit, add additional space if requested
      plt.ylim(0, 10 + padding)
  
      # Remove the black axes border which may obscure the labels
      axes.spines['polar'].set_visible(False)
  
      # Draw lines between connected nodes, only draw the strongest connections
      if n_lines is not None and len(con) > n_lines:
          con_thresh = np.sort(np.abs(con).ravel())[-n_lines]
      else:
          con_thresh = 0.
  
      # get the connections which we are drawing and sort by connection strength
      # this will allow us to draw the strongest connections first
      con_abs = np.abs(con)
      con_draw_idx = np.where(con_abs >= con_thresh)[0]
  
      con = con[con_draw_idx]
      con_abs = con_abs[con_draw_idx]
      indices = [ind[con_draw_idx] for ind in indices]
  
      # now sort them
      sort_idx = np.argsort(con_abs)
      con_abs = con_abs[sort_idx]
      con = con[sort_idx]
      indices = [ind[sort_idx] for ind in indices]
  
      # Get vmin vmax for color scaling
      if vmin is None:
          vmin = np.min(con[np.abs(con) >= con_thresh])
      if vmax is None:
          vmax = np.max(con)
      vrange = vmax - vmin
  
      # We want to add some "noise" to the start and end position of the
      # edges: We modulate the noise with the number of connections of the
      # node and the connection strength, such that the strongest connections
      # are closer to the node center
      nodes_n_con = np.zeros((n_nodes), dtype=np.int)
      for i, j in zip(indices[0], indices[1]):
          nodes_n_con[i] += 1
          nodes_n_con[j] += 1
  
      # initialize random number generator so plot is reproducible
      rng = np.random.mtrand.RandomState(seed=0)
  
      n_con = len(indices[0])
      noise_max = 0.25 * node_width
      start_noise = rng.uniform(-noise_max, noise_max, n_con)
      end_noise = rng.uniform(-noise_max, noise_max, n_con)
  
      nodes_n_con_seen = np.zeros_like(nodes_n_con)
      for i, (start, end) in enumerate(zip(indices[0], indices[1])):
          nodes_n_con_seen[start] += 1
          nodes_n_con_seen[end] += 1
  
          start_noise[i] *= ((nodes_n_con[start] - nodes_n_con_seen[start]) /
                             float(nodes_n_con[start]))
          end_noise[i] *= ((nodes_n_con[end] - nodes_n_con_seen[end]) /
                           float(nodes_n_con[end]))
  
      # scale connectivity for colormap (vmin<=>0, vmax<=>1)
      con_val_scaled = (con - vmin) / vrange
  
      # Finally, we draw the connections
      for pos, (i, j) in enumerate(zip(indices[0], indices[1])):
          # Start point
          t0, r0 = node_angles[i], 10
  
          # End point
          t1, r1 = node_angles[j], 10
  
          # Some noise in start and end point
          t0 += start_noise[pos]
          t1 += end_noise[pos]
  
          verts = [(t0, r0), (t0, 5), (t1, 5), (t1, r1)]
          codes = [m_path.Path.MOVETO, m_path.Path.CURVE4, m_path.Path.CURVE4,
                   m_path.Path.LINETO]
          path = m_path.Path(verts, codes)
  
          color = colormap(con_val_scaled[pos])
  
          # Actual line
          patch = m_patches.PathPatch(path, fill=False, edgecolor=color,
                                      linewidth=linewidth, alpha=1.)
          axes.add_patch(patch)
  
      # Draw ring with colored nodes
      height = np.ones(n_nodes) * 1.0
      bars = axes.bar(node_angles, height, width=node_width, bottom=9,
                      edgecolor=node_edgecolor, lw=node_linewidth,
                      facecolor='.9', align='center')
  
      for bar, color in zip(bars, node_colors):
          bar.set_facecolor(color)
  
      # Draw node labels
      angles_deg = 180 * node_angles / np.pi
      for name, angle_rad, angle_deg in zip(node_names, node_angles, angles_deg):
          if angle_deg >= 270:
              ha = 'left'
          else:
              # Flip the label, so text is always upright
              angle_deg += 180
              ha = 'right'
  
          axes.text(angle_rad, 10.4, name, size=fontsize_names,
                    rotation=angle_deg, rotation_mode='anchor',
                    horizontalalignment=ha, verticalalignment='center',
                    color=textcolor)
  
      if title is not None:
          plt.title(title, color=textcolor, fontsize=fontsize_title,
                    axes=axes)
  
      if colorbar:
          sm = plt.cm.ScalarMappable(cmap=colormap,
                                     norm=plt.Normalize(vmin, vmax))
          sm.set_array(np.linspace(vmin, vmax))
          cb = plt.colorbar(sm, ax=axes, use_gridspec=False,
                            shrink=colorbar_size,
                            anchor=colorbar_pos)
          cb_yticks = plt.getp(cb.ax.axes, 'yticklabels')
          cb.ax.tick_params(labelsize=fontsize_colorbar)
          plt.setp(cb_yticks, color=textcolor)
  
      # Add callback for interaction
      if interactive:
          callback = partial(_plot_connectivity_circle_onpick, fig=fig,
                             axes=axes, indices=indices, n_nodes=n_nodes,
                             node_angles=node_angles)
  
          fig.canvas.mpl_connect('button_press_event', callback)
  
      plt_show(show)
      return fig, axes
   # Authors: Alexandre Gramfort <alexandre.gramfort@telecom-paristech.fr>
  #          Matti Hamalainen <msh@nmr.mgh.harvard.edu>
  #          Teon Brooks <teon.brooks@gmail.com>
  #
  # License: BSD (3-clause)
  
  from collections import Counter
  from copy import deepcopy
  from datetime import datetime as dt
  import os.path as op
  import re
  
  import numpy as np
  from scipy import linalg
  
  from .pick import channel_type
  from .constants import FIFF
  from .open import fiff_open
  from .tree import dir_tree_find
  from .tag import read_tag, find_tag
  from .proj import _read_proj, _write_proj, _uniquify_projs, _normalize_proj
  from .ctf_comp import read_ctf_comp, write_ctf_comp
  from .write import (start_file, end_file, start_block, end_block,
                      write_string, write_dig_point, write_float, write_int,
                      write_coord_trans, write_ch_info, write_name_list,
                      write_julian, write_float_matrix)
  from .proc_history import _read_proc_history, _write_proc_history
  from ..utils import logger, verbose, warn
  from .. import __version__
  from ..externals.six import b, BytesIO, string_types, text_type
  
  
  _kind_dict = dict(
      eeg=(FIFF.FIFFV_EEG_CH, FIFF.FIFFV_COIL_EEG, FIFF.FIFF_UNIT_V),
      mag=(FIFF.FIFFV_MEG_CH, FIFF.FIFFV_COIL_VV_MAG_T3, FIFF.FIFF_UNIT_T),
      grad=(FIFF.FIFFV_MEG_CH, FIFF.FIFFV_COIL_VV_PLANAR_T1, FIFF.FIFF_UNIT_T_M),
      ref_meg=(FIFF.FIFFV_REF_MEG_CH, FIFF.FIFFV_COIL_VV_MAG_T3,
               FIFF.FIFF_UNIT_T),
      misc=(FIFF.FIFFV_MISC_CH, FIFF.FIFFV_COIL_NONE, FIFF.FIFF_UNIT_NONE),
      stim=(FIFF.FIFFV_STIM_CH, FIFF.FIFFV_COIL_NONE, FIFF.FIFF_UNIT_V),
      eog=(FIFF.FIFFV_EOG_CH, FIFF.FIFFV_COIL_NONE, FIFF.FIFF_UNIT_V),
      ecg=(FIFF.FIFFV_ECG_CH, FIFF.FIFFV_COIL_NONE, FIFF.FIFF_UNIT_V),
      seeg=(FIFF.FIFFV_SEEG_CH, FIFF.FIFFV_COIL_EEG, FIFF.FIFF_UNIT_V),
      bio=(FIFF.FIFFV_BIO_CH, FIFF.FIFFV_COIL_NONE, FIFF.FIFF_UNIT_V),
      ecog=(FIFF.FIFFV_ECOG_CH, FIFF.FIFFV_COIL_EEG, FIFF.FIFF_UNIT_V),
      hbo=(FIFF.FIFFV_FNIRS_CH, FIFF.FIFFV_COIL_FNIRS_HBO, FIFF.FIFF_UNIT_MOL),
      hbr=(FIFF.FIFFV_FNIRS_CH, FIFF.FIFFV_COIL_FNIRS_HBR, FIFF.FIFF_UNIT_MOL)
  )
  
  
  def _summarize_str(st):
      """Aux function"""
      return st[:56][::-1].split(',', 1)[-1][::-1] + ', ...'
  
  
  class Info(dict):
      """Information about the recording.
  
      This data structure behaves like a dictionary. It contains all meta-data
      that is available for a recording.
  
      The attributes listed below are the possible dictionary entries:
  
      Attributes
      ----------
      bads : list of str
          List of bad (noisy/broken) channels, by name. These channels will by
          default be ignored by many processing steps.
      ch_names : list-like of str (read-only)
          The names of the channels.
          This object behaves like a read-only Python list. Behind the scenes
          it iterates over the channels dictionaries in `info['chs']`:
          `info['ch_names'][x] == info['chs'][x]['ch_name']`
      chs : list of dict
          A list of channel information structures.
          See: :ref:`faq` for details.
      comps : list of dict
          CTF software gradient compensation data.
          See: :ref:`faq` for details.
      custom_ref_applied : bool
          Whether a custom (=other than average) reference has been applied to
          the EEG data. This flag is checked by some algorithms that require an
          average reference to be set.
      events : list of dict
          Event list, usually extracted from the stim channels.
          See: :ref:`faq` for details.
      hpi_results : list of dict
          Head position indicator (HPI) digitization points and fit information
          (e.g., the resulting transform). See: :ref:`faq` for details.
      meas_date : list of int
          The first element of this list is a POSIX timestamp (milliseconds since
          1970-01-01 00:00:00) denoting the date and time at which the
          measurement was taken. The second element is the number of
          microseconds.
      nchan : int
          Number of channels.
      projs : list of dict
          List of SSP operators that operate on the data.
          See: :ref:`faq` for details.
      sfreq : float
          Sampling frequency in Hertz.
          See: :ref:`faq` for details.
      acq_pars : str | None
          MEG system acquition parameters.
      acq_stim : str | None
          MEG system stimulus parameters.
      buffer_size_sec : float | None
          Buffer size (in seconds) when reading the raw data in chunks.
      ctf_head_t : dict | None
          The transformation from 4D/CTF head coordinates to Neuromag head
          coordinates. This is only present in 4D/CTF data.
          See: :ref:`faq` for details.
      description : str | None
          String description of the recording.
      dev_ctf_t : dict | None
          The transformation from device coordinates to 4D/CTF head coordinates.
          This is only present in 4D/CTF data.
          See: :ref:`faq` for details.
      dev_head_t : dict | None
          The device to head transformation.
          See: :ref:`faq` for details.
      dig : list of dict | None
          The Polhemus digitization data in head coordinates.
          See: :ref:`faq` for details.
      experimentor : str | None
          Name of the person that ran the experiment.
      file_id : dict | None
          The fif ID datastructure of the measurement file.
          See: :ref:`faq` for details.
      filename : str | None
          The name of the file that provided the raw data.
      highpass : float | None
          Highpass corner frequency in Hertz. Zero indicates a DC recording.
      hpi_meas : list of dict | None
          HPI measurements that were taken at the start of the recording
          (e.g. coil frequencies).
      hpi_subsystem : dict | None
          Information about the HPI subsystem that was used (e.g., event
          channel used for cHPI measurements).
      line_freq : float | None
          Frequency of the power line in Hertz.
      lowpass : float | None
          Lowpass corner frequency in Hertz.
      meas_id : dict | None
          The ID assigned to this measurement by the acquisition system or during
          file conversion.
          See: :ref:`faq` for details.
      proj_id : int | None
          ID number of the project the experiment belongs to.
      proj_name : str | None
          Name of the project the experiment belongs to.
      subject_info : dict | None
          Information about the subject.
      proc_history : list of dict | None | not present in dict
          The SSS info, the CTC correction and the calibaraions from the SSS
          processing logs inside of a raw file.
          See: :ref:`faq` for details.
      """
  
      def copy(self):
          """Copy the instance
  
          Returns
          -------
          info : instance of Info
              The copied info.
          """
          return Info(deepcopy(self))
  
      def normalize_proj(self):
          """(Re-)Normalize projection vectors after subselection
  
          Applying projection after sub-selecting a set of channels that
          were originally used to compute the original projection vectors
          can be dangerous (e.g., if few channels remain, most power was
          in channels that are no longer picked, etc.). By default, mne
          will emit a warning when this is done.
  
          This function will re-normalize projectors to use only the
          remaining channels, thus avoiding that warning. Only use this
          function if you're confident that the projection vectors still
          adequately capture the original signal of interest.
          """
          _normalize_proj(self)
  
      def __repr__(self):
          """Summarize info instead of printing all"""
          strs = ['<Info | %s non-empty fields']
          non_empty = 0
          for k, v in self.items():
              if k in ['bads', 'ch_names']:
                  entr = (', '.join(b for ii, b in enumerate(v) if ii < 10)
                          if v else '0 items')
                  if len(entr) >= 56:
                      # get rid of of half printed ch names
                      entr = _summarize_str(entr)
              elif k == 'filename' and v:
                  path, fname = op.split(v)
                  entr = path[:10] + '.../' + fname
              elif k == 'projs' and v:
                  entr = ', '.join(p['desc'] + ': o%s' %
                                   {0: 'ff', 1: 'n'}[p['active']] for p in v)
                  if len(entr) >= 56:
                      entr = _summarize_str(entr)
              elif k == 'meas_date' and np.iterable(v):
                  # first entry in meas_date is meaningful
                  entr = dt.fromtimestamp(v[0]).strftime('%Y-%m-%d %H:%M:%S')
              elif k == 'kit_system_id' and v is not None:
                  from .kit.constants import SYSNAMES as KIT_SYSNAMES
                  entr = '%i (%s)' % (v, KIT_SYSNAMES.get(v, 'unknown'))
              else:
                  this_len = (len(v) if hasattr(v, '__len__') else
                              ('%s' % v if v is not None else None))
                  entr = (('%d items' % this_len) if isinstance(this_len, int)
                          else ('%s' % this_len if this_len else ''))
              if entr:
                  non_empty += 1
                  entr = ' | ' + entr
              if k == 'chs':
                  ch_types = [channel_type(self, idx) for idx in range(len(v))]
                  ch_counts = Counter(ch_types)
                  entr += " (%s)" % ', '.join("%s: %d" % (ch_type.upper(), count)
                                              for ch_type, count
                                              in ch_counts.items())
              strs.append('%s : %s%s' % (k, str(type(v))[7:-2], entr))
              if k in ['sfreq', 'lowpass', 'highpass']:
                  strs[-1] += ' Hz'
          strs_non_empty = sorted(s for s in strs if '|' in s)
          strs_empty = sorted(s for s in strs if '|' not in s)
          st = '\n    '.join(strs_non_empty + strs_empty)
          st += '\n>'
          st %= non_empty
          return st
  
      def _check_consistency(self):
          """Do some self-consistency checks and datatype tweaks"""
          missing = [bad for bad in self['bads'] if bad not in self['ch_names']]
          if len(missing) > 0:
              raise RuntimeError('bad channel(s) %s marked do not exist in info'
                                 % (missing,))
  
          chs = [ch['ch_name'] for ch in self['chs']]
          if len(self['ch_names']) != len(chs) or any(
                  ch_1 != ch_2 for ch_1, ch_2 in zip(self['ch_names'], chs)) or \
                  self['nchan'] != len(chs):
              raise RuntimeError('info channel name inconsistency detected, '
                                 'please notify mne-python developers')
  
          # make sure we have the proper datatypes
          for key in ('sfreq', 'highpass', 'lowpass'):
              if self.get(key) is not None:
                  self[key] = float(self[key])
  
          # make sure channel names are unique
          unique_ids = np.unique(self['ch_names'], return_index=True)[1]
          if len(unique_ids) != self['nchan']:
              dups = set(self['ch_names'][x]
                         for x in np.setdiff1d(range(self['nchan']), unique_ids))
              raise RuntimeError('Channel names are not unique, found '
                                 'duplicates for: %s' % dups)
  
      def _update_redundant(self):
          """Update the redundant entries"""
          self['ch_names'] = [ch['ch_name'] for ch in self['chs']]
          self['nchan'] = len(self['chs'])
  
  
  def read_fiducials(fname):
      """Read fiducials from a fiff file
  
      Parameters
      ----------
      fname : str
          The filename to read.
  
      Returns
      -------
      pts : list of dicts
          List of digitizer points (each point in a dict).
      coord_frame : int
          The coordinate frame of the points (one of
          mne.io.constants.FIFF.FIFFV_COORD_...)
      """
      fid, tree, _ = fiff_open(fname)
      with fid:
          isotrak = dir_tree_find(tree, FIFF.FIFFB_ISOTRAK)
          isotrak = isotrak[0]
          pts = []
          coord_frame = FIFF.FIFFV_COORD_UNKNOWN
          for k in range(isotrak['nent']):
              kind = isotrak['directory'][k].kind
              pos = isotrak['directory'][k].pos
              if kind == FIFF.FIFF_DIG_POINT:
                  tag = read_tag(fid, pos)
                  pts.append(tag.data)
              elif kind == FIFF.FIFF_MNE_COORD_FRAME:
                  tag = read_tag(fid, pos)
                  coord_frame = tag.data[0]
  
      if coord_frame == FIFF.FIFFV_COORD_UNKNOWN:
          err = ("No coordinate frame was found in the file %r, it is probably "
                 "not a valid fiducials file." % fname)
          raise ValueError(err)
  
      # coord_frame is not stored in the tag
      for pt in pts:
          pt['coord_frame'] = coord_frame
  
      return pts, coord_frame
  
  
  def write_fiducials(fname, pts, coord_frame=0):
      """Write fiducials to a fiff file
  
      Parameters
      ----------
      fname : str
          Destination file name.
      pts : iterator of dict
          Iterator through digitizer points. Each point is a dictionary with
          the keys 'kind', 'ident' and 'r'.
      coord_frame : int
          The coordinate frame of the points (one of
          mne.io.constants.FIFF.FIFFV_COORD_...)
      """
      pts_frames = set((pt.get('coord_frame', coord_frame) for pt in pts))
      bad_frames = pts_frames - set((coord_frame,))
      if len(bad_frames) > 0:
          err = ("Points have coord_frame entries that are incompatible with "
                 "coord_frame=%i: %s." % (coord_frame, str(tuple(bad_frames))))
          raise ValueError(err)
  
      fid = start_file(fname)
      start_block(fid, FIFF.FIFFB_ISOTRAK)
      write_int(fid, FIFF.FIFF_MNE_COORD_FRAME, coord_frame)
      for pt in pts:
          write_dig_point(fid, pt)
  
      end_block(fid, FIFF.FIFFB_ISOTRAK)
      end_file(fid)
  
  
  def _read_dig_fif(fid, meas_info):
      """Helper to read digitizer data from a FIFF file"""
      isotrak = dir_tree_find(meas_info, FIFF.FIFFB_ISOTRAK)
      dig = None
      if len(isotrak) == 0:
          logger.info('Isotrak not found')
      elif len(isotrak) > 1:
          warn('Multiple Isotrak found')
      else:
          isotrak = isotrak[0]
          dig = []
          for k in range(isotrak['nent']):
              kind = isotrak['directory'][k].kind
              pos = isotrak['directory'][k].pos
              if kind == FIFF.FIFF_DIG_POINT:
                  tag = read_tag(fid, pos)
                  dig.append(tag.data)
                  dig[-1]['coord_frame'] = FIFF.FIFFV_COORD_HEAD
      return dig
  
  
  def _read_dig_points(fname, comments='%', unit='auto'):
      """Read digitizer data from a text file.
  
      If fname ends in .hsp or .esp, the function assumes digitizer files in [m],
      otherwise it assumes space-delimited text files in [mm].
  
      Parameters
      ----------
      fname : str
          The filepath of space delimited file with points.
      comments : str
          The character used to indicate the start of a comment;
          Default: '%'.
      unit : 'auto' | 'm' | 'cm' | 'mm'
          Unit of the digitizer files (hsp and elp). If not 'm', coordinates will
          be rescaled to 'm'. Default is 'auto', which assumes 'm' for *.hsp and
          *.elp files and 'mm' for *.txt files, corresponding to the known
          Polhemus export formats.
  
      Returns
      -------
      dig_points : np.ndarray, shape (n_points, 3)
          Array of dig points in [m].
      """
      if unit not in ('auto', 'm', 'mm', 'cm'):
          raise ValueError('unit must be one of "auto", "m", "mm", or "cm"')
  
      _, ext = op.splitext(fname)
      if ext == '.elp' or ext == '.hsp':
          with open(fname) as fid:
              file_str = fid.read()
          value_pattern = "\-?\d+\.?\d*e?\-?\d*"
          coord_pattern = "({0})\s+({0})\s+({0})\s*$".format(value_pattern)
          if ext == '.hsp':
              coord_pattern = '^' + coord_pattern
          points_str = [m.groups() for m in re.finditer(coord_pattern, file_str,
                                                        re.MULTILINE)]
          dig_points = np.array(points_str, dtype=float)
      else:
          dig_points = np.loadtxt(fname, comments=comments, ndmin=2)
          if unit == 'auto':
              unit = 'mm'
  
      if dig_points.shape[-1] != 3:
          err = 'Data must be (n, 3) instead of %s' % (dig_points.shape,)
          raise ValueError(err)
  
      if unit == 'mm':
          dig_points /= 1000.
      elif unit == 'cm':
          dig_points /= 100.
  
      return dig_points
  
  
  def _write_dig_points(fname, dig_points):
      """Write points to text file
  
      Parameters
      ----------
      fname : str
          Path to the file to write. The kind of file to write is determined
          based on the extension: '.txt' for tab separated text file.
      dig_points : numpy.ndarray, shape (n_points, 3)
          Points.
      """
      _, ext = op.splitext(fname)
      dig_points = np.asarray(dig_points)
      if (dig_points.ndim != 2) or (dig_points.shape[1] != 3):
          err = ("Points must be of shape (n_points, 3), "
                 "not %s" % (dig_points.shape,))
          raise ValueError(err)
  
      if ext == '.txt':
          with open(fname, 'wb') as fid:
              version = __version__
              now = dt.now().strftime("%I:%M%p on %B %d, %Y")
              fid.write(b("% Ascii 3D points file created by mne-python version "
                          "{version} at {now}\n".format(version=version,
                                                        now=now)))
              fid.write(b("% {N} 3D points, "
                          "x y z per line\n".format(N=len(dig_points))))
              np.savetxt(fid, dig_points, delimiter='\t', newline='\n')
      else:
          msg = "Unrecognized extension: %r. Need '.txt'." % ext
          raise ValueError(msg)
  
  
  def _make_dig_points(nasion=None, lpa=None, rpa=None, hpi=None,
                       dig_points=None, dig_ch_pos=None):
      """Constructs digitizer info for the info.
  
      Parameters
      ----------
      nasion : array-like | numpy.ndarray, shape (3,) | None
          Point designated as the nasion point.
      lpa : array-like |  numpy.ndarray, shape (3,) | None
          Point designated as the left auricular point.
      rpa : array-like |  numpy.ndarray, shape (3,) | None
          Point designated as the right auricular point.
      hpi : array-like | numpy.ndarray, shape (n_points, 3) | None
          Points designated as head position indicator points.
      dig_points : array-like | numpy.ndarray, shape (n_points, 3)
          Points designed as the headshape points.
      dig_ch_pos : dict
          Dict of EEG channel positions.
  
      Returns
      -------
      dig : list
          List of digitizer points to be added to the info['dig'].
      """
      dig = []
      if lpa is not None:
          lpa = np.asarray(lpa)
          if lpa.shape == (3,):
              dig.append({'r': lpa, 'ident': FIFF.FIFFV_POINT_LPA,
                          'kind': FIFF.FIFFV_POINT_CARDINAL,
                          'coord_frame': FIFF.FIFFV_COORD_HEAD})
          else:
              msg = ('LPA should have the shape (3,) instead of %s'
                     % (lpa.shape,))
              raise ValueError(msg)
      if nasion is not None:
          nasion = np.asarray(nasion)
          if nasion.shape == (3,):
              dig.append({'r': nasion, 'ident': FIFF.FIFFV_POINT_NASION,
                          'kind': FIFF.FIFFV_POINT_CARDINAL,
                          'coord_frame': FIFF.FIFFV_COORD_HEAD})
          else:
              msg = ('Nasion should have the shape (3,) instead of %s'
                     % (nasion.shape,))
              raise ValueError(msg)
      if rpa is not None:
          rpa = np.asarray(rpa)
          if rpa.shape == (3,):
              dig.append({'r': rpa, 'ident': FIFF.FIFFV_POINT_RPA,
                          'kind': FIFF.FIFFV_POINT_CARDINAL,
                          'coord_frame': FIFF.FIFFV_COORD_HEAD})
          else:
              msg = ('RPA should have the shape (3,) instead of %s'
                     % (rpa.shape,))
              raise ValueError(msg)
      if hpi is not None:
          hpi = np.asarray(hpi)
          if hpi.shape[1] == 3:
              for idx, point in enumerate(hpi):
                  dig.append({'r': point, 'ident': idx + 1,
                              'kind': FIFF.FIFFV_POINT_HPI,
                              'coord_frame': FIFF.FIFFV_COORD_HEAD})
          else:
              msg = ('HPI should have the shape (n_points, 3) instead of '
                     '%s' % (hpi.shape,))
              raise ValueError(msg)
      if dig_points is not None:
          dig_points = np.asarray(dig_points)
          if dig_points.shape[1] == 3:
              for idx, point in enumerate(dig_points):
                  dig.append({'r': point, 'ident': idx + 1,
                              'kind': FIFF.FIFFV_POINT_EXTRA,
                              'coord_frame': FIFF.FIFFV_COORD_HEAD})
          else:
              msg = ('Points should have the shape (n_points, 3) instead of '
                     '%s' % (dig_points.shape,))
              raise ValueError(msg)
      if dig_ch_pos is not None:
          keys = sorted(dig_ch_pos.keys())
          for key in keys:
              dig.append({'r': dig_ch_pos[key], 'ident': int(key[-3:]),
                          'kind': FIFF.FIFFV_POINT_EEG,
                          'coord_frame': FIFF.FIFFV_COORD_HEAD})
      return dig
  
  
  @verbose
  def read_info(fname, verbose=None):
      """Read measurement info from a file
  
      Parameters
      ----------
      fname : str
          File name.
      verbose : bool, str, int, or None
          If not None, override default verbose level (see mne.verbose).
  
      Returns
      -------
      info : instance of Info
         Measurement information for the dataset.
      """
      f, tree, _ = fiff_open(fname)
      with f as fid:
          info = read_meas_info(fid, tree)[0]
      return info
  
  
  def read_bad_channels(fid, node):
      """Read bad channels
  
      Parameters
      ----------
      fid : file
          The file descriptor.
  
      node : dict
          The node of the FIF tree that contains info on the bad channels.
  
      Returns
      -------
      bads : list
          A list of bad channel's names.
      """
      nodes = dir_tree_find(node, FIFF.FIFFB_MNE_BAD_CHANNELS)
  
      bads = []
      if len(nodes) > 0:
          for node in nodes:
              tag = find_tag(fid, node, FIFF.FIFF_MNE_CH_NAME_LIST)
              if tag is not None and tag.data is not None:
                  bads = tag.data.split(':')
      return bads
  
  
  @verbose
  def read_meas_info(fid, tree, clean_bads=False, verbose=None):
      """Read the measurement info
  
      Parameters
      ----------
      fid : file
          Open file descriptor.
      tree : tree
          FIF tree structure.
      clean_bads : bool
          If True, clean info['bads'] before running consistency check.
          Should only be needed for old files where we did not check bads
          before saving.
      verbose : bool, str, int, or None
          If not None, override default verbose level (see mne.verbose).
  
      Returns
      -------
      info : instance of Info
         Info on dataset.
      meas : dict
          Node in tree that contains the info.
      """
  
      #   Find the desired blocks
      meas = dir_tree_find(tree, FIFF.FIFFB_MEAS)
      if len(meas) == 0:
          raise ValueError('Could not find measurement data')
      if len(meas) > 1:
          raise ValueError('Cannot read more that 1 measurement data')
      meas = meas[0]
  
      meas_info = dir_tree_find(meas, FIFF.FIFFB_MEAS_INFO)
      if len(meas_info) == 0:
          raise ValueError('Could not find measurement info')
      if len(meas_info) > 1:
          raise ValueError('Cannot read more that 1 measurement info')
      meas_info = meas_info[0]
  
      #   Read measurement info
      dev_head_t = None
      ctf_head_t = None
      dev_ctf_t = None
      meas_date = None
      highpass = None
      lowpass = None
      nchan = None
      sfreq = None
      chs = []
      experimenter = None
      description = None
      proj_id = None
      proj_name = None
      line_freq = None
      custom_ref_applied = False
      xplotter_layout = None
      kit_system_id = None
      for k in range(meas_info['nent']):
          kind = meas_info['directory'][k].kind
          pos = meas_info['directory'][k].pos
          if kind == FIFF.FIFF_NCHAN:
              tag = read_tag(fid, pos)
              nchan = int(tag.data)
          elif kind == FIFF.FIFF_SFREQ:
              tag = read_tag(fid, pos)
              sfreq = float(tag.data)
          elif kind == FIFF.FIFF_CH_INFO:
              tag = read_tag(fid, pos)
              chs.append(tag.data)
          elif kind == FIFF.FIFF_LOWPASS:
              tag = read_tag(fid, pos)
              lowpass = float(tag.data)
          elif kind == FIFF.FIFF_HIGHPASS:
              tag = read_tag(fid, pos)
              highpass = float(tag.data)
          elif kind == FIFF.FIFF_MEAS_DATE:
              tag = read_tag(fid, pos)
              meas_date = tag.data
          elif kind == FIFF.FIFF_COORD_TRANS:
              tag = read_tag(fid, pos)
              cand = tag.data
  
              if cand['from'] == FIFF.FIFFV_COORD_DEVICE and \
                      cand['to'] == FIFF.FIFFV_COORD_HEAD:
                  dev_head_t = cand
              elif cand['from'] == FIFF.FIFFV_MNE_COORD_CTF_HEAD and \
                      cand['to'] == FIFF.FIFFV_COORD_HEAD:
                  ctf_head_t = cand
              elif cand['from'] == FIFF.FIFFV_MNE_COORD_CTF_DEVICE and \
                      cand['to'] == FIFF.FIFFV_MNE_COORD_CTF_HEAD:
                  dev_ctf_t = cand
          elif kind == FIFF.FIFF_EXPERIMENTER:
              tag = read_tag(fid, pos)
              experimenter = tag.data
          elif kind == FIFF.FIFF_DESCRIPTION:
              tag = read_tag(fid, pos)
              description = tag.data
          elif kind == FIFF.FIFF_PROJ_ID:
              tag = read_tag(fid, pos)
              proj_id = tag.data
          elif kind == FIFF.FIFF_PROJ_NAME:
              tag = read_tag(fid, pos)
              proj_name = tag.data
          elif kind == FIFF.FIFF_LINE_FREQ:
              tag = read_tag(fid, pos)
              line_freq = float(tag.data)
          elif kind in [FIFF.FIFF_MNE_CUSTOM_REF, 236]:  # 236 used before v0.11
              tag = read_tag(fid, pos)
              custom_ref_applied = bool(tag.data)
          elif kind == FIFF.FIFF_XPLOTTER_LAYOUT:
              tag = read_tag(fid, pos)
              xplotter_layout = str(tag.data)
          elif kind == FIFF.FIFF_MNE_KIT_SYSTEM_ID:
              tag = read_tag(fid, pos)
              kit_system_id = int(tag.data)
  
      # Check that we have everything we need
      if nchan is None:
          raise ValueError('Number of channels is not defined')
  
      if sfreq is None:
          raise ValueError('Sampling frequency is not defined')
  
      if len(chs) == 0:
          raise ValueError('Channel information not defined')
  
      if len(chs) != nchan:
          raise ValueError('Incorrect number of channel definitions found')
  
      if dev_head_t is None or ctf_head_t is None:
          hpi_result = dir_tree_find(meas_info, FIFF.FIFFB_HPI_RESULT)
          if len(hpi_result) == 1:
              hpi_result = hpi_result[0]
              for k in range(hpi_result['nent']):
                  kind = hpi_result['directory'][k].kind
                  pos = hpi_result['directory'][k].pos
                  if kind == FIFF.FIFF_COORD_TRANS:
                      tag = read_tag(fid, pos)
                      cand = tag.data
                      if (cand['from'] == FIFF.FIFFV_COORD_DEVICE and
                              cand['to'] == FIFF.FIFFV_COORD_HEAD and
                              dev_head_t is None):
                          dev_head_t = cand
                      elif (cand['from'] == FIFF.FIFFV_MNE_COORD_CTF_HEAD and
                            cand['to'] == FIFF.FIFFV_COORD_HEAD and
                            ctf_head_t is None):
                          ctf_head_t = cand
  
      #   Locate the Polhemus data
      dig = _read_dig_fif(fid, meas_info)
  
      #   Locate the acquisition information
      acqpars = dir_tree_find(meas_info, FIFF.FIFFB_DACQ_PARS)
      acq_pars = None
      acq_stim = None
      if len(acqpars) == 1:
          acqpars = acqpars[0]
          for k in range(acqpars['nent']):
              kind = acqpars['directory'][k].kind
              pos = acqpars['directory'][k].pos
              if kind == FIFF.FIFF_DACQ_PARS:
                  tag = read_tag(fid, pos)
                  acq_pars = tag.data
              elif kind == FIFF.FIFF_DACQ_STIM:
                  tag = read_tag(fid, pos)
                  acq_stim = tag.data
  
      #   Load the SSP data
      projs = _read_proj(fid, meas_info)
  
      #   Load the CTF compensation data
      comps = read_ctf_comp(fid, meas_info, chs)
  
      #   Load the bad channel list
      bads = read_bad_channels(fid, meas_info)
  
      #
      #   Put the data together
      #
      if tree['id'] is not None:
          info = Info(file_id=tree['id'])
      else:
          info = Info(file_id=None)
  
      #   Locate events list
      events = dir_tree_find(meas_info, FIFF.FIFFB_EVENTS)
      evs = list()
      for event in events:
          ev = dict()
          for k in range(event['nent']):
              kind = event['directory'][k].kind
              pos = event['directory'][k].pos
              if kind == FIFF.FIFF_EVENT_CHANNELS:
                  ev['channels'] = read_tag(fid, pos).data
              elif kind == FIFF.FIFF_EVENT_LIST:
                  ev['list'] = read_tag(fid, pos).data
          evs.append(ev)
      info['events'] = evs
  
      #   Locate HPI result
      hpi_results = dir_tree_find(meas_info, FIFF.FIFFB_HPI_RESULT)
      hrs = list()
      for hpi_result in hpi_results:
          hr = dict()
          hr['dig_points'] = []
          for k in range(hpi_result['nent']):
              kind = hpi_result['directory'][k].kind
              pos = hpi_result['directory'][k].pos
              if kind == FIFF.FIFF_DIG_POINT:
                  hr['dig_points'].append(read_tag(fid, pos).data)
              elif kind == FIFF.FIFF_HPI_DIGITIZATION_ORDER:
                  hr['order'] = read_tag(fid, pos).data
              elif kind == FIFF.FIFF_HPI_COILS_USED:
                  hr['used'] = read_tag(fid, pos).data
              elif kind == FIFF.FIFF_HPI_COIL_MOMENTS:
                  hr['moments'] = read_tag(fid, pos).data
              elif kind == FIFF.FIFF_HPI_FIT_GOODNESS:
                  hr['goodness'] = read_tag(fid, pos).data
              elif kind == FIFF.FIFF_HPI_FIT_GOOD_LIMIT:
                  hr['good_limit'] = float(read_tag(fid, pos).data)
              elif kind == FIFF.FIFF_HPI_FIT_DIST_LIMIT:
                  hr['dist_limit'] = float(read_tag(fid, pos).data)
              elif kind == FIFF.FIFF_HPI_FIT_ACCEPT:
                  hr['accept'] = int(read_tag(fid, pos).data)
              elif kind == FIFF.FIFF_COORD_TRANS:
                  hr['coord_trans'] = read_tag(fid, pos).data
          hrs.append(hr)
      info['hpi_results'] = hrs
  
      #   Locate HPI Measurement
      hpi_meass = dir_tree_find(meas_info, FIFF.FIFFB_HPI_MEAS)
      hms = list()
      for hpi_meas in hpi_meass:
          hm = dict()
          for k in range(hpi_meas['nent']):
              kind = hpi_meas['directory'][k].kind
              pos = hpi_meas['directory'][k].pos
              if kind == FIFF.FIFF_CREATOR:
                  hm['creator'] = text_type(read_tag(fid, pos).data)
              elif kind == FIFF.FIFF_SFREQ:
                  hm['sfreq'] = float(read_tag(fid, pos).data)
              elif kind == FIFF.FIFF_NCHAN:
                  hm['nchan'] = int(read_tag(fid, pos).data)
              elif kind == FIFF.FIFF_NAVE:
                  hm['nave'] = int(read_tag(fid, pos).data)
              elif kind == FIFF.FIFF_HPI_NCOIL:
                  hm['ncoil'] = int(read_tag(fid, pos).data)
              elif kind == FIFF.FIFF_FIRST_SAMPLE:
                  hm['first_samp'] = int(read_tag(fid, pos).data)
              elif kind == FIFF.FIFF_LAST_SAMPLE:
                  hm['last_samp'] = int(read_tag(fid, pos).data)
          hpi_coils = dir_tree_find(hpi_meas, FIFF.FIFFB_HPI_COIL)
          hcs = []
          for hpi_coil in hpi_coils:
              hc = dict()
              for k in range(hpi_coil['nent']):
                  kind = hpi_coil['directory'][k].kind
                  pos = hpi_coil['directory'][k].pos
                  if kind == FIFF.FIFF_HPI_COIL_NO:
                      hc['number'] = int(read_tag(fid, pos).data)
                  elif kind == FIFF.FIFF_EPOCH:
                      hc['epoch'] = read_tag(fid, pos).data
                  elif kind == FIFF.FIFF_HPI_SLOPES:
                      hc['slopes'] = read_tag(fid, pos).data
                  elif kind == FIFF.FIFF_HPI_CORR_COEFF:
                      hc['corr_coeff'] = read_tag(fid, pos).data
                  elif kind == FIFF.FIFF_HPI_COIL_FREQ:
                      hc['coil_freq'] = read_tag(fid, pos).data
              hcs.append(hc)
          hm['hpi_coils'] = hcs
          hms.append(hm)
      info['hpi_meas'] = hms
  
      subject_info = dir_tree_find(meas_info, FIFF.FIFFB_SUBJECT)
      si = None
      if len(subject_info) == 1:
          subject_info = subject_info[0]
          si = dict()
          for k in range(subject_info['nent']):
              kind = subject_info['directory'][k].kind
              pos = subject_info['directory'][k].pos
              if kind == FIFF.FIFF_SUBJ_ID:
                  tag = read_tag(fid, pos)
                  si['id'] = int(tag.data)
              elif kind == FIFF.FIFF_SUBJ_HIS_ID:
                  tag = read_tag(fid, pos)
                  si['his_id'] = text_type(tag.data)
              elif kind == FIFF.FIFF_SUBJ_LAST_NAME:
                  tag = read_tag(fid, pos)
                  si['last_name'] = text_type(tag.data)
              elif kind == FIFF.FIFF_SUBJ_FIRST_NAME:
                  tag = read_tag(fid, pos)
                  si['first_name'] = text_type(tag.data)
              elif kind == FIFF.FIFF_SUBJ_MIDDLE_NAME:
                  tag = read_tag(fid, pos)
                  si['middle_name'] = text_type(tag.data)
              elif kind == FIFF.FIFF_SUBJ_BIRTH_DAY:
                  tag = read_tag(fid, pos)
                  si['birthday'] = tag.data
              elif kind == FIFF.FIFF_SUBJ_SEX:
                  tag = read_tag(fid, pos)
                  si['sex'] = int(tag.data)
              elif kind == FIFF.FIFF_SUBJ_HAND:
                  tag = read_tag(fid, pos)
                  si['hand'] = int(tag.data)
      info['subject_info'] = si
  
      hpi_subsystem = dir_tree_find(meas_info, FIFF.FIFFB_HPI_SUBSYSTEM)
      hs = None
      if len(hpi_subsystem) == 1:
          hpi_subsystem = hpi_subsystem[0]
          hs = dict()
          for k in range(hpi_subsystem['nent']):
              kind = hpi_subsystem['directory'][k].kind
              pos = hpi_subsystem['directory'][k].pos
              if kind == FIFF.FIFF_HPI_NCOIL:
                  tag = read_tag(fid, pos)
                  hs['ncoil'] = int(tag.data)
              elif kind == FIFF.FIFF_EVENT_CHANNEL:
                  tag = read_tag(fid, pos)
                  hs['event_channel'] = text_type(tag.data)
              hpi_coils = dir_tree_find(hpi_subsystem, FIFF.FIFFB_HPI_COIL)
              hc = []
              for coil in hpi_coils:
                  this_coil = dict()
                  for j in range(coil['nent']):
                      kind = coil['directory'][j].kind
                      pos = coil['directory'][j].pos
                      if kind == FIFF.FIFF_EVENT_BITS:
                          tag = read_tag(fid, pos)
                          this_coil['event_bits'] = np.array(tag.data)
                  hc.append(this_coil)
              hs['hpi_coils'] = hc
      info['hpi_subsystem'] = hs
  
      #   Read processing history
      _read_proc_history(fid, tree, info)
  
      #  Make the most appropriate selection for the measurement id
      if meas_info['parent_id'] is None:
          if meas_info['id'] is None:
              if meas['id'] is None:
                  if meas['parent_id'] is None:
                      info['meas_id'] = info['file_id']
                  else:
                      info['meas_id'] = meas['parent_id']
              else:
                  info['meas_id'] = meas['id']
          else:
              info['meas_id'] = meas_info['id']
      else:
          info['meas_id'] = meas_info['parent_id']
  
      info['experimenter'] = experimenter
      info['description'] = description
      info['proj_id'] = proj_id
      info['proj_name'] = proj_name
  
      if meas_date is None:
          meas_date = [info['meas_id']['secs'], info['meas_id']['usecs']]
      info['meas_date'] = meas_date
  
      info['sfreq'] = sfreq
      info['highpass'] = highpass if highpass is not None else 0.
      info['lowpass'] = lowpass if lowpass is not None else info['sfreq'] / 2.0
      info['line_freq'] = line_freq
  
      #   Add the channel information and make a list of channel names
      #   for convenience
      info['chs'] = chs
  
      #
      #  Add the coordinate transformations
      #
      info['dev_head_t'] = dev_head_t
      info['ctf_head_t'] = ctf_head_t
      info['dev_ctf_t'] = dev_ctf_t
      if dev_head_t is not None and ctf_head_t is not None and dev_ctf_t is None:
          from ..transforms import Transform
          head_ctf_trans = linalg.inv(ctf_head_t['trans'])
          dev_ctf_trans = np.dot(head_ctf_trans, info['dev_head_t']['trans'])
          info['dev_ctf_t'] = Transform('meg', 'ctf_head', dev_ctf_trans)
  
      #   All kinds of auxliary stuff
      info['dig'] = dig
      info['bads'] = bads
      info._update_redundant()
      if clean_bads:
          info['bads'] = [b for b in bads if b in info['ch_names']]
      info['projs'] = projs
      info['comps'] = comps
      info['acq_pars'] = acq_pars
      info['acq_stim'] = acq_stim
      info['custom_ref_applied'] = custom_ref_applied
      info['xplotter_layout'] = xplotter_layout
      info['kit_system_id'] = kit_system_id
      info._check_consistency()
      return info, meas
  
  
  def write_meas_info(fid, info, data_type=None, reset_range=True):
      """Write measurement info into a file id (from a fif file)
  
      Parameters
      ----------
      fid : file
          Open file descriptor.
      info : instance of Info
          The measurement info structure.
      data_type : int
          The data_type in case it is necessary. Should be 4 (FIFFT_FLOAT),
          5 (FIFFT_DOUBLE), or 16 (FIFFT_DAU_PACK16) for
          raw data.
      reset_range : bool
          If True, info['chs'][k]['range'] will be set to unity.
  
      Notes
      -----
      Tags are written in a particular order for compatibility with maxfilter.
      """
      info._check_consistency()
  
      # Measurement info
      start_block(fid, FIFF.FIFFB_MEAS_INFO)
  
      for event in info['events']:
          start_block(fid, FIFF.FIFFB_EVENTS)
          if event.get('channels') is not None:
              write_int(fid, FIFF.FIFF_EVENT_CHANNELS, event['channels'])
          if event.get('list') is not None:
              write_int(fid, FIFF.FIFF_EVENT_LIST, event['list'])
          end_block(fid, FIFF.FIFFB_EVENTS)
  
      #   HPI Result
      for hpi_result in info['hpi_results']:
          start_block(fid, FIFF.FIFFB_HPI_RESULT)
          for d in hpi_result['dig_points']:
              write_dig_point(fid, d)
          if 'order' in hpi_result:
              write_int(fid, FIFF.FIFF_HPI_DIGITIZATION_ORDER,
                        hpi_result['order'])
          if 'used' in hpi_result:
              write_int(fid, FIFF.FIFF_HPI_COILS_USED, hpi_result['used'])
          if 'moments' in hpi_result:
              write_float_matrix(fid, FIFF.FIFF_HPI_COIL_MOMENTS,
                                 hpi_result['moments'])
          if 'goodness' in hpi_result:
              write_float(fid, FIFF.FIFF_HPI_FIT_GOODNESS,
                          hpi_result['goodness'])
          if 'good_limit' in hpi_result:
              write_float(fid, FIFF.FIFF_HPI_FIT_GOOD_LIMIT,
                          hpi_result['good_limit'])
          if 'dist_limit' in hpi_result:
              write_float(fid, FIFF.FIFF_HPI_FIT_DIST_LIMIT,
                          hpi_result['dist_limit'])
          if 'accept' in hpi_result:
              write_int(fid, FIFF.FIFF_HPI_FIT_ACCEPT, hpi_result['accept'])
          if 'coord_trans' in hpi_result:
              write_coord_trans(fid, hpi_result['coord_trans'])
          end_block(fid, FIFF.FIFFB_HPI_RESULT)
  
      #   HPI Measurement
      for hpi_meas in info['hpi_meas']:
          start_block(fid, FIFF.FIFFB_HPI_MEAS)
          if hpi_meas.get('creator') is not None:
              write_string(fid, FIFF.FIFF_CREATOR, hpi_meas['creator'])
          if hpi_meas.get('sfreq') is not None:
              write_float(fid, FIFF.FIFF_SFREQ, hpi_meas['sfreq'])
          if hpi_meas.get('nchan') is not None:
              write_int(fid, FIFF.FIFF_NCHAN, hpi_meas['nchan'])
          if hpi_meas.get('nave') is not None:
              write_int(fid, FIFF.FIFF_NAVE, hpi_meas['nave'])
          if hpi_meas.get('ncoil') is not None:
              write_int(fid, FIFF.FIFF_HPI_NCOIL, hpi_meas['ncoil'])
          if hpi_meas.get('first_samp') is not None:
              write_int(fid, FIFF.FIFF_FIRST_SAMPLE, hpi_meas['first_samp'])
          if hpi_meas.get('last_samp') is not None:
              write_int(fid, FIFF.FIFF_LAST_SAMPLE, hpi_meas['last_samp'])
          for hpi_coil in hpi_meas['hpi_coils']:
              start_block(fid, FIFF.FIFFB_HPI_COIL)
              if hpi_coil.get('number') is not None:
                  write_int(fid, FIFF.FIFF_HPI_COIL_NO, hpi_coil['number'])
              if hpi_coil.get('epoch') is not None:
                  write_float_matrix(fid, FIFF.FIFF_EPOCH, hpi_coil['epoch'])
              if hpi_coil.get('slopes') is not None:
                  write_float(fid, FIFF.FIFF_HPI_SLOPES, hpi_coil['slopes'])
              if hpi_coil.get('corr_coeff') is not None:
                  write_float(fid, FIFF.FIFF_HPI_CORR_COEFF,
                              hpi_coil['corr_coeff'])
              if hpi_coil.get('coil_freq') is not None:
                  write_float(fid, FIFF.FIFF_HPI_COIL_FREQ,
                              hpi_coil['coil_freq'])
              end_block(fid, FIFF.FIFFB_HPI_COIL)
          end_block(fid, FIFF.FIFFB_HPI_MEAS)
  
      #   Polhemus data
      if info['dig'] is not None:
          start_block(fid, FIFF.FIFFB_ISOTRAK)
          for d in info['dig']:
              write_dig_point(fid, d)
  
          end_block(fid, FIFF.FIFFB_ISOTRAK)
  
      #   megacq parameters
      if info['acq_pars'] is not None or info['acq_stim'] is not None:
          start_block(fid, FIFF.FIFFB_DACQ_PARS)
          if info['acq_pars'] is not None:
              write_string(fid, FIFF.FIFF_DACQ_PARS, info['acq_pars'])
  
          if info['acq_stim'] is not None:
              write_string(fid, FIFF.FIFF_DACQ_STIM, info['acq_stim'])
  
          end_block(fid, FIFF.FIFFB_DACQ_PARS)
  
      #   Coordinate transformations if the HPI result block was not there
      if info['dev_head_t'] is not None:
          write_coord_trans(fid, info['dev_head_t'])
  
      if info['ctf_head_t'] is not None:
          write_coord_trans(fid, info['ctf_head_t'])
  
      if info['dev_ctf_t'] is not None:
          write_coord_trans(fid, info['dev_ctf_t'])
  
      #   Projectors
      _write_proj(fid, info['projs'])
  
      #   Bad channels
      if len(info['bads']) > 0:
          start_block(fid, FIFF.FIFFB_MNE_BAD_CHANNELS)
          write_name_list(fid, FIFF.FIFF_MNE_CH_NAME_LIST, info['bads'])
          end_block(fid, FIFF.FIFFB_MNE_BAD_CHANNELS)
  
      #   General
      if info.get('experimenter') is not None:
          write_string(fid, FIFF.FIFF_EXPERIMENTER, info['experimenter'])
      if info.get('description') is not None:
          write_string(fid, FIFF.FIFF_DESCRIPTION, info['description'])
      if info.get('proj_id') is not None:
          write_int(fid, FIFF.FIFF_PROJ_ID, info['proj_id'])
      if info.get('proj_name') is not None:
          write_string(fid, FIFF.FIFF_PROJ_NAME, info['proj_name'])
      if info.get('meas_date') is not None:
          write_int(fid, FIFF.FIFF_MEAS_DATE, info['meas_date'])
      write_int(fid, FIFF.FIFF_NCHAN, info['nchan'])
      write_float(fid, FIFF.FIFF_SFREQ, info['sfreq'])
      if info['lowpass'] is not None:
          write_float(fid, FIFF.FIFF_LOWPASS, info['lowpass'])
      if info['highpass'] is not None:
          write_float(fid, FIFF.FIFF_HIGHPASS, info['highpass'])
      if info.get('line_freq') is not None:
          write_float(fid, FIFF.FIFF_LINE_FREQ, info['line_freq'])
      if data_type is not None:
          write_int(fid, FIFF.FIFF_DATA_PACK, data_type)
      if info.get('custom_ref_applied'):
          write_int(fid, FIFF.FIFF_MNE_CUSTOM_REF, info['custom_ref_applied'])
      if info.get('xplotter_layout'):
          write_string(fid, FIFF.FIFF_XPLOTTER_LAYOUT, info['xplotter_layout'])
  
      #  Channel information
      for k, c in enumerate(info['chs']):
          #   Scan numbers may have been messed up
          c = deepcopy(c)
          c['scanno'] = k + 1
          # for float/double, the "range" param is unnecessary
          if reset_range is True:
              c['range'] = 1.0
          write_ch_info(fid, c)
  
      # Subject information
      if info.get('subject_info') is not None:
          start_block(fid, FIFF.FIFFB_SUBJECT)
          si = info['subject_info']
          if si.get('id') is not None:
              write_int(fid, FIFF.FIFF_SUBJ_ID, si['id'])
          if si.get('his_id') is not None:
              write_string(fid, FIFF.FIFF_SUBJ_HIS_ID, si['his_id'])
          if si.get('last_name') is not None:
              write_string(fid, FIFF.FIFF_SUBJ_LAST_NAME, si['last_name'])
          if si.get('first_name') is not None:
              write_string(fid, FIFF.FIFF_SUBJ_FIRST_NAME, si['first_name'])
          if si.get('middle_name') is not None:
              write_string(fid, FIFF.FIFF_SUBJ_MIDDLE_NAME, si['middle_name'])
          if si.get('birthday') is not None:
              write_julian(fid, FIFF.FIFF_SUBJ_BIRTH_DAY, si['birthday'])
          if si.get('sex') is not None:
              write_int(fid, FIFF.FIFF_SUBJ_SEX, si['sex'])
          if si.get('hand') is not None:
              write_int(fid, FIFF.FIFF_SUBJ_HAND, si['hand'])
          end_block(fid, FIFF.FIFFB_SUBJECT)
  
      if info.get('hpi_subsystem') is not None:
          hs = info['hpi_subsystem']
          start_block(fid, FIFF.FIFFB_HPI_SUBSYSTEM)
          if hs.get('ncoil') is not None:
              write_int(fid, FIFF.FIFF_HPI_NCOIL, hs['ncoil'])
          if hs.get('event_channel') is not None:
              write_string(fid, FIFF.FIFF_EVENT_CHANNEL, hs['event_channel'])
          if hs.get('hpi_coils') is not None:
              for coil in hs['hpi_coils']:
                  start_block(fid, FIFF.FIFFB_HPI_COIL)
                  if coil.get('event_bits') is not None:
                      write_int(fid, FIFF.FIFF_EVENT_BITS,
                                coil['event_bits'])
                  end_block(fid, FIFF.FIFFB_HPI_COIL)
          end_block(fid, FIFF.FIFFB_HPI_SUBSYSTEM)
  
      #   CTF compensation info
      write_ctf_comp(fid, info['comps'])
  
      #   KIT system ID
      if info.get('kit_system_id') is not None:
          write_int(fid, FIFF.FIFF_MNE_KIT_SYSTEM_ID, info['kit_system_id'])
  
      end_block(fid, FIFF.FIFFB_MEAS_INFO)
  
      #   Processing history
      _write_proc_history(fid, info)
  
  
  def write_info(fname, info, data_type=None, reset_range=True):
      """Write measurement info in fif file.
  
      Parameters
      ----------
      fname : str
          The name of the file. Should end by -info.fif.
      info : instance of Info
          The measurement info structure
      data_type : int
          The data_type in case it is necessary. Should be 4 (FIFFT_FLOAT),
          5 (FIFFT_DOUBLE), or 16 (FIFFT_DAU_PACK16) for
          raw data.
      reset_range : bool
          If True, info['chs'][k]['range'] will be set to unity.
      """
      fid = start_file(fname)
      start_block(fid, FIFF.FIFFB_MEAS)
      write_meas_info(fid, info, data_type, reset_range)
      end_block(fid, FIFF.FIFFB_MEAS)
      end_file(fid)
  
  
  def _is_equal_dict(dicts):
      """Aux function"""
      tests = zip(*[d.items() for d in dicts])
      is_equal = []
      for d in tests:
          k0, v0 = d[0]
          if (isinstance(v0, (list, np.ndarray)) and len(v0) > 0 and
                  isinstance(v0[0], dict)):
              for k, v in d:
                  is_equal.append((k0 == k) and _is_equal_dict(v))
          else:
              is_equal.append(all(np.all(k == k0) and
                              (np.array_equal(v, v0) if isinstance(v, np.ndarray)
                               else np.all(v == v0)) for k, v in d))
      return all(is_equal)
  
  
  @verbose
  def _merge_dict_values(dicts, key, verbose=None):
      """Merge things together
  
      Fork for {'dict', 'list', 'array', 'other'}
      and consider cases where one or all are of the same type.
      """
      values = [d[key] for d in dicts]
      msg = ("Don't know how to merge '%s'. Make sure values are "
             "compatible." % key)
  
      def _flatten(lists):
          return [item for sublist in lists for item in sublist]
  
      def _check_isinstance(values, kind, func):
          return func([isinstance(v, kind) for v in values])
  
      def _where_isinstance(values, kind):
          """Aux function"""
          return np.where([isinstance(v, type) for v in values])[0]
  
      # list
      if _check_isinstance(values, list, all):
          lists = (d[key] for d in dicts)
          return (_uniquify_projs(_flatten(lists)) if key == 'projs'
                  else _flatten(lists))
      elif _check_isinstance(values, list, any):
          idx = _where_isinstance(values, list)
          if len(idx) == 1:
              return values[int(idx)]
          elif len(idx) > 1:
              lists = (d[key] for d in dicts if isinstance(d[key], list))
              return _flatten(lists)
      # dict
      elif _check_isinstance(values, dict, all):
          is_qual = _is_equal_dict(values)
          if is_qual:
              return values[0]
          else:
              RuntimeError(msg)
      elif _check_isinstance(values, dict, any):
          idx = _where_isinstance(values, dict)
          if len(idx) == 1:
              return values[int(idx)]
          elif len(idx) > 1:
              raise RuntimeError(msg)
      # ndarray
      elif _check_isinstance(values, np.ndarray, all):
          is_qual = all(np.all(values[0] == x) for x in values[1:])
          if is_qual:
              return values[0]
          elif key == 'meas_date':
              logger.info('Found multiple entries for %s. '
                          'Setting value to `None`' % key)
              return None
          else:
              raise RuntimeError(msg)
      elif _check_isinstance(values, np.ndarray, any):
          idx = _where_isinstance(values, np.ndarray)
          if len(idx) == 1:
              return values[int(idx)]
          elif len(idx) > 1:
              raise RuntimeError(msg)
      # other
      else:
          unique_values = set(values)
          if len(unique_values) == 1:
              return list(values)[0]
          elif isinstance(list(unique_values)[0], BytesIO):
              logger.info('Found multiple StringIO instances. '
                          'Setting value to `None`')
              return None
          elif isinstance(list(unique_values)[0], string_types):
              logger.info('Found multiple filenames. '
                          'Setting value to `None`')
              return None
          else:
              raise RuntimeError(msg)
  
  
  @verbose
  def _merge_info(infos, force_update_to_first=False, verbose=None):
      """Merge multiple measurement info dictionaries.
  
       - Fields that are present in only one info object will be used in the
         merged info.
       - Fields that are present in multiple info objects and are the same
         will be used in the merged info.
       - Fields that are present in multiple info objects and are different
         will result in a None value in the merged info.
       - Channels will be concatenated. If multiple info objects contain
         channels with the same name, an exception is raised.
  
      Parameters
      ----------
      infos | list of instance of Info
          Info objects to merge into one info object.
      force_update_to_first : bool
          If True, force the fields for objects in `info` will be updated
          to match those in the first item. Use at your own risk, as this
          may overwrite important metadata.
      verbose : bool, str, int, or NonIe
          If not None, override default verbose level (see mne.verbose).
  
      Returns
      -------
      info : instance of Info
          The merged info object.
      """
      for info in infos:
          info._check_consistency()
      if force_update_to_first is True:
          infos = deepcopy(infos)
          _force_update_info(infos[0], infos[1:])
      info = Info()
      info['chs'] = []
      for this_info in infos:
          info['chs'].extend(this_info['chs'])
      info._update_redundant()
      duplicates = set([ch for ch in info['ch_names']
                        if info['ch_names'].count(ch) > 1])
      if len(duplicates) > 0:
          msg = ("The following channels are present in more than one input "
                 "measurement info objects: %s" % list(duplicates))
          raise ValueError(msg)
  
      transforms = ['ctf_head_t', 'dev_head_t', 'dev_ctf_t']
      for trans_name in transforms:
          trans = [i[trans_name] for i in infos if i[trans_name]]
          if len(trans) == 0:
              info[trans_name] = None
          elif len(trans) == 1:
              info[trans_name] = trans[0]
          elif all(np.all(trans[0]['trans'] == x['trans']) and
                   trans[0]['from'] == x['from'] and
                   trans[0]['to'] == x['to']
                   for x in trans[1:]):
              info[trans_name] = trans[0]
          else:
              msg = ("Measurement infos provide mutually inconsistent %s" %
                     trans_name)
              raise ValueError(msg)
  
      # KIT system-IDs
      kit_sys_ids = [i['kit_system_id'] for i in infos if i['kit_system_id']]
      if len(kit_sys_ids) == 0:
          info['kit_system_id'] = None
      elif len(set(kit_sys_ids)) == 1:
          info['kit_system_id'] = kit_sys_ids[0]
      else:
          raise ValueError("Trying to merge channels from different KIT systems")
  
      # other fields
      other_fields = ['acq_pars', 'acq_stim', 'bads', 'buffer_size_sec',
                      'comps', 'custom_ref_applied', 'description', 'dig',
                      'experimenter', 'file_id', 'filename', 'highpass',
                      'hpi_results', 'hpi_meas', 'hpi_subsystem', 'events',
                      'line_freq', 'lowpass', 'meas_date', 'meas_id',
                      'proj_id', 'proj_name', 'projs', 'sfreq',
                      'subject_info', 'sfreq', 'xplotter_layout']
      for k in other_fields:
          info[k] = _merge_dict_values(infos, k)
  
      info._check_consistency()
      return info
  
  
  def create_info(ch_names, sfreq, ch_types=None, montage=None):
      """Create a basic Info instance suitable for use with create_raw
  
      Parameters
      ----------
      ch_names : list of str | int
          Channel names. If an int, a list of channel names will be created
          from :func:`range(ch_names) <range>`.
      sfreq : float
          Sample rate of the data.
      ch_types : list of str | str
          Channel types. If None, data are assumed to be misc.
          Currently supported fields are 'ecg', 'bio', 'stim', 'eog', 'misc',
          'seeg', 'ecog', 'mag', 'eeg', 'ref_meg', 'grad', 'hbr' or 'hbo'.
          If str, then all channels are assumed to be of the same type.
      montage : None | str | Montage | DigMontage | list
          A montage containing channel positions. If str or Montage is
          specified, the channel info will be updated with the channel
          positions. Default is None. If DigMontage is specified, the
          digitizer information will be updated. A list of unique montages,
          can be specifed and applied to the info. See also the documentation of
          :func:`mne.channels.read_montage` for more information.
  
      Returns
      -------
      info : instance of Info
          The measurement info.
  
      Notes
      -----
      The info dictionary will be sparsely populated to enable functionality
      within the rest of the package. Advanced functionality such as source
      localization can only be obtained through substantial, proper
      modifications of the info structure (not recommended).
  
      Note that the MEG device-to-head transform ``info['dev_head_t']`` will
      be initialized to the identity transform.
      """
      if isinstance(ch_names, int):
          ch_names = list(np.arange(ch_names).astype(str))
      if not isinstance(ch_names, (list, tuple)):
          raise TypeError('ch_names must be a list, tuple, or int')
      sfreq = float(sfreq)
      if sfreq <= 0:
          raise ValueError('sfreq must be positive')
      nchan = len(ch_names)
      if ch_types is None:
          ch_types = ['misc'] * nchan
      if isinstance(ch_types, string_types):
          ch_types = [ch_types] * nchan
      if len(ch_types) != nchan:
          raise ValueError('ch_types and ch_names must be the same length '
                           '(%s != %s)' % (len(ch_types), nchan))
      info = _empty_info(sfreq)
      info['meas_date'] = np.array([0, 0], np.int32)
      loc = np.concatenate((np.zeros(3), np.eye(3).ravel())).astype(np.float32)
      for ci, (name, kind) in enumerate(zip(ch_names, ch_types)):
          if not isinstance(name, string_types):
              raise TypeError('each entry in ch_names must be a string')
          if not isinstance(kind, string_types):
              raise TypeError('each entry in ch_types must be a string')
          if kind not in _kind_dict:
              raise KeyError('kind must be one of %s, not %s'
                             % (list(_kind_dict.keys()), kind))
          kind = _kind_dict[kind]
          chan_info = dict(loc=loc.copy(), unit_mul=0, range=1., cal=1.,
                           kind=kind[0], coil_type=kind[1],
                           unit=kind[2], coord_frame=FIFF.FIFFV_COORD_UNKNOWN,
                           ch_name=name, scanno=ci + 1, logno=ci + 1)
          info['chs'].append(chan_info)
      info._update_redundant()
      if montage is not None:
          from ..channels.montage import (Montage, DigMontage, _set_montage,
                                          read_montage)
          if not isinstance(montage, list):
              montage = [montage]
          for montage_ in montage:
              if isinstance(montage_, (Montage, DigMontage)):
                  _set_montage(info, montage_)
              elif isinstance(montage_, string_types):
                  montage_ = read_montage(montage_)
                  _set_montage(info, montage_)
              else:
                  raise TypeError('Montage must be an instance of Montage, '
                                  'DigMontage, a list of montages, or filepath, '
                                  'not %s.' % type(montage))
      info._check_consistency()
      return info
  
  
  RAW_INFO_FIELDS = (
      'acq_pars', 'acq_stim', 'bads', 'buffer_size_sec', 'ch_names', 'chs',
      'comps', 'ctf_head_t', 'custom_ref_applied', 'description', 'dev_ctf_t',
      'dev_head_t', 'dig', 'experimenter', 'events',
      'file_id', 'filename', 'highpass', 'hpi_meas', 'hpi_results',
      'hpi_subsystem', 'kit_system_id', 'line_freq', 'lowpass', 'meas_date',
      'meas_id', 'nchan', 'proj_id', 'proj_name', 'projs', 'sfreq',
      'subject_info', 'xplotter_layout',
  )
  
  
  def _empty_info(sfreq):
      """Create an empty info dictionary"""
      from ..transforms import Transform
      _none_keys = (
          'acq_pars', 'acq_stim', 'buffer_size_sec', 'ctf_head_t', 'description',
          'dev_ctf_t', 'dig', 'experimenter',
          'file_id', 'filename', 'highpass', 'hpi_subsystem', 'kit_system_id',
          'line_freq', 'lowpass', 'meas_date', 'meas_id', 'proj_id', 'proj_name',
          'subject_info', 'xplotter_layout',
      )
      _list_keys = ('bads', 'chs', 'comps', 'events', 'hpi_meas', 'hpi_results',
                    'projs')
      info = Info()
      for k in _none_keys:
          info[k] = None
      for k in _list_keys:
          info[k] = list()
      info['custom_ref_applied'] = False
      info['dev_head_t'] = Transform('meg', 'head', np.eye(4))
      info['highpass'] = 0.
      info['sfreq'] = float(sfreq)
      info['lowpass'] = info['sfreq'] / 2.
      info._update_redundant()
      info._check_consistency()
      return info
  
  
  def _force_update_info(info_base, info_target):
      """Update target info objects with values from info base.
  
      Note that values in info_target will be overwritten by those in info_base.
      This will overwrite all fields except for: 'chs', 'ch_names', 'nchan'.
  
      Parameters
      ----------
      info_base : mne.Info
          The Info object you want to use for overwriting values
          in target Info objects.
      info_target : mne.Info | list of mne.Info
          The Info object(s) you wish to overwrite using info_base. These objects
          will be modified in-place.
      """
      exclude_keys = ['chs', 'ch_names', 'nchan']
      info_target = np.atleast_1d(info_target).ravel()
      all_infos = np.hstack([info_base, info_target])
      for ii in all_infos:
          if not isinstance(ii, Info):
              raise ValueError('Inputs must be of type Info. '
                               'Found type %s' % type(ii))
      for key, val in info_base.items():
          if key in exclude_keys:
              continue
          for i_targ in info_target:
              i_targ[key] = val
  
  
  def anonymize_info(info):
      """Anonymize measurement information in place.
  
      Reset 'subject_info', 'meas_date', 'file_id', and 'meas_id' keys if they
      exist in ``info``.
  
      Parameters
      ----------
      info : dict, instance of Info
          Measurement information for the dataset.
  
      Returns
      -------
      info : instance of Info
          Measurement information for the dataset.
  
      Notes
      -----
      Operates in place.
      """
      if not isinstance(info, Info):
          raise ValueError('self must be an Info instance.')
      if info.get('subject_info') is not None:
          del info['subject_info']
      info['meas_date'] = [0, 0]
      for key_1 in ('file_id', 'meas_id'):
          for key_2 in ('secs', 'msecs', 'usecs'):
              info[key_1][key_2] = 0
      return info
  
apis: numpy.ones numpy.diff numpy.any numpy.argsort numpy.size numpy.random.mtrand.RandomState matplotlib.path.Path matplotlib.pyplot.xticks matplotlib.pyplot.figure numpy.abs matplotlib.pyplot.title matplotlib.pyplot.get_cmap matplotlib.patches.PathPatch numpy.where numpy.linspace numpy.unique numpy.diag_indices numpy.zeros numpy.max matplotlib.pyplot.ylim matplotlib.pyplot.colorbar numpy.zeros_like numpy.cumsum matplotlib.pyplot.getp matplotlib.pyplot.Normalize numpy.tril_indices matplotlib.pyplot.setp matplotlib.pyplot.subplot numpy.array matplotlib.pyplot.yticks
apis: numpy.eye numpy.zeros numpy.savetxt numpy.iterable numpy.asarray scipy.linalg.inv numpy.atleast_1d numpy.arange numpy.hstack numpy.all numpy.array_equal numpy.array numpy.dot numpy.unique numpy.loadtxt