<commit_before>﻿using System.Collections.Immutable;
using System.Linq;
using ExRam.Gremlinq.Core.Steps;

namespace ExRam.Gremlinq.Core.Projections
{
    public sealed class TupleProjection : Projection
    {
        private readonly (string Key, Projection Projection)[] _projections;

        internal TupleProjection((string Key, Projection Projection)[] projections)
        {
            _projections = projections;
        }
              
        public Projection Select(ImmutableArray<Key> keys)
        {
            var projections = _projections
                .Where(x => keys.Contains(x.Key))
                .ToArray();

            return projections.Length switch
            {
                0 => Empty,
                1 => projections[0].Projection,
                _ => new TupleProjection(projections)
            };
        }

        public override Traversal ToTraversal(IGremlinQueryEnvironment environment)
        {
            var emptyProjectionProtection = environment.Options.GetValue(GremlinqOption.EnableEmptyProjectionValueProtection)
                ? environment.Options.GetValue(GremlinqOption.EmptyProjectionProtectionDecoratorSteps)
                : default(Traversal?);

            var projectionTraversals = _projections
                .Select(projection =>
                {
                    var projectionTraversal = projection.Projection
                        .ToTraversal(environment);

                    return Traversal.Create(
                        emptyProjectionProtection is not null
                            ? projectionTraversal.Count + 2
                            : projectionTraversal.Count + 1,
                        (projection, projectionTraversal, emptyProjectionProtection),
                        static (steps, state) =>
                        {
                            var (projection, projectionTraversal, emptyProjectionProtection) = state;

                            steps[0] = new SelectKeysStep(projection.Key);

                            projectionTraversal
                                .AsSpan()
                                .CopyTo(steps[1..]);

                            if (emptyProjectionProtection is not null)
                                steps[^1] = FoldStep.Instance;
                        });
                })
                .ToArray();

            if (projectionTraversals.All(static x => x.Count == 1))
                return Traversal.Empty;

            return Traversal.Create(
                projectionTraversals.Length + 1 + (emptyProjectionProtection is { } protection
                    ? protection.Count
                    : 0),
                (_projections, projectionTraversals, emptyProjectionProtection),
                static (steps, state) =>
                {
                    var (projections, projectionTraversals, emptyProjectionProtection) = state;

                    steps[0] = new ProjectStep(projections
                        .Select(static x => x.Key)
                        .ToImmutableArray());

                    for (var i = 0; i < projectionTraversals.Length; i++)
                    {
                        steps[i + 1] = new ProjectStep.ByTraversalStep(projectionTraversals[i]);
                    }

                    if (emptyProjectionProtection is { } protection)
                    {
                        protection
                            .AsSpan()
                            .CopyTo(steps[(projectionTraversals.Length + 1)..]);
                    }
                });
        }

        public override Projection Lower() => Empty;
    }
}<commit_msg>TupleProjection: Review ToTraversal.<commit_after>﻿using System.Collections.Immutable;
using System.Linq;
using ExRam.Gremlinq.Core.Steps;

namespace ExRam.Gremlinq.Core.Projections
{
    public sealed class TupleProjection : Projection
    {
        private readonly (string Key, Projection Projection)[] _projections;

        internal TupleProjection((string Key, Projection Projection)[] projections)
        {
            _projections = projections;
        }
              
        public Projection Select(ImmutableArray<Key> keys)
        {
            var projections = _projections
                .Where(x => keys.Contains(x.Key))
                .ToArray();

            return projections.Length switch
            {
                0 => Empty,
                1 => projections[0].Projection,
                _ => new TupleProjection(projections)
            };
        }

        public override Traversal ToTraversal(IGremlinQueryEnvironment environment)
        {
            var emptyProjectionProtection = environment.Options.GetValue(GremlinqOption.EnableEmptyProjectionValueProtection)
                ? environment.Options.GetValue(GremlinqOption.EmptyProjectionProtectionDecoratorSteps)
                : default(Traversal?);

            var projectionTraversals = _projections
                .Select(projection =>
                {
                    var projectionTraversal = projection.Projection
                        .ToTraversal(environment);

                    return Traversal.Create(
                        emptyProjectionProtection is not null
                            ? projectionTraversal.Count + 2
                            : projectionTraversal.Count + 1,
                        (projection, projectionTraversal, emptyProjectionProtection),
                        static (steps, state) =>
                        {
                            var (projection, projectionTraversal, emptyProjectionProtection) = state;

                            steps[0] = new SelectKeysStep(projection.Key);

                            projectionTraversal
                                .AsSpan()
                                .CopyTo(steps[1..]);

                            if (emptyProjectionProtection is not null)
                                steps[^1] = FoldStep.Instance;
                        });
                })
                .ToArray();

            if (projectionTraversals.All(static x => x.Count == 1))
                return Traversal.Empty;

            var projectStep = new ProjectStep(_projections
                .Select(static x => x.Key)
                .ToImmutableArray());

            return Traversal.Create(
                emptyProjectionProtection is { } protection
                    ? projectionTraversals.Length + protection.Count + 1
                    : projectionTraversals.Length + 1,
                (projectStep, projectionTraversals, emptyProjectionProtection),
                static (steps, state) =>
                {
                    var (projectStep, projectionTraversals, emptyProjectionProtection) = state;

                    steps[0] = projectStep;

                    for (var i = 0; i < projectionTraversals.Length; i++)
                    {
                        steps[i + 1] = new ProjectStep.ByTraversalStep(projectionTraversals[i]);
                    }

                    if (emptyProjectionProtection is { } protection)
                    {
                        protection
                            .AsSpan()
                            .CopyTo(steps[^protection.Count..]);
                    }
                });
        }

        public override Projection Lower() => Empty;
    }
}
{'fraction_non_alphanumeric': 0.07524107021594459, 'fraction_numerical': 0.003938611978812984, 'mean_word_length': 1.3244949494949494, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4039568', 'n_tokens_mistral': 1779, 'n_tokens_neox': 1597, 'n_words': 321}