function validateInputData(data, relayError) {
  const processError = e => {
    if (relayError) {
      return [false, e];
    } else {
      throw e;
    }
  };
  // todo: add tests
  const inputKeys = ['x', 'yTrue', 'yPred'];
  if (
    typeof data !== 'object' ||
    inputKeys.some(key => Object.keys(data).indexOf(key) < 0)
  ) {
    return processError(
      new Error('Input data must contain these keys: `x`, `yTrue`, `yPred`.')
    );
  }
  const {x, yTrue, yPred} = data;
  if (!x || !yTrue || !yPred || !x.length || !yTrue.length || !yPred.length) {
    return processError(
      new Error(
        'One or more required fields (`x`, `yTrue`, `yPred`) in input data is empty.'
      )
    );
  }
  const nInstances = x.length;
  if (yTrue.length !== nInstances || yPred.some(y => y.length !== nInstances)) {
    return processError(
      new Error(
        'Number of data instances in `x`, `yTrue` and `yPred` are not consistent. ' +
          'Check the shape of your input data.'
      )
    );
  }
  const predInstance0 = yPred[0][0];
  if (typeof predInstance0 !== 'object') {
    return processError(
      new Error(
        '`yPred` must be an array of array of objects. ' +
          'Check the shape of your input data.'
      )
    );
  }
  const predObjKeys = Object.keys(predInstance0);

  yPred.forEach((predArr, i) => {
    predArr.forEach((predEle, j) => {
      if (Object.keys(predEle).some(key => predObjKeys.indexOf(key) < 0)) {
        return processError(
          new Error(
            `yPred[${i}][${j}] has a different shape than other element in yPred.
          Check your input data.`
          )
        );
      }
    });
  });

  yTrue.forEach((trueEle, i) => {
    // regression
    if (predObjKeys.length === 1) {
      if (typeof yTrue[i] !== 'number') {
        return processError(
          new Error(
            `yTrue[${i}] has wrong data type. Check your input data.
          Expect: number, got: ${typeof yTrue[i]}`
          )
        );
      }
    }
    // classification
    else {
      if (predObjKeys.indexOf(String(trueEle)) < 0) {
        return processError(
          new Error(
            `Class label at yTrue[${i}] is not found in corresbonding yPred.
            Check your input data.`
          )
        );
      }
    }
  });
  return relayError ? [true, data] : data;
}
check whether user input data is valid
@param {Object} data - {x: Array, yPred: Array<Array>, yTrue: Array}
@param {Boolean} relayError - decide whether to throw errors immediately
@return {Object | Array} -  returns the data when relayError option is off, otherwise, return a array of [Boolean, data]