Reads the intensities from a file.

    :param file_name: the name of the input file.
    :param needed_markers_chr: the markers that are needed.
    :param needed_samples_gender: the gender of all the samples.
    :param sex_problems: the sample with sex problem.

    :type file_name: str
    :type needed_markers_chr: dict
    :type needed_samples_gender: dict
    :type sex_problems: frozenset

    :returns: a :py:class`numpy.recarray` containing the following columns (for
              each of the samples): ``sampleID``, ``chr23``, ``chr24``,
              ``gender`` and ``status``.

    Reads the normalized intensities from a final report. The file must contain
    the following columns: ``SNP Name``, ``Sample ID``, ``X``, ``Y`` and
    ``Chr``. It then keeps only the required markers (those that are on
    sexual chromosomes (``23`` or ``24``), encoding `NaN` intensities to zero.

    The final data set contains the following information for each sample:

    * ``sampleID``: the sample ID.
    * ``chr23``: the summarized intensities for chromosome 23.
    * ``chr24``: the summarized intensities for chromosome 24.
    * ``gender``: the gender of the sample (``Male`` or ``Female``).
    * ``status``: the status of the sample (``OK`` or ``Problem``).

    The summarized intensities for a chromosome (:math:`S_{chr}`) is computed
    using this formula (where :math:`I_{chr}` is the set of all marker
    intensities on chromosome :math:`chr`):

    .. math::
        S_{chr} = \\frac{\\sum{I_{chr}}}{||I_{chr}||}

def read_intensities(file_name, needed_markers_chr, needed_samples_gender,
                     sex_problems):
    """Reads the intensities from a file.

    :param file_name: the name of the input file.
    :param needed_markers_chr: the markers that are needed.
    :param needed_samples_gender: the gender of all the samples.
    :param sex_problems: the sample with sex problem.

    :type file_name: str
    :type needed_markers_chr: dict
    :type needed_samples_gender: dict
    :type sex_problems: frozenset

    :returns: a :py:class`numpy.recarray` containing the following columns (for
              each of the samples): ``sampleID``, ``chr23``, ``chr24``,
              ``gender`` and ``status``.

    Reads the normalized intensities from a final report. The file must contain
    the following columns: ``SNP Name``, ``Sample ID``, ``X``, ``Y`` and
    ``Chr``. It then keeps only the required markers (those that are on
    sexual chromosomes (``23`` or ``24``), encoding `NaN` intensities to zero.

    The final data set contains the following information for each sample:

    * ``sampleID``: the sample ID.
    * ``chr23``: the summarized intensities for chromosome 23.
    * ``chr24``: the summarized intensities for chromosome 24.
    * ``gender``: the gender of the sample (``Male`` or ``Female``).
    * ``status``: the status of the sample (``OK`` or ``Problem``).

    The summarized intensities for a chromosome (:math:`S_{chr}`) is computed
    using this formula (where :math:`I_{chr}` is the set of all marker
    intensities on chromosome :math:`chr`):

    .. math::
        S_{chr} = \\frac{\\sum{I_{chr}}}{||I_{chr}||}

    """
    input_file = None
    if file_name.endswith(".gz"):
        input_file = gzip.open(file_name, 'rb')
    else:
        input_file = open(file_name, 'r')

    # The intensities
    intensities = {}

    header_index = None
    for line_nb, line in enumerate(input_file):
        row = line.rstrip("\r\n").split("\t")

        if line_nb == 0:
            # This is the header
            header_index = dict([
                (col_name, i) for i, col_name in enumerate(row)
            ])
            # Check the column names
            for col_name in {"SNP Name", "Sample ID", "X", "Y", "Chr"}:
                if col_name not in header_index:
                    msg = "{}: no column named {}".format(file_name, col_name)
                    raise ProgramError(msg)
        else:
            # This is the data
            # Check if we want this sample and this marker
            sampleID = row[header_index["Sample ID"]]
            markerID = row[header_index["SNP Name"]]
            chromosome = encode_chr(row[header_index["Chr"]])
            if chromosome not in {23, 24}:
                # Not good chromsoome
                continue
            if sampleID not in needed_samples_gender:
                # Sample not needed
                continue
            if markerID not in needed_markers_chr:
                # Marker not needed
                continue

            if sampleID not in intensities:
                # First time we see this sample
                intensities[sampleID] = [0, 0, 0, 0]

            # We get the intensities
            allele_a = row[header_index["X"]]
            allele_b = row[header_index["Y"]]

            # Check for NaN
            if allele_a == "NaN" or allele_b == "NaN":
                continue
            try:
                allele_a = float(allele_a)
                allele_b = float(allele_b)
            except ValueError:
                msg = "{}: {} and {}: wrong intensities".format(file_name,
                                                                allele_a,
                                                                allele_b)
                raise ProgramError(msg)

            if chromosome == 23:
                # Chromosome 23
                intensities[sampleID][0] += allele_a + allele_b
                intensities[sampleID][1] += 1
            else:
                # Chromosome 24
                intensities[sampleID][2] += allele_a + allele_b
                intensities[sampleID][3] += 1

    # Closing the input file
    input_file.close()

    # Creating the data structure
    data = []
    for sampleID in intensities.iterkeys():
        sum_chr_23, nb_chr_23, sum_chr_24, nb_chr_24 = intensities[sampleID]
        status = "OK"
        if sampleID in sex_problems:
            status = "Problem"
        try:
            data.append((sampleID, sum_chr_23 / nb_chr_23, sum_chr_24 /
                         nb_chr_24, needed_samples_gender[sampleID], status))
        except ZeroDivisionError:
            msg = "0 marker on chr23 or chr24"
            raise ProgramError(msg)

    data = np.array(
        data,
        dtype=[("sampleID", "a{}".format(max([len(i[0]) for i in data]))),
               ("chr23", float),
               ("chr24", float),
               ("gender", "a{}".format(max([len(i[3]) for i in data]))),
               ("status", "a{}".format(max([len(i[4]) for i in data])))],
    )
    return data