class NormalDistributionStatisticsComputer implements Serializable {

    public List<NormalDistributionStatistics> computeStatsOnPartition(BootstrappedDatasetPartition part,
        List<FeatureMeta> meta) {

        double[] sumOfValues = new double[meta.size()];
        double[] sumOfSquares = new double[sumOfValues.length];
        double[] min = new double[sumOfValues.length];
        double[] max = new double[sumOfValues.length];
        Arrays.fill(min, Double.POSITIVE_INFINITY);
        Arrays.fill(max, Double.NEGATIVE_INFINITY);

        for (int i = 0; i < part.getRowsCount(); i++) {
            Vector vec = part.getRow(i).features();
            for (int featureId = 0; featureId < vec.size(); featureId++) {
                if (!meta.get(featureId).isCategoricalFeature()) {
                    double featureVal = vec.get(featureId);
                    sumOfValues[featureId] += featureVal;
                    sumOfSquares[featureId] += Math.pow(featureVal, 2);
                    min[featureId] = Math.min(min[featureId], featureVal);
                    max[featureId] = Math.max(max[featureId], featureVal);
                }
            }
        }

        ArrayList<NormalDistributionStatistics> res = new ArrayList<>();
        for (int featureId = 0; featureId < sumOfSquares.length; featureId++) {
            res.add(new NormalDistributionStatistics(
                min[featureId], max[featureId],
                sumOfSquares[featureId], sumOfValues[featureId],
                part.getRowsCount())
            );
        }
        return res;
    }

}