public static IDataset interpolate(IDataset oldx, IDataset oldy, IDataset newx) {

    // TODO more sanity checks on inputs

    DoubleDataset dx = (DoubleDataset) DatasetUtils.cast(oldx, Dataset.FLOAT64);
    DoubleDataset dy = (DoubleDataset) DatasetUtils.cast(oldy, Dataset.FLOAT64);

    boolean sorted = true;
    double maxtest = oldx.getDouble(0);
    for (int i = 1; i < ((Dataset) oldx).count(); i++) {
      if (maxtest > oldx.getDouble(i)) {
        sorted = false;
        break;
      }
      maxtest = dx.getDouble(i);
    }

    double[] sortedx = null;
    double[] sortedy = null;

    if (!sorted) {
      IntegerDataset sIdx = getIndiciesOfSorted(dx);
      sortedx = new double[dx.getData().length];
      sortedy = new double[dy.getData().length];

      for (int i = 0; i < sIdx.getSize(); i++) {
        sortedx[i] = dx.getDouble(sIdx.get(i));
        sortedy[i] = dy.getDouble(sIdx.get(i));
      }
    } else {
      sortedx = dx.getData();
      sortedy = dy.getData();
    }

    SplineInterpolator si = new SplineInterpolator();
    PolynomialSplineFunction poly = si.interpolate(sortedx, sortedy);

    IDataset newy = newx.clone();
    newy.setName(oldy.getName() + "_interpolated");

    for (int i = 0; i < ((Dataset) newx).count(); i++) {
      newy.set(poly.value(newx.getDouble(i)), i);
    }

    return newy;
  }
  @Override
  public List<Dataset> integrate(IDataset dataset) {

    // Generate radial and azimuthal look-up arrays as required
    // TODO test shape of axis array
    if (radialArray == null) {
      generateRadialArray(dataset.getShape(), false);
    }

    if (azimuthalArray == null) {
      generateMinMaxAzimuthalArray(
          qSpace.getDetectorProperties().getBeamCentreCoords(), dataset.getShape());
    }

    Dataset mt = mask;
    if (mask != null && !Arrays.equals(mask.getShape(), dataset.getShape()))
      throw new IllegalArgumentException("Mask shape does not match dataset shape");

    List<Dataset> result = new ArrayList<Dataset>();
    if (binEdges == null) {
      binEdges = calculateBins(radialArray, mt, radialRange, nbins);
      binsChi = calculateBins(azimuthalArray, mt, azimuthalRange, nBinsChi);
    }

    final double[] edgesQ = binEdges.getData();
    final double loQ = edgesQ[0];
    final double hiQ = edgesQ[nbins];
    final double spanQ = (hiQ - loQ) / nbins;

    final double[] edgesChi = binsChi.getData();
    final double loChi = edgesChi[0];
    final double hiChi = edgesChi[nBinsChi];
    final double spanChi = (hiChi - loChi) / nBinsChi;

    FloatDataset histo = new FloatDataset(nBinsChi, nbins);
    FloatDataset intensity = new FloatDataset(nBinsChi, nbins);
    //			final double[] h = histo.getData();
    //			final double[] in = intensity.getData();
    //			if (spanQ <= 0) {
    //				h[0] = ds.getSize();
    //				result.add(histo);
    //				result.add(bins);
    //				continue;
    //			}

    Dataset a = DatasetUtils.convertToDataset(radialArray[0]);
    Dataset b = DatasetUtils.convertToDataset(dataset);
    //		PositionIterator iter = b.getPositionIterator();
    //
    //		int[] pos = iter.getPos();
    //		int[] posStop = pos.clone();

    IndexIterator iter = a.getIterator();

    while (iter.hasNext()) {

      //			posStop[0] = pos[0]+2;
      //			posStop[1] = pos[1]+2;
      //			Dataset qrange = a.getSlice(pos, posStop, null);
      //			Dataset chirange = azimuthalArray.getSlice(pos, posStop, null);
      if (mt != null && !mt.getElementBooleanAbs(iter.index)) continue;
      final double qMax = radialArray[1].getElementDoubleAbs(iter.index);
      final double qMin = radialArray[0].getElementDoubleAbs(iter.index);
      final double chiMax = azimuthalArray[1].getElementDoubleAbs(iter.index);
      final double chiMin = azimuthalArray[0].getElementDoubleAbs(iter.index);

      final double sig = b.getElementDoubleAbs(iter.index);

      if (qMax < loQ || qMin > hiQ) {
        continue;
      }

      if (chiMax < loChi || chiMin > hiChi) {
        continue;
      }

      // losing something here? is flooring (int cast best?)

      double minBinExactQ = (qMin - loQ) / spanQ;
      double maxBinExactQ = (qMax - loQ) / spanQ;
      int minBinQ = (int) minBinExactQ;
      int maxBinQ = (int) maxBinExactQ;

      double minBinExactChi = (chiMin - loChi) / spanChi;
      double maxBinExactChi = (chiMax - loChi) / spanChi;
      int minBinChi = (int) minBinExactChi;
      int maxBinChi = (int) maxBinExactChi;

      // FIXME potentially may need to deal with azimuthal arrays with discontinuities (+180/-180
      // degress etc)

      double iPerPixel = 1 / ((maxBinExactQ - minBinExactQ) * (maxBinExactChi - minBinExactChi));
      double minFracQ = 1 - (minBinExactQ - minBinQ);
      double maxFracQ = maxBinExactQ - maxBinQ;
      double minFracChi = 1 - (minBinExactChi - minBinChi);
      double maxFracChi = maxBinExactChi - maxBinChi;

      for (int i = minBinQ; i <= maxBinQ; i++) {
        if (i < 0 || i >= binEdges.getSize() - 1) continue;
        for (int j = minBinChi; j <= maxBinChi; j++) {
          if (j < 0 || j >= binsChi.getSize() - 1) continue;

          int[] setPos = new int[] {j, i};
          double val = histo.get(setPos);

          double modify = 1;

          if (i == minBinQ && minBinQ != maxBinQ) modify *= minFracQ;
          if (i == maxBinQ && minBinQ != maxBinQ) modify *= maxFracQ;
          if (i == minBinChi && minBinChi != maxBinChi) modify *= minFracChi;
          if (i == maxBinChi && minBinChi != maxBinChi) modify *= maxFracChi;

          histo.set(val + iPerPixel * modify, setPos);
          double inVal = intensity.get(setPos);
          intensity.set(inVal + sig * iPerPixel * modify, setPos);
        }
      }
    }

    processAndAddToResult(intensity, histo, result, radialRange, dataset.getName());

    return result;
  }