package com.conveyal.r5.analyst;

import com.conveyal.r5.analyst.cluster.AnalysisTask;
import com.conveyal.r5.analyst.cluster.RegionalTask;

/**
 * This holds and accumulates multiple accessibility indicator values for a single origin as they are computed.
 * The different accessibility indicator values are for different opportunity PointSets, different percentiles of travel
 * time, and different travel time cutoffs.
 *
 * This should only be used internally by R5 workers. Values are kept as doubles while accumulating results.
 * Once accumulation is done and results are ready for use elsewhere (e.g. assembling multiple results in the broker)
 * the getIntValues method returns an array of rounded integers.
 */
public class AccessibilityResult {

    private final int nPointSets;
    private final int nPercentiles;
    private final int nCutoffs;

    private final double[][][] cumulativeOpportunities;

    /** Construct an AccessibilityResult of the appropriate dimensions for the specified AnalysisTask. */
    public AccessibilityResult (AnalysisTask task) {
        this.nPointSets = 1;
        this.nPercentiles = task.percentiles.length;
        this.nCutoffs = task.cutoffs.length;
        cumulativeOpportunities = new double[nPointSets][nPercentiles][nCutoffs];
    }

    /** Constructor for empty results, for use in testing only. */
    public AccessibilityResult () {
        this.nPointSets = 0;
        this.nPercentiles = 0;
        this.nCutoffs = 0;
        this.cumulativeOpportunities = new double[0][0][0];
    }

    /**
     * Increment the accessibility indicator value for the given grid, cutoff, and percentile
     * by the given number of opportunities. This is called repeatedly to accumulate reachable
     * destinations into different indicator values.
     */
    public void incrementAccessibility (int gridIndex, int percentileIndex, int cutoffIndex, double amount) {
        cumulativeOpportunities[gridIndex][percentileIndex][cutoffIndex] += amount;
    }

    /**
     * Opportunity counts may be fractional because they were disaggregated from polygons, or because a weighting or
     * rolloff function was applied to them. After accumulating many such potentially fractional opportunity counts,
     * we round them off to whole numbers for reporting as final results.
     */
    public int[][][] getIntValues () {
        int[][][] intAccessibility = new int[nPointSets][nPercentiles][nCutoffs];
        for (int d = 0; d < nPointSets; d++) {
            for (int p = 0; p < nPercentiles; p++) {
                for (int c = 0; c < nCutoffs; c++) {
                    intAccessibility[d][p][c] = (int) Math.round(cumulativeOpportunities[d][p][c]);
                }
            }
        }
        return  intAccessibility;
    }
}

@@ -83,61 +83,8 @@
 ask;
-%0Aimport com.conveyal.r5.analyst.cluster.RegionalTask;
 %0A%0A/*
@@ -1940,32 +1940,1013 @@
  amount;%0A    %7D%0A%0A
+    /**%0A     * As travel time cutoff increases, accessibility should increase.%0A     * As percentile increases, travel time should decrease, and accessibility should decrease.%0A     * If one of these invariants does not hold, there is something wrong with the calculations.%0A     */%0A    private void checkInvariants () %7B%0A        for (int d = 0; d %3C nPointSets; d++) %7B%0A            for (int p = 0; p %3C nPercentiles; p++) %7B%0A                for (int c = 0; c %3C nCutoffs; c++) %7B%0A                    if (c %3E 0 && cumulativeOpportunities%5Bd%5D%5Bp%5D%5Bc%5D %3C cumulativeOpportunities%5Bd%5D%5Bp%5D%5Bc - 1%5D) %7B%0A                        throw new AssertionError(%22Increasing travel time decreased accessibility.%22);%0A                    %7D%0A                    if (p %3E 0 && cumulativeOpportunities%5Bd%5D%5Bp%5D%5Bc%5D %3E cumulativeOpportunities%5Bd%5D%5Bp - 1%5D%5Bc%5D) %7B%0A                        throw new AssertionError(%22Increasing percentile increased accessibility.%22);%0A                    %7D%0A                %7D%0A            %7D%0A        %7D%0A    %7D%0A%0A
     /**%0A     * O
@@ -3288,24 +3288,51 @@
 Values () %7B%0A
+        checkInvariants();%0A
         int%5B

add invariant checks on multi-cutoff, multi-percentile results