docs: private void calculateOutside(int spanStart, int spanEnd, CfgParseChart chart, double[] newValues) {
      Factor parentOutside = chart.getOutsideEntries(spanStart, spanEnd);
      Tensor parentWeights = parentOutside.coerceToDiscrete().getWeights();
  
      Tensor binaryDistributionWeights = binaryDistribution.coerceToDiscrete().getWeights();
      int parentIndex = Ints.indexOf(binaryDistributionWeights.getDimensionNumbers(), parentVar.getOnlyVariableNum());
      int leftIndex = Ints.indexOf(binaryDistributionWeights.getDimensionNumbers(), leftVar.getOnlyVariableNum());
      int rightIndex = Ints.indexOf(binaryDistributionWeights.getDimensionNumbers(), rightVar.getOnlyVariableNum());
  
      int length = newValues.length;
      double[] binaryDistributionValues = binaryDistributionWeights.getValues();
      for (int i = 0; i < spanEnd - spanStart; i++) {
        double[] leftInside = chart.getInsideEntriesArray(spanStart, spanStart + i);
        double[] rightInside = chart.getInsideEntriesArray(spanStart + i + 1, spanEnd);
  
        for (int j = 0; j < length; j++) {
          newValues[j] = binaryDistributionValues[j] * parentWeights.get(binaryDistributionWeights.indexToPartialDimKey(j, parentIndex));
          newValues[j] *= rightInside[binaryDistributionWeights.indexToPartialDimKey(j, rightIndex)];
        }
        chart.updateOutsideEntry(spanStart, spanStart + i, newValues, binaryDistribution, leftVar);
        
        for (int j = 0; j < length; j++) {
          newValues[j] *= leftInside[binaryDistributionWeights.indexToPartialDimKey(j, leftIndex)];
        }
        chart.updateBinaryRuleExpectations(newValues);
  
        for (int j = 0; j < length; j++) {
          if (newValues[j] != 0.0) {
            newValues[j] = newValues[j] / rightInside[binaryDistributionWeights.indexToPartialDimKey(j, rightIndex)];
          }
        }
        chart.updateOutsideEntry(spanStart + i + 1, spanEnd, newValues, binaryDistribution, rightVar);
      }
    }