<commit_before>﻿using System.Linq;
using ExRam.Gremlinq.Core.Steps;
using Gremlin.Net.Process.Traversal;

namespace ExRam.Gremlinq.Core.Projections
{
    public sealed class GroupProjection : Projection
    {
        private readonly Projection _keyProjection;
        private readonly Projection? _valueProjection;

        internal GroupProjection(Projection keyProjection, Projection? valueProjection)
        {
            _keyProjection = keyProjection;
            _valueProjection = valueProjection;
        }

        public override Traversal ToTraversal(IGremlinQueryEnvironment environment)
        {
            var keyProjectionTraversal = _keyProjection.ToTraversal(environment);
            var maybeValueProjectionTraversal = _valueProjection?.ToTraversal(environment);

            return (keyProjectionTraversal.Count == 0 && (maybeValueProjectionTraversal?.Count).GetValueOrDefault() == 0)
                ? Traversal.Empty
                : new LocalStep(Traversal
                    .Create(
                        4,
                        (keyProjectionTraversal, maybeValueProjectionTraversal),
                        static (steps, state) =>
                        {
                            var (keyProjectionTraversal, maybeValueProjectionTraversal) = state;

                            steps[0] = UnfoldStep.Instance;
                            steps[1] = GroupStep.Instance;

                            steps[2] = new GroupStep.ByTraversalStep(keyProjectionTraversal
                                .Prepend(new SelectColumnStep(Column.Keys))
                                .ToTraversal());

                            steps[3] = maybeValueProjectionTraversal is { } valueProjectionTraversal
                                ? new GroupStep.ByTraversalStep(valueProjectionTraversal
                                    .Prepend(UnfoldStep.Instance)
                                    .Prepend(new SelectColumnStep(Column.Values))
                                    .Append(FoldStep.Instance)
                                    .ToTraversal())
                                : new GroupStep.ByTraversalStep(new SelectColumnStep(Column.Values));
                        }));
        }

        public override Projection Lower() => Empty;
    }
}
<commit_msg>GroupProjection: Use Traversal.Create to avoid excessive array allocations.<commit_after>﻿using System.Linq;
using ExRam.Gremlinq.Core.Steps;
using Gremlin.Net.Process.Traversal;

namespace ExRam.Gremlinq.Core.Projections
{
    public sealed class GroupProjection : Projection
    {
        private readonly Projection _keyProjection;
        private readonly Projection? _valueProjection;

        internal GroupProjection(Projection keyProjection, Projection? valueProjection)
        {
            _keyProjection = keyProjection;
            _valueProjection = valueProjection;
        }

        public override Traversal ToTraversal(IGremlinQueryEnvironment environment)
        {
            var keyProjectionTraversal = _keyProjection.ToTraversal(environment);
            var maybeValueProjectionTraversal = _valueProjection?.ToTraversal(environment);

            return (keyProjectionTraversal.Count == 0 && (maybeValueProjectionTraversal?.Count).GetValueOrDefault() == 0)
                ? Traversal.Empty
                : new LocalStep(Traversal
                    .Create(
                        4,
                        (keyProjectionTraversal, maybeValueProjectionTraversal),
                        static (steps, state) =>
                        {
                            var (keyProjectionTraversal, maybeValueProjectionTraversal) = state;

                            steps[0] = UnfoldStep.Instance;
                            steps[1] = GroupStep.Instance;

                            steps[2] = new GroupStep.ByTraversalStep(Traversal
                                .Create(
                                    keyProjectionTraversal.Count + 1,
                                    keyProjectionTraversal,
                                    static (steps, keyProjectionTraversal) =>
                                    {
                                        steps[0] = new SelectColumnStep(Column.Keys);

                                        keyProjectionTraversal
                                            .AsSpan()
                                            .CopyTo(steps[1..]);
                                    }));

                            steps[3] = maybeValueProjectionTraversal is { } valueProjectionTraversal
                                ? new GroupStep.ByTraversalStep(Traversal
                                    .Create(
                                        valueProjectionTraversal.Count + 3,
                                        valueProjectionTraversal,
                                        static (steps, valueProjectionTraversal) =>
                                        {
                                            steps[0] = new SelectColumnStep(Column.Values);
                                            steps[1] = UnfoldStep.Instance;
                                            
                                            valueProjectionTraversal
                                                .AsSpan()
                                                .CopyTo(steps[2..]);

                                            steps[^1] = FoldStep.Instance;
                                        }))
                                    : new GroupStep.ByTraversalStep(new SelectColumnStep(Column.Values));
                        }));
        }

        public override Projection Lower() => Empty;
    }
}

{'fraction_non_alphanumeric': 0.06431207169214549, 'fraction_numerical': 0.003865752943243718, 'mean_word_length': 1.2243063696756546, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 38, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2271211', 'n_tokens_mistral': 1278, 'n_tokens_neox': 1156, 'n_words': 207}