/*
 * Copyright (c) "Neo4j"
 * Neo4j Sweden AB [http://neo4j.com]
 *
 * This file is part of Neo4j.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package apoc.coll;

import static apoc.convert.ConvertUtils.convertArrayToList;
import static apoc.util.Util.containsValueEquals;
import static apoc.util.Util.toAnyValues;
import static java.util.Arrays.asList;

import apoc.util.Util;
import java.lang.reflect.Array;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.ListIterator;
import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.RandomAccess;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.BiFunction;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.apache.commons.lang3.mutable.MutableDouble;
import org.apache.commons.lang3.mutable.MutableInt;
import org.apache.commons.lang3.tuple.Pair;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Path;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.Result;
import org.neo4j.graphdb.Transaction;
import org.neo4j.internal.kernel.api.procs.ProcedureCallContext;
import org.neo4j.kernel.api.QueryLanguage;
import org.neo4j.kernel.api.procedure.QueryLanguageScope;
import org.neo4j.kernel.impl.util.ValueUtils;
import org.neo4j.procedure.Context;
import org.neo4j.procedure.Description;
import org.neo4j.procedure.Name;
import org.neo4j.procedure.NotThreadSafe;
import org.neo4j.procedure.Procedure;
import org.neo4j.procedure.UserFunction;
import org.neo4j.values.AnyValue;

public class Coll {

    public static final char ASCENDING_ORDER_CHAR = '^';

    @Context
    public Transaction tx;

    @Context
    public ProcedureCallContext procedureCallContext;

    @UserFunction("apoc.coll.stdev")
    @QueryLanguageScope(scope = {QueryLanguage.CYPHER_5})
    @Description("Returns sample or population standard deviation with `isBiasCorrected` true or false respectively.")
    public Number stdevCypher5(
            @Name(value = "list", description = "A list to collect the standard deviation from.") List<Number> list,
            @Name(
                            value = "isBiasCorrected",
                            defaultValue = "true",
                            description =
                                    "Will perform a sample standard deviation if `isBiasCorrected`, otherwise a population standard deviation is performed.")
                    boolean isBiasCorrected) {
        return stdev(list, isBiasCorrected);
    }

    @Deprecated
    @UserFunction(name = "apoc.coll.stdev", deprecatedBy = "Cypher's `stDev()` and `stDevP()` functions.")
    @QueryLanguageScope(scope = {QueryLanguage.CYPHER_25})
    @Description("Returns sample or population standard deviation with `isBiasCorrected` true or false respectively.")
    public Number stdev(
            @Name(value = "list", description = "A list to collect the standard deviation from.") List<Number> list,
            @Name(
                            value = "isBiasCorrected",
                            defaultValue = "true",
                            description =
                                    "Will perform a sample standard deviation if `isBiasCorrected`, otherwise a population standard deviation is performed.")
                    boolean isBiasCorrected) {
        if (list == null || list.isEmpty()) return null;
        final double stdev = StandardDeviation.stdDev(
                list.stream().mapToDouble(Number::doubleValue).toArray(), isBiasCorrected);
        if ((long) stdev == stdev) return (long) stdev;
        return stdev;
    }

    @UserFunction("apoc.coll.runningTotal")
    @Description("Returns an accumulative `LIST<INTEGER | FLOAT>`.")
    public List<Number> runningTotal(
            @Name(value = "list", description = "The list to return a running total from.") List<Number> list) {
        if (list == null || list.isEmpty()) return null;
        MutableDouble sum = new MutableDouble();
        return list.stream()
                .map(i -> {
                    double value = sum.addAndGet(i.doubleValue());
                    if (value == sum.longValue()) return sum.longValue();
                    return value;
                })
                .collect(Collectors.toList());
    }

    public record ZipToRowsListResult(@Description("A zipped pair.") List<Object> value) {}

    @Procedure("apoc.coll.zipToRows")
    @QueryLanguageScope(scope = {QueryLanguage.CYPHER_5})
    @Description("Returns the two `LIST<ANY>` values zipped together, with one row per zipped pair.")
    public Stream<ZipToRowsListResult> zipToRowsCypher5(
            @Name(value = "list1", description = "The list to zip together with `list2`.") List<Object> list1,
            @Name(value = "list2", description = "The list to zip together with `list1`.") List<Object> list2) {
        return zipToRows(list1, list2);
    }

    @Deprecated
    @Procedure(
            name = "apoc.coll.zipToRows",
            deprecatedBy =
                    "Cypher's `UNWIND` and `range()` function; `UNWIND range(0, size(list1) - 1) AS i RETURN [list1[i], list2[i]]`.")
    @QueryLanguageScope(scope = {QueryLanguage.CYPHER_25})
    @Description("Returns the two `LIST<ANY>` values zipped together, with one row per zipped pair.")
    public Stream<ZipToRowsListResult> zipToRows(
            @Name(value = "list1", description = "The list to zip together with `list2`.") List<Object> list1,
            @Name(value = "list2", description = "The list to zip together with `list1`.") List<Object> list2) {
        if (list1.isEmpty()) return Stream.empty();
        ListIterator<Object> it = list2.listIterator();
        return list1.stream().map((e) -> new ZipToRowsListResult(asList(e, it.hasNext() ? it.next() : null)));
    }

    @UserFunction("apoc.coll.zip")
    @QueryLanguageScope(scope = {QueryLanguage.CYPHER_5})
    @Description("Returns the two given `LIST<ANY>` values zipped together as a `LIST<LIST<ANY>>`.")
    public List<List<Object>> zipCypher5(
            @Name(value = "list1", description = "The list to zip together with `list2`.") List<Object> list1,
            @Name(value = "list2", description = "The list to zip together with `list1`.") List<Object> list2) {
        return zip(list1, list2);
    }

    @Deprecated
    @UserFunction(
            name = "apoc.coll.zip",
            deprecatedBy =
                    "Cypher's `UNWIND` and `range()` function; `COLLECT { UNWIND range(0, size(list1) - 1) AS i RETURN [list1[i], list2[i]] }`.")
    @QueryLanguageScope(scope = {QueryLanguage.CYPHER_25})
    @Description("Returns the two given `LIST<ANY>` values zipped together as a `LIST<LIST<ANY>>`.")
    public List<List<Object>> zip(
            @Name(value = "list1", description = "The list to zip together with `list2`.") List<Object> list1,
            @Name(value = "list2", description = "The list to zip together with `list1`.") List<Object> list2) {
        if (list1 == null || list2 == null) return null;
        if (list1.isEmpty() || list2.isEmpty()) return Collections.emptyList();
        List<List<Object>> result = new ArrayList<>(list1.size());
        ListIterator it = list2.listIterator();
        for (Object o1 : list1) {
            result.add(asList(o1, it.hasNext() ? it.next() : null));
        }
        return result;
    }

    @UserFunction("apoc.coll.pairs")
    @QueryLanguageScope(scope = {QueryLanguage.CYPHER_5})
    @Description("Returns a `LIST<ANY>` of adjacent elements in the `LIST<ANY>` ([1,2],[2,3],[3,null]).")
    public List<List<Object>> pairsCypher5(
            @Name(value = "list", description = "The list to create pairs from.") List<Object> list) {
        return pairs(list);
    }

    @Deprecated
    @UserFunction(
            name = "apoc.coll.pairs",
            deprecatedBy =
                    "Cypher's list comprehension: `RETURN [i IN range(0, size(list) - 1) | [list[i], list[i + 1]]]`")
    @QueryLanguageScope(scope = {QueryLanguage.CYPHER_25})
    @Description("Returns a `LIST<ANY>` of adjacent elements in the `LIST<ANY>` ([1,2],[2,3],[3,null]).")
    public List<List<Object>> pairs(
            @Name(value = "list", description = "The list to create pairs from.") List<Object> list) {
        if (list == null) return null;
        if (list.isEmpty()) return Collections.emptyList();
        return zip(list, list.subList(1, list.size()));
    }

    @UserFunction("apoc.coll.pairsMin")
    @QueryLanguageScope(scope = {QueryLanguage.CYPHER_5})
    @Description(
            "Returns `LIST<ANY>` values of adjacent elements in the `LIST<ANY>` ([1,2],[2,3]), skipping the final element.")
    public List<List<Object>> pairsMinCypher5(
            @Name(value = "list", description = "The list to create pairs from.") List<Object> list) {
        return pairsMin(list);
    }

    @Deprecated
    @UserFunction(
            name = "apoc.coll.pairsMin",
            deprecatedBy =
                    "Cypher's list comprehension: `RETURN [i IN range(0, size(list) - 2) | [list[i], list[i + 1]]]`")
    @QueryLanguageScope(scope = {QueryLanguage.CYPHER_25})
    @Description(
            "Returns `LIST<ANY>` values of adjacent elements in the `LIST<ANY>` ([1,2],[2,3]), skipping the final element.")
    public List<List<Object>> pairsMin(
            @Name(value = "list", description = "The list to create pairs from.") List<Object> list) {
        if (list == null) return null;
        if (list.isEmpty()) return Collections.emptyList();
        return zip(list.subList(0, list.size() - 1), list.subList(1, list.size()));
    }

    @UserFunction("apoc.coll.sum")
    @QueryLanguageScope(scope = {QueryLanguage.CYPHER_5})
    @Description("Returns the sum of all the `INTEGER | FLOAT` in the `LIST<INTEGER | FLOAT>`.")
    public Double sumCypher5(
            @Name(value = "coll", description = "The list of numbers to create a sum from.") List<Number> list) {
        return sum(list);
    }

    @Deprecated
    @UserFunction(
            name = "apoc.coll.sum",
            deprecatedBy = "Cypher's `reduce()` function: `RETURN reduce(sum = 0.0, x IN list | sum + x)`.")
    @QueryLanguageScope(scope = {QueryLanguage.CYPHER_25})
    @Description("Returns the sum of all the `INTEGER | FLOAT` in the `LIST<INTEGER | FLOAT>`.")
    public Double sum(
            @Name(value = "coll", description = "The list of numbers to create a sum from.") List<Number> list) {
        if (list == null || list.isEmpty()) return null;
        double sum = 0;
        for (Number number : list) {
            sum += number.doubleValue();
        }
        return sum;
    }

    @UserFunction("apoc.coll.avg")
    @Description("Returns the average of the numbers in the `LIST<INTEGER | FLOAT>`.")
    public Double avg(@Name(value = "coll", description = "The list to return the average from.") List<Number> list) {
        if (list == null || list.isEmpty()) return null;
        double avg = 0;
        for (Number number : list) {
            avg += number.doubleValue();
        }
        return (avg / (double) list.size());
    }

    @NotThreadSafe
    @UserFunction("apoc.coll.min")
    @QueryLanguageScope(scope = {QueryLanguage.CYPHER_5})
    @Description("Returns the minimum of all values in the given `LIST<ANY>`.")
    public Object minCypher5(
            @Name(value = "values", description = "The list to find the minimum in.") List<Object> list) {
        return min(list);
    }

    @NotThreadSafe
    @Deprecated
    @UserFunction(name = "apoc.coll.min", deprecatedBy = "Cypher's `coll.min` function.")
    @QueryLanguageScope(scope = {QueryLanguage.CYPHER_25})
    @Description("Returns the minimum of all values in the given `LIST<ANY>`.")
    public Object min(@Name(value = "values", description = "The list to find the minimum in.") List<Object> list) {
        if (list == null || list.isEmpty()) return null;
        if (list.size() == 1) return list.get(0);

        var preparser = "CYPHER " + Util.getCypherVersionString(procedureCallContext) + " runtime=slotted ";
        try (Result result = tx.execute(
                preparser
                        + "return reduce(res=null, x in $list | CASE WHEN res IS NULL OR x<res THEN x ELSE res END) as value",
                Collections.singletonMap("list", list))) {
            return result.next().get("value");
        }
    }

    @NotThreadSafe
    @UserFunction("apoc.coll.max")
    @QueryLanguageScope(scope = {QueryLanguage.CYPHER_5})
    @Description("Returns the maximum of all values in the given `LIST<ANY>`.")
    public Object maxCypher5(
            @Name(value = "values", description = "The list to find the maximum in.") List<Object> list) {
        return max(list);
    }

    @NotThreadSafe
    @Deprecated
    @UserFunction(name = "apoc.coll.max", deprecatedBy = "Cypher's `coll.max` function.")
    @QueryLanguageScope(scope = {QueryLanguage.CYPHER_25})
    @Description("Returns the maximum of all values in the given `LIST<ANY>`.")
    public Object max(@Name(value = "values", description = "The list to find the maximum in.") List<Object> list) {
        if (list == null || list.isEmpty()) return null;
        if (list.size() == 1) return list.get(0);
        var preparser = "CYPHER " + Util.getCypherVersionString(procedureCallContext) + " runtime=slotted ";
        try (Result result = tx.execute(
                preparser
                        + "RETURN reduce(res=null, x in $list | CASE WHEN res IS NULL OR res < x THEN x ELSE res END) AS value",
                Collections.singletonMap("list", list))) {
            return result.next().get("value");
        }
    }

    @Procedure("apoc.coll.elements")
    @Description("Deconstructs a `LIST<ANY>` into identifiers indicating their specific type.")
    public Stream<ElementsResult> elements(
            @Name(value = "coll", description = "A list of values to deconstruct.") List<Object> list,
            @Name(
                            value = "limit",
                            defaultValue = "-1",
                            description = "The maximum size of elements to deconstruct from the given list.")
                    long limit,
            @Name(value = "offset", defaultValue = "0", description = "The offset to start deconstructing from.")
                    long offset) {
        int elements = (limit < 0 ? list.size() : Math.min((int) (offset + limit), list.size())) - (int) offset;
        if (elements > ElementsResult.MAX_ELEMENTS) elements = ElementsResult.MAX_ELEMENTS;
        ElementsResult result = new ElementsResult();
        for (int i = 0; i < elements; i++) {
            result.add(list.get((int) offset + i));
        }
        return Stream.of(result);
    }

    public static class ElementsResult {
        @Description("The value of the first item.")
        public Object _1;

        @Description("The value of the second item.")
        public Object _2;

        @Description("The value of the third item.")
        public Object _3;

        @Description("The value of the fourth item.")
        public Object _4;

        @Description("The value of the fifth item.")
        public Object _5;

        @Description("The value of the sixth item.")
        public Object _6;

        @Description("The value of the seventh item.")
        public Object _7;

        @Description("The value of the eighth item.")
        public Object _8;

        @Description("The value of the ninth item.")
        public Object _9;

        @Description("The value of the tenth item.")
        public Object _10;

        @Description("The value of the first item, if it is a string value.")
        public String _1s;

        @Description("The value of the second item, if it is a string value.")
        public String _2s;

        @Description("The value of the third item, if it is a string value.")
        public String _3s;

        @Description("The value of the fourth item, if it is a string value.")
        public String _4s;

        @Description("The value of the fifth item, if it is a string value.")
        public String _5s;

        @Description("The value of the sixth item, if it is a string value.")
        public String _6s;

        @Description("The value of the seventh item, if it is a string value.")
        public String _7s;

        @Description("The value of the eighth item, if it is a string value.")
        public String _8s;

        @Description("The value of the ninth item, if it is a string value.")
        public String _9s;

        @Description("The value of the tenth item, if it is a string value.")
        public String _10s;

        @Description("The value of the first item, if it is an integer value.")
        public Long _1i;

        @Description("The value of the second item, if it is an integer value.")
        public Long _2i;

        @Description("The value of the third item, if it is an integer value.")
        public Long _3i;

        @Description("The value of the fourth item, if it is an integer value.")
        public Long _4i;

        @Description("The value of the fifth item, if it is an integer value.")
        public Long _5i;

        @Description("The value of the sixth item, if it is an integer value.")
        public Long _6i;

        @Description("The value of the seventh item, if it is an integer value.")
        public Long _7i;

        @Description("The value of the eighth item, if it is an integer value.")
        public Long _8i;

        @Description("The value of the ninth item, if it is an integer value.")
        public Long _9i;

        @Description("The value of the tenth item, if it is an integer value.")
        public Long _10i;

        @Description("The value of the first item, if it is a float value.")
        public Double _1f;

        @Description("The value of the second item, if it is a float value.")
        public Double _2f;

        @Description("The value of the third item, if it is a float value.")
        public Double _3f;

        @Description("The value of the fourth item, if it is a float value.")
        public Double _4f;

        @Description("The value of the fifth item, if it is a float value.")
        public Double _5f;

        @Description("The value of the sixth item, if it is a float value.")
        public Double _6f;

        @Description("The value of the seventh item, if it is a float value.")
        public Double _7f;

        @Description("The value of the eighth item, if it is a float value.")
        public Double _8f;

        @Description("The value of the ninth item, if it is a float value.")
        public Double _9f;

        @Description("The value of the tenth item, if it is a float value.")
        public Double _10f;

        @Description("The value of the first item, if it is a boolean value.")
        public Boolean _1b;

        @Description("The value of the second item, if it is a boolean value.")
        public Boolean _2b;

        @Description("The value of the third item, if it is a boolean value.")
        public Boolean _3b;

        @Description("The value of the fourth item, if it is a boolean value.")
        public Boolean _4b;

        @Description("The value of the fifth item, if it is a boolean value.")
        public Boolean _5b;

        @Description("The value of the sixth item, if it is a boolean value.")
        public Boolean _6b;

        @Description("The value of the seventh item, if it is a boolean value.")
        public Boolean _7b;

        @Description("The value of the eighth item, if it is a boolean value.")
        public Boolean _8b;

        @Description("The value of the ninth item, if it is a boolean value.")
        public Boolean _9b;

        @Description("The value of the tenth item, if it is a boolean value.")
        public Boolean _10b;

        @Description("The value of the first item, if it is a list value.")
        public List<Object> _1l;

        @Description("The value of the second item, if it is a list value.")
        public List<Object> _2l;

        @Description("The value of the third item, if it is a list value.")
        public List<Object> _3l;

        @Description("The value of the fourth item, if it is a list value.")
        public List<Object> _4l;

        @Description("The value of the fifth item, if it is a list value.")
        public List<Object> _5l;

        @Description("The value of the sixth item, if it is a list value.")
        public List<Object> _6l;

        @Description("The value of the seventh item, if it is a list value.")
        public List<Object> _7l;

        @Description("The value of the eighth item, if it is a list value.")
        public List<Object> _8l;

        @Description("The value of the ninth item, if it is a list value.")
        public List<Object> _9l;

        @Description("The value of the tenth item, if it is a list value.")
        public List<Object> _10l;

        @Description("The value of the first item, if it is a map value.")
        public Map<String, Object> _1m;

        @Description("The value of the second item, if it is a map value.")
        public Map<String, Object> _2m;

        @Description("The value of the third item, if it is a map value.")
        public Map<String, Object> _3m;

        @Description("The value of the fourth item, if it is a map value.")
        public Map<String, Object> _4m;

        @Description("The value of the fifth item, if it is a map value.")
        public Map<String, Object> _5m;

        @Description("The value of the sixth item, if it is a map value.")
        public Map<String, Object> _6m;

        @Description("The value of the seventh item, if it is a map value.")
        public Map<String, Object> _7m;

        @Description("The value of the eighth item, if it is a map value.")
        public Map<String, Object> _8m;

        @Description("The value of the ninth item, if it is a map value.")
        public Map<String, Object> _9m;

        @Description("The value of the tenth item, if it is a map value.")
        public Map<String, Object> _10m;

        @Description("The value of the first item, if it is a node value.")
        public Node _1n;

        @Description("The value of the second item, if it is a node value.")
        public Node _2n;

        @Description("The value of the third item, if it is a node value.")
        public Node _3n;

        @Description("The value of the fourth item, if it is a node value.")
        public Node _4n;

        @Description("The value of the fifth item, if it is a node value.")
        public Node _5n;

        @Description("The value of the sixth item, if it is a node value.")
        public Node _6n;

        @Description("The value of the seventh item, if it is a node value.")
        public Node _7n;

        @Description("The value of the eighth item, if it is a node value.")
        public Node _8n;

        @Description("The value of the ninth item, if it is a node value.")
        public Node _9n;

        @Description("The value of the tenth item, if it is a node value.")
        public Node _10n;

        @Description("The value of the first item, if it is a relationship value.")
        public Relationship _1r;

        @Description("The value of the second item, if it is a relationship value.")
        public Relationship _2r;

        @Description("The value of the third item, if it is a relationship value.")
        public Relationship _3r;

        @Description("The value of the fourth item, if it is a relationship value.")
        public Relationship _4r;

        @Description("The value of the fifth item, if it is a relationship value.")
        public Relationship _5r;

        @Description("The value of the sixth item, if it is a relationship value.")
        public Relationship _6r;

        @Description("The value of the seventh item, if it is a relationship value.")
        public Relationship _7r;

        @Description("The value of the eighth item, if it is a relationship value.")
        public Relationship _8r;

        @Description("The value of the ninth item, if it is a relationship value.")
        public Relationship _9r;

        @Description("The value of the tenth item, if it is a relationship value.")
        public Relationship _10r;

        @Description("The value of the first item, if it is a path value.")
        public Path _1p;

        @Description("The value of the second item, if it is a path value.")
        public Path _2p;

        @Description("The value of the third item, if it is a path value.")
        public Path _3p;

        @Description("The value of the fourth item, if it is a path value.")
        public Path _4p;

        @Description("The value of the fifth item, if it is a path value.")
        public Path _5p;

        @Description("The value of the sixth item, if it is a path value.")
        public Path _6p;

        @Description("The value of the seventh item, if it is a path value.")
        public Path _7p;

        @Description("The value of the eighth item, if it is a path value.")
        public Path _8p;

        @Description("The value of the ninth item, if it is a path value.")
        public Path _9p;

        @Description("The value of the tenth item, if it is a path value.")
        public Path _10p;

        @Description("The number of deconstructed elements.")
        public long elements;

        static final int MAX_ELEMENTS = 10;

        void add(Object o) {
            if (elements == MAX_ELEMENTS) return;
            // Arrays are considered lists in Cypher and
            // should be treated as such
            if (o != null && o.getClass().isArray()) {
                o = convertArrayToList(o);
            }
            setObject(o, (int) elements);
            if (o instanceof String) {
                setString((String) o, (int) elements);
            }
            if (o instanceof Number) {
                setLong(((Number) o).longValue(), (int) elements);
                setDouble(((Number) o).doubleValue(), (int) elements);
            }
            if (o instanceof Boolean) {
                setBoolean((Boolean) o, (int) elements);
            }
            if (o instanceof Map) {
                setMap((Map) o, (int) elements);
            }
            if (o instanceof List) {
                setList((List) o, (int) elements);
            }
            if (o instanceof Node) {
                setNode((Node) o, (int) elements);
            }
            if (o instanceof Relationship) {
                setRelationship((Relationship) o, (int) elements);
            }
            if (o instanceof Path) {
                setPath((Path) o, (int) elements);
            }
            elements++;
        }

        public void setObject(Object o, int idx) {
            switch (idx) {
                case 0 -> _1 = o;
                case 1 -> _2 = o;
                case 2 -> _3 = o;
                case 3 -> _4 = o;
                case 4 -> _5 = o;
                case 5 -> _6 = o;
                case 6 -> _7 = o;
                case 7 -> _8 = o;
                case 8 -> _9 = o;
                case 9 -> _10 = o;
            }
        }

        public void setString(String o, int idx) {
            switch (idx) {
                case 0:
                    _1s = o;
                    break;
                case 1:
                    _2s = o;
                    break;
                case 2:
                    _3s = o;
                    break;
                case 3:
                    _4s = o;
                    break;
                case 4:
                    _5s = o;
                    break;
                case 5:
                    _6s = o;
                    break;
                case 6:
                    _7s = o;
                    break;
                case 7:
                    _8s = o;
                    break;
                case 8:
                    _9s = o;
                    break;
                case 9:
                    _10s = o;
                    break;
            }
        }

        public void setLong(Long o, int idx) {
            switch (idx) {
                case 0 -> _1i = o;
                case 1 -> _2i = o;
                case 2 -> _3i = o;
                case 3 -> _4i = o;
                case 4 -> _5i = o;
                case 5 -> _6i = o;
                case 6 -> _7i = o;
                case 7 -> _8i = o;
                case 8 -> _9i = o;
                case 9 -> _10i = o;
            }
        }

        public void setBoolean(Boolean o, int idx) {
            switch (idx) {
                case 0 -> _1b = o;
                case 1 -> _2b = o;
                case 2 -> _3b = o;
                case 3 -> _4b = o;
                case 4 -> _5b = o;
                case 5 -> _6b = o;
                case 6 -> _7b = o;
                case 7 -> _8b = o;
                case 8 -> _9b = o;
                case 9 -> _10b = o;
            }
        }

        public void setDouble(Double o, int idx) {
            switch (idx) {
                case 0 -> _1f = o;
                case 1 -> _2f = o;
                case 2 -> _3f = o;
                case 3 -> _4f = o;
                case 4 -> _5f = o;
                case 5 -> _6f = o;
                case 6 -> _7f = o;
                case 7 -> _8f = o;
                case 8 -> _9f = o;
                case 9 -> _10f = o;
            }
        }

        public void setNode(Node o, int idx) {
            switch (idx) {
                case 0:
                    _1n = o;
                    break;
                case 1:
                    _2n = o;
                    break;
                case 2:
                    _3n = o;
                    break;
                case 3:
                    _4n = o;
                    break;
                case 4:
                    _5n = o;
                    break;
                case 5:
                    _6n = o;
                    break;
                case 6:
                    _7n = o;
                    break;
                case 7:
                    _8n = o;
                    break;
                case 8:
                    _9n = o;
                    break;
                case 9:
                    _10n = o;
                    break;
            }
        }

        public void setRelationship(Relationship o, int idx) {
            switch (idx) {
                case 0:
                    _1r = o;
                    break;
                case 1:
                    _2r = o;
                    break;
                case 2:
                    _3r = o;
                    break;
                case 3:
                    _4r = o;
                    break;
                case 4:
                    _5r = o;
                    break;
                case 5:
                    _6r = o;
                    break;
                case 6:
                    _7r = o;
                    break;
                case 7:
                    _8r = o;
                    break;
                case 8:
                    _9r = o;
                    break;
                case 9:
                    _10r = o;
                    break;
            }
        }

        public void setPath(Path o, int idx) {
            switch (idx) {
                case 0 -> _1p = o;
                case 1 -> _2p = o;
                case 2 -> _3p = o;
                case 3 -> _4p = o;
                case 4 -> _5p = o;
                case 5 -> _6p = o;
                case 6 -> _7p = o;
                case 7 -> _8p = o;
                case 8 -> _9p = o;
                case 9 -> _10p = o;
            }
        }

        public void setMap(Map o, int idx) {
            switch (idx) {
                case 0 -> _1m = o;
                case 1 -> _2m = o;
