class Group {
        /** */
        private final List<AccumulatorWrapper<Row>> accumWrps;

        /** */
        private final Object[] grpKeys;

        /** */
        private Group(Object[] grpKeys) {
            this.grpKeys = grpKeys;

            accumWrps = hasAccumulators() ? accFactory.get() : Collections.emptyList();
        }

        /** */
        private void add(Row row) {
            if (type == REDUCE)
                addOnReducer(row);
            else
                addOnMapper(row);
        }

        /** */
        private Row row() {
            if (type == AggregateType.MAP)
                return rowOnMapper();
            else
                return rowOnReducer();
        }

        /** */
        private void addOnMapper(Row row) {
            for (AccumulatorWrapper<Row> wrapper : accumWrps)
                wrapper.add(row);
        }

        /** */
        private void addOnReducer(Row row) {
            RowHandler<Row> hnd = context().rowHandler();

            List<Accumulator> accums = hasAccumulators() ?
                (List<Accumulator>)hnd.get(hnd.columnCount(row) - 1, row)
                : Collections.emptyList();

            for (int i = 0; i < accums.size(); i++) {
                AccumulatorWrapper<Row> wrapper = accumWrps.get(i);

                Accumulator accum = accums.get(i);

                wrapper.apply(accum);
            }
        }

        /** */
        private Row rowOnMapper() {
            Object[] fields = new Object[grpSet.cardinality() + (accFactory != null ? 1 : 0)];

            int i = 0;

            for (Object grpKey : grpKeys)
                fields[i++] = grpKey;

            // Last column is the accumulators collection.
            if (hasAccumulators())
                fields[i] = Commons.transform(accumWrps, AccumulatorWrapper::accumulator);

            return rowFactory.create(fields);
        }

        /** */
        private Row rowOnReducer() {<FILL_FUNCTION_BODY>}
    }

            Object[] fields = new Object[grpSet.cardinality() + accumWrps.size()];

            int i = 0;

            for (Object grpKey : grpKeys)
                fields[i++] = grpKey;

            for (AccumulatorWrapper<Row> accWrp : accumWrps)
                fields[i++] = accWrp.end();

            return rowFactory.create(fields);
        
<methods><variables>protected final non-sealed Supplier<List<AccumulatorWrapper<Row>>> accFactory,protected final non-sealed boolean hasAggAccum,protected final non-sealed RowFactory<Row> rowFactory,protected final non-sealed org.apache.ignite.internal.processors.query.calcite.exec.exp.agg.AggregateType type