/// <summary>
        ///   Runs the one-against-one learning algorithm.
        /// </summary>
        ///
        /// <param name="computeError">
        ///   True to compute error after the training
        ///   process completes, false otherwise. Default is true.
        /// </param>
        /// <param name="token">
        ///   A <see cref="CancellationToken"/> which can be used
        ///   to request the cancellation of the learning algorithm
        ///   when it is being run in another thread.
        /// </param>
        ///
        /// <returns>
        ///   The sum of squares error rate for
        ///   the resulting support vector machine.
        /// </returns>
        ///
        public double Run(bool computeError, CancellationToken token)
        {
            if (configure == null)
            {
                var excp = new InvalidOperationException("Please specify the algorithm configuration function "
                                                         + "by setting the Algorithm property for this class. Examples are available in the "
                                                         + "documentation for Multiclass Support Vector Learning class (given in the help link).");
                excp.HelpLink = "http://accord-framework.net/svn/docs/html/T_BestCS_MachineLearning_VectorMachines_MulticlassSupportVectorMachine.htm";
                throw excp;
            }


            int classes  = msvm.Classes;
            int total    = (classes * (classes - 1)) / 2;
            int progress = 0;

            var pairs = new Tuple <int, int> [total];

            for (int i = 0, k = 0; i < classes; i++)
            {
                for (int j = 0; j < i; j++, k++)
                {
                    pairs[k] = Tuple.Create(i, j);
                }
            }

            msvm.Reset();

            // Save exceptions but process all machines
            var exceptions = new ConcurrentBag <Exception>();

            // For each class i
            Parallel.For(0, total, k =>
            {
                if (token.IsCancellationRequested)
                {
                    return;
                }

                int i = pairs[k].Item1;
                int j = pairs[k].Item2;

                // We will start the binary sub-problem
                var args = new SubproblemEventArgs(i, j);
                OnSubproblemStarted(args);

                // Retrieve the associated machine
                KernelSupportVectorMachine machine = msvm[i, j];

                // Retrieve the associated classes
                int[] idx = outputs.Find(x => x == i || x == j);

                double[][] subInputs = inputs.Submatrix(idx);
                int[] subOutputs     = outputs.Submatrix(idx);


                // Transform it into a two-class problem
                subOutputs.ApplyInPlace(x => x = (x == i) ? -1 : +1);

                // Train the machine on the two-class problem.
                var subproblem = configure(machine, subInputs, subOutputs, i, j);

                var canCancel = (subproblem as ISupportCancellation);

                try
                {
                    if (canCancel != null)
                    {
                        canCancel.Run(false, token);
                    }
                    else
                    {
                        subproblem.Run(false);
                    }
                }
                catch (Exception ex)
                {
                    exceptions.Add(ex);
                }

                // Update and report progress
                args.Progress = Interlocked.Increment(ref progress);
                args.Maximum  = total;

                OnSubproblemFinished(args);
            });


            if (exceptions.Count > 0)
            {
                throw new AggregateException("One or more exceptions were thrown when teaching "
                                             + "the machines. Please check the InnerException property of this AggregateException "
                                             + "to discover what exactly caused this error.", exceptions);
            }


            // Compute error if required.
            return((computeError) ? ComputeError(inputs, outputs) : 0.0);
        }