//------------------------------------------------------------
int validate_plural(vector<RESULT>& results, vector<SAH_RESULT>& sah_results, int& canonicalid, double& granted_credit) {
// This routine is called for redundant validation (from nontrusted or test case clients) .
//------------------------------------------------------------

    unsigned int i, j, k;
    bool found;
    double max_credit, min_credit, sum;
    int max_credit_i=-1, min_credit_i=-1, nvalid, retval;

    // see if there's a pair of results that are strongly similar
    // Not all results are *neccessarily* checked for overflow.  Any
    // result that may become the canonical reult is checked and thus
    // a valid overflow indicator is always paased on to the assimilator.
    found = false;
    for (i=0; i<sah_results.size()-1; i++) {    // scan with [i] to find canonical
        if (!sah_results[i].have_result) continue;
        check_overflow_result(results[i]);
        for (j=i+1; j<sah_results.size(); j++) {     // scan with [j] to match [i]
            if (!sah_results[j].have_result) continue;
            check_overflow_result(results[j]);
            validate_stats.nstrong_compare++;
            if (sah_results[i].strongly_similar(sah_results[j])) {
                log_messages.printf(
                    SCHED_MSG_LOG::MSG_DEBUG,
                    "[RESULT#%d (%d signals) and RESULT#%d (%d signals)] ARE strongly similar\n",
                    results[i].id, sah_results[i].num_signals, results[j].id,
                    sah_results[j].num_signals
                );
                found = true;
                validate_stats.nstrong++;
                break;
            }
            handle_cuda_notvalid(results[i], results[j]);           // temporary for cuda debugging
            log_messages.printf(
                SCHED_MSG_LOG::MSG_DEBUG,
                "[RESULT#%d (%d signals) and RESULT#%d (%d signals)] are NOT strongly similar\n",
                results[i].id, sah_results[i].num_signals, results[j].id,
                sah_results[j].num_signals
            );
        }  // end scan with [j] to match [i]
        if (found) break;
    }  // end scan with [i] to find canonical

    if (found) {
        // At this point results[i] is the canonical result and results[j]
        // is strongly similar to results[i].
        canonicalid = results[i].id;
        max_credit = 0;
        min_credit = 0;
        nvalid = 0;
        for (k=0; k<sah_results.size(); k++) {   // scan with [k] to validate rest of set against canonical
            if (!sah_results[k].have_result) continue;
            if (k == i || k == j) {
                results[k].validate_state = VALIDATE_STATE_VALID;
            } else {
                validate_stats.nweak_compare++;
                if (sah_results[k].weakly_similar(sah_results[i])) {
                    validate_stats.nweak++;
                    log_messages.printf(
                        SCHED_MSG_LOG::MSG_DEBUG,
                        "[CANONICAL RESULT#%d (%d signals) and RESULT#%d (%d signals)] ARE weakly similar\n",
                        results[i].id, sah_results[i].num_signals, results[k].id,
                        sah_results[k].num_signals
                    );
                    results[k].validate_state = VALIDATE_STATE_VALID;
                } else {
                    log_messages.printf(
                        SCHED_MSG_LOG::MSG_DEBUG,
                        "[CANONICAL RESULT#%d (%d signals) and RESULT#%d (%d signals)] are NOT weakly similar\n",
                        results[i].id, sah_results[i].num_signals, results[k].id,
                        sah_results[k].num_signals
                    );
                    results[k].validate_state = VALIDATE_STATE_INVALID;
                    handle_cuda_notvalid(results[i], results[k]);           // temporary for cuda debugging
                }
            }

            if (results[k].validate_state == VALIDATE_STATE_VALID) {
                if (results[k].claimed_credit < 0) {
                    results[k].claimed_credit = 0;
                }
                if (nvalid == 0) {
                    max_credit = min_credit = results[k].claimed_credit;
                    max_credit_i = min_credit_i = k;
                } else {
                    if (results[k].claimed_credit >= max_credit) {
                        max_credit = results[k].claimed_credit;
                        max_credit_i = k;
                    }
                    if (results[k].claimed_credit <= min_credit) {
                        min_credit = results[k].claimed_credit;
                        min_credit_i = k;
                    }
                }
                nvalid++;
            }  // end validate_state == VALIDATE_STATE_VALID
        }  // end scan with [k] to validate rest of set against canonical

        // the granted credit is the average of claimed credits
        // of valid results, discarding the largest and smallest
        //
        if (nvalid == 2) {
            granted_credit = min_credit;
        } else {
            // Take care of case where all claimed credits are equal.
            if (max_credit == min_credit) {
                granted_credit = min_credit;
            } else {
                sum = 0;
                for (k=0; k<results.size(); k++) {
                    if (!sah_results[k].have_result) continue;
                    if (results[k].validate_state != VALIDATE_STATE_VALID) continue;
                    if (k == max_credit_i) continue;
                    if (k == min_credit_i) continue;
                    sum += results[k].claimed_credit;
                }
                granted_credit = sum/(nvalid-2);
            }
        }
        for (k=0; k<results.size(); k++) {
            if (results[k].validate_state == VALIDATE_STATE_VALID) {
                results[k].granted_credit = granted_credit;
            }
        }
    } else {   // no canonical result found
        canonicalid = 0;
    }  // end if found

    return(0);
}
// check_pair() is called by BOINC code to validate any results arriving
// after the canonical result has been chosen.
//------------------------------------------------------------
int check_pair(RESULT& new_result, RESULT& canonical, bool& retry) {
//------------------------------------------------------------
    int retval;
    SAH_RESULT sah_new, sah_canonical;
    DB_RESULT db_result;

    retry=false;  // init

    log_messages.printf(
        SCHED_MSG_LOG::MSG_DEBUG,
        "[RESULT#%d] getting new result file %s\n",
        new_result.id, new_result.name
    );
    retval = get_result_file((RESULT&)new_result, sah_new);
    if (retval) {
        log_messages.printf(
            SCHED_MSG_LOG::MSG_DEBUG,
            "[RESULT#%d] read/parse of %s FAILED with retval %d\n",
            new_result.id, new_result.name, retval
        );
        // A directory problem may be transient.
        if (retval == ERR_OPENDIR) {
            retry = true;
            retval = 0;
            goto return_retval;
        } else {
            // a non-transient, non-recoverable error
            new_result.outcome =  RESULT_OUTCOME_VALIDATE_ERROR;
            new_result.validate_state = VALIDATE_STATE_INVALID;
            retval = 0;
            goto return_retval;
        }
    }

    log_messages.printf(
        SCHED_MSG_LOG::MSG_DEBUG,
        "[RESULT#%d] getting canonical result file %s\n",
        canonical.id, canonical.name
    );
    retval = get_result_file((RESULT &)canonical, sah_canonical);
    if (retval) {
        log_messages.printf(
            SCHED_MSG_LOG::MSG_DEBUG,
            "[RESULT#%d] read/parse of %s FAILED with retval %d\n",
            canonical.id, canonical.name, retval
        );
        // A directory problem may be transient.
        if (retval == ERR_OPENDIR) {
            retry = true;
            retval = 0;
            goto return_retval;
        } else {
            // a non-transient, non-recoverable error - set new_result to error.
            new_result.outcome =  RESULT_OUTCOME_VALIDATE_ERROR;
            new_result.validate_state = VALIDATE_STATE_INVALID;
            retval = 0;
            goto return_retval;
        }
    }

    if (sah_canonical.weakly_similar(sah_new)) {
        log_messages.printf(
            SCHED_MSG_LOG::MSG_DEBUG,
            "[CANONICAL RESULT#%d (%d signals) and NEW RESULT#%d (%d signals)] ARE weakly similar\n",
            canonical.id, sah_canonical.num_signals, new_result.id, sah_new.num_signals
        );
        new_result.validate_state = VALIDATE_STATE_VALID;
    } else {
        log_messages.printf(
            SCHED_MSG_LOG::MSG_DEBUG,
            "[CANONICAL RESULT#%d (%d signals) and NEW RESULT#%d (%d signals)] are NOT weakly similar\n",
            canonical.id, sah_canonical.num_signals, new_result.id, sah_new.num_signals
        );
        new_result.validate_state = VALIDATE_STATE_INVALID;
        handle_cuda_notvalid(canonical, new_result);           // temporary for cuda debugging
    }

    if (new_result.validate_state == VALIDATE_STATE_VALID) {
        log_cuda_result(new_result, canonical.granted_credit);
    }

    retval = 0;
return_retval:
    return(retval);
}
// check_pair() is called by BOINC code to validate any results arriving
// after the canonical result has been chosen.
//------------------------------------------------------------
int check_pair(RESULT& new_result, RESULT& canonical, bool& retry) {
//------------------------------------------------------------
    int retval;
    SAH_RESULT sah_new, sah_canonical;
    DB_RESULT db_result;

    retry=false;  // init

    log_messages.printf(
        SCHED_MSG_LOG::MSG_DEBUG,
        "[RESULT#%lld] getting new result file %s\n",
        new_result.id, new_result.name
    );
    retval = get_result_file((RESULT&)new_result, sah_new);
    if (retval) {
        log_messages.printf(
            SCHED_MSG_LOG::MSG_DEBUG,
            "[RESULT#%lld] read/parse of %s FAILED with retval %d\n",
            new_result.id, new_result.name, retval
        );
        // Someone is trying to process v7 workunits with v6
        if (!sah_new.found_best_autocorr && 
                !(sah_new.is_overflow || canonical.runtime_outlier) &&
                !strcmp(app_name,"setiathome_v7")) {
            new_result.validate_state = VALIDATE_STATE_INVALID;
            retval = 0;
            log_messages.printf(
                SCHED_MSG_LOG::MSG_DEBUG,
                "[RESULT#%lld] is from an invalid app_version\n", new_result.id
            );
            goto return_retval;
        }   
        // A directory problem may be transient.
        if (retval == ERR_OPENDIR) {
            retry = true;
            retval = 0;
            goto return_retval;
        } else {
            // a non-transient, non-recoverable error
            new_result.outcome =  RESULT_OUTCOME_VALIDATE_ERROR;
            new_result.validate_state = VALIDATE_STATE_INVALID;
            retval = 0;
            goto return_retval;
        }
    }

    log_messages.printf(
        SCHED_MSG_LOG::MSG_DEBUG,
        "[RESULT#%lld] getting canonical result file %s\n",
        canonical.id, canonical.name
    );
    retval = get_result_file((RESULT &)canonical, sah_canonical);
    if (retval) {
        log_messages.printf(
            SCHED_MSG_LOG::MSG_DEBUG,
            "[RESULT#%lld] read/parse of %s FAILED with retval %d\n",
            canonical.id, canonical.name, retval
        );
        // A directory problem may be transient.
        if (retval == ERR_OPENDIR) {
            retry = true;
            retval = 0;
            goto return_retval;
        } else {
            // a non-transient, non-recoverable error - set new_result to error.
            new_result.outcome =  RESULT_OUTCOME_VALIDATE_ERROR;
            new_result.validate_state = VALIDATE_STATE_INVALID;
            retval = 0;
            goto return_retval;
        }
    }

    if (sah_canonical.weakly_similar(sah_new)) {
        log_messages.printf(
            SCHED_MSG_LOG::MSG_DEBUG,
            "[CANONICAL RESULT#%lld (%d signals) and NEW RESULT#%lld (%d signals)] ARE weakly similar\n",
            canonical.id, sah_canonical.num_signals, new_result.id, sah_new.num_signals
        );
        new_result.validate_state = VALIDATE_STATE_VALID;
    } else {
        log_messages.printf(
            SCHED_MSG_LOG::MSG_DEBUG,
            "[CANONICAL RESULT#%lld (%d signals) and NEW RESULT#%lld (%lld signals)] are NOT weakly similar\n",
            canonical.id, sah_canonical.num_signals, new_result.id, sah_new.num_signals
        );
        new_result.validate_state = VALIDATE_STATE_INVALID;
        handle_cuda_notvalid(canonical, new_result);           // temporary for cuda debugging
    }

    if (new_result.validate_state == VALIDATE_STATE_VALID) {
        log_cuda_result(new_result, canonical.granted_credit);
    }

    retval = 0;
return_retval:
    return(retval);
}