Add a result. This should be :term:`results dict` as returned from
        an instance of :class:`Experiment`, that contains metadata,
        parameters, and result. Results cannot be overridden, as
        notebooks are immutable: adding more results simply adds
        another result set.

        The results may include one or more nested results dicts, for example as
        returned by :meth:`RepeatedExperiment`, whose results are a list of results
        at the same point in the parameter space. In this case the emebedded
        results will themselves be unpacked and added.

        One may also add a list of results dicts directly, in which case they will
        be added individually.

        If the jobid is present, this result resolves the corresponding pending result.
        As with result, jobid can be a list. (The two lists need not be the same
        length, to allow for experiments that return lists of result dicts.)

        :param result: a results dict
        :param jobids: the pending result job id(s) (defaults to no jobs)

def addResult( self, result, jobids = None ):
        """Add a result. This should be :term:`results dict` as returned from
        an instance of :class:`Experiment`, that contains metadata,
        parameters, and result. Results cannot be overridden, as
        notebooks are immutable: adding more results simply adds
        another result set.

        The results may include one or more nested results dicts, for example as
        returned by :meth:`RepeatedExperiment`, whose results are a list of results
        at the same point in the parameter space. In this case the emebedded
        results will themselves be unpacked and added.

        One may also add a list of results dicts directly, in which case they will
        be added individually.

        If the jobid is present, this result resolves the corresponding pending result.
        As with result, jobid can be a list. (The two lists need not be the same
        length, to allow for experiments that return lists of result dicts.)

        :param result: a results dict
        :param jobids: the pending result job id(s) (defaults to no jobs)
        """

        # deal with the dufferent ways of presenting results to be added
        if isinstance(result, list):
            # a list, recursively add
            for res in result:
                self.addResult(res)
        else:
            if isinstance(result, dict):
                if isinstance(result[Experiment.RESULTS], list):
                    # a result with embedded results, unwrap and and add them
                    for res in result[Experiment.RESULTS]:
                        self.addResult(res)
                else:
                    # a single results dict with a single set of experimental results
                    k = self._parametersAsIndex(result[Experiment.PARAMETERS])
            
                    # retrieve or create the result list
                    if k in self._results.keys():
                        rs = self._results[k]
                    else:
                        rs = []
                        self._results[k] = rs

                    # store the result
                    rs.insert(0, result)
            else:
                raise Exception("Can't deal with results like this: {r}".format(r = result)) 
                    
        # if there is are job ids provided, cancel the corresponding pending jobs
        if jobids is not None:
            if not isinstance(jobids, list):
                jobids = [ jobids ]
            for jobid in jobids:
                if jobid in self._pending.keys():
                    # grab the results list for which this is a pending job
                    k = self._pending[jobid]
                    if k in self._results.keys():
                        # delete job id from current results
                        rs = self._results[k]
                        j = rs.index(jobid)
                        del rs[j]
                    
                        # ...and from the set of pending results
                        del self._pending[jobid]
                    else:
                        # we've screwed-up the internal data structures
                        raise RuntimeError('Internal structure error for {j} -> {ps}'.format(j = jobid,
                                                                                             ps = k))
                else:
                    # we've screwed-up the internal data structures
                    raise RuntimeError('Internal structure error for {j}'.format(j = jobid))