//-------------------------------------------------------------------------
        // shift the curve
        private NodalCurve withShift(InterpolatedNodalCurve curve, IList <ParameterMetadata> parameterMetadata, DoubleMatrix sensitivity, bool computeJacobian, double shift)
        {
            int nNode = curve.ParameterCount;

            if (shift < curve.XValues.get(0))
            {
                //offset less than t value of 1st knot, so no knots are not removed
                double        eta      = curve.YValues.get(0) * shift;
                DoubleArray   time     = DoubleArray.of(nNode, i => curve.XValues.get(i) - shift);
                DoubleArray   rate     = DoubleArray.of(nNode, i => (curve.YValues.get(i) * curve.XValues.get(i) - eta) / time.get(i));
                CurveMetadata metadata = curve.Metadata.withParameterMetadata(parameterMetadata);
                if (computeJacobian)
                {
//JAVA TO C# CONVERTER NOTE: The following call to the 'RectangularArrays' helper class reproduces the rectangular array initialization that is automatic in Java:
//ORIGINAL LINE: double[][] transf = new double[nNode][nNode];
                    double[][] transf = RectangularArrays.ReturnRectangularDoubleArray(nNode, nNode);
                    for (int i = 0; i < nNode; ++i)
                    {
                        transf[i][0]  = -shift / time.get(i);
                        transf[i][i] += curve.XValues.get(i) / time.get(i);
                    }
                    DoubleMatrix jacobianMatrix        = (DoubleMatrix)MATRIX_ALGEBRA.multiply(DoubleMatrix.ofUnsafe(transf), MATRIX_ALGEBRA.getInverse(sensitivity));
                    JacobianCalibrationMatrix jacobian = JacobianCalibrationMatrix.of(ImmutableList.of(CurveParameterSize.of(curve.Name, nNode)), jacobianMatrix);
                    return(curve.withValues(time, rate).withMetadata(metadata.withInfo(CurveInfoType.JACOBIAN, jacobian)));
                }
                return(curve.withValues(time, rate).withMetadata(metadata));
            }
            if (shift >= curve.XValues.get(nNode - 1))
            {
                //new base after last knot. The new 'curve' has a constant zero rate which we represent with a nominal knot at 1.0
                double time     = 1d;
                double interval = curve.XValues.get(nNode - 1) - curve.XValues.get(nNode - 2);
                double rate     = (curve.YValues.get(nNode - 1) * curve.XValues.get(nNode - 1) - curve.YValues.get(nNode - 2) * curve.XValues.get(nNode - 2)) / interval;
                if (computeJacobian)
                {
//JAVA TO C# CONVERTER NOTE: The following call to the 'RectangularArrays' helper class reproduces the rectangular array initialization that is automatic in Java:
//ORIGINAL LINE: double[][] transf = new double[1][nNode];
                    double[][] transf = RectangularArrays.ReturnRectangularDoubleArray(1, nNode);
                    transf[0][nNode - 2] = -curve.XValues.get(nNode - 2) / interval;
                    transf[0][nNode - 1] = curve.XValues.get(nNode - 1) / interval;
                    DoubleMatrix jacobianMatrix        = (DoubleMatrix)MATRIX_ALGEBRA.multiply(DoubleMatrix.ofUnsafe(transf), MATRIX_ALGEBRA.getInverse(sensitivity));
                    JacobianCalibrationMatrix jacobian = JacobianCalibrationMatrix.of(ImmutableList.of(CurveParameterSize.of(curve.Name, nNode)), jacobianMatrix);
                    return(ConstantNodalCurve.of(curve.Metadata.withInfo(CurveInfoType.JACOBIAN, jacobian), time, rate));
                }
                return(ConstantNodalCurve.of(curve.Metadata, time, rate));
            }
            //offset greater than (or equal to) t value of 1st knot, so at least one knot must be removed
            int index = Arrays.binarySearch(curve.XValues.toArray(), shift);

            if (index < 0)
            {
                index = -(index + 1);
            }
            else
            {
                index++;
            }
            double        interval = curve.XValues.get(index) - curve.XValues.get(index - 1);
            double        tt1      = curve.XValues.get(index - 1) * (curve.XValues.get(index) - shift);
            double        tt2      = curve.XValues.get(index) * (shift - curve.XValues.get(index - 1));
            double        eta      = (curve.YValues.get(index - 1) * tt1 + curve.YValues.get(index) * tt2) / interval;
            int           m        = nNode - index;
            CurveMetadata metadata = curve.Metadata.withParameterMetadata(parameterMetadata.subList(index, nNode));
//JAVA TO C# CONVERTER WARNING: The original Java variable was marked 'final':
//ORIGINAL LINE: final int indexFinal = index;
            int         indexFinal = index;
            DoubleArray time       = DoubleArray.of(m, i => curve.XValues.get(i + indexFinal) - shift);
            DoubleArray rate       = DoubleArray.of(m, i => (curve.YValues.get(i + indexFinal) * curve.XValues.get(i + indexFinal) - eta) / time.get(i));

            if (computeJacobian)
            {
//JAVA TO C# CONVERTER NOTE: The following call to the 'RectangularArrays' helper class reproduces the rectangular array initialization that is automatic in Java:
//ORIGINAL LINE: double[][] transf = new double[m][nNode];
                double[][] transf = RectangularArrays.ReturnRectangularDoubleArray(m, nNode);
                for (int i = 0; i < m; ++i)
                {
                    transf[i][index - 1] -= tt1 / (time.get(i) * interval);
                    transf[i][index]     -= tt2 / (time.get(i) * interval);
                    transf[i][i + index] += curve.XValues.get(i + index) / time.get(i);
                }
                DoubleMatrix jacobianMatrix        = (DoubleMatrix)MATRIX_ALGEBRA.multiply(DoubleMatrix.ofUnsafe(transf), MATRIX_ALGEBRA.getInverse(sensitivity));
                JacobianCalibrationMatrix jacobian = JacobianCalibrationMatrix.of(ImmutableList.of(CurveParameterSize.of(curve.Name, nNode)), jacobianMatrix);
                return(curve.withValues(time, rate).withMetadata(metadata.withInfo(CurveInfoType.JACOBIAN, jacobian)));
            }
            return(curve.withValues(time, rate).withMetadata(metadata));
        }