local ____lualib = require("lualib_bundle")
local Set = ____lualib.Set
local __TS__Iterator = ____lualib.__TS__Iterator
local __TS__New = ____lualib.__TS__New
local __TS__ArrayMap = ____lualib.__TS__ArrayMap
local __TS__Spread = ____lualib.__TS__Spread
local __TS__ArraySort = ____lualib.__TS__ArraySort
local __TS__ObjectKeys = ____lualib.__TS__ObjectKeys
local __TS__ObjectValues = ____lualib.__TS__ObjectValues
local __TS__ArraySome = ____lualib.__TS__ArraySome
local ____exports = {}
local ____ReadonlySet = require("types.ReadonlySet")
local ReadonlySet = ____ReadonlySet.ReadonlySet
local ____array = require("functions.array")
local getArrayCombinations = ____array.getArrayCombinations
local getRandomArrayElement = ____array.getRandomArrayElement
local sumArray = ____array.sumArray
local ____types = require("functions.types")
local isPrimitive = ____types.isPrimitive
--- Helper function to get a sorted array based on the contents of a set.
-- 
-- Normally, set values are returned in insertion order, so use this function when the ordering of
-- the contents is important.
function ____exports.getSortedSetValues(self, set)
    local values = {__TS__Spread(set)}
    local firstElement = values[1]
    if firstElement ~= nil then
        local arrayType = type(firstElement)
        if not isPrimitive(nil, arrayType) then
            error(("Failed to get the sorted set values because the provided set was of type \"" .. tostring(arrayType)) .. "\". Having sets with non-primitive types doesn't make much sense in general, so you might need to rethink what you are doing.")
        end
    end
    __TS__ArraySort(values)
    return values
end
--- Helper function to convert the keys of an object to a set.
-- 
-- Note that the set values will be inserted in a random order, due to how `pairs` works under the
-- hood.
-- 
-- Also see the `objectKeysToReadonlySet` function.
function ____exports.objectKeysToSet(self, object)
    local set = __TS__New(Set)
    for ____, key in ipairs(__TS__ObjectKeys(object)) do
        set:add(key)
    end
    return set
end
--- Helper function to convert the values of an object to a set.
-- 
-- Note that the set values will be inserted in a random order, due to how `pairs` works under the
-- hood.
-- 
-- Also see the `objectValuesToReadonlySet` function.
function ____exports.objectValuesToSet(self, object)
    local set = __TS__New(Set)
    for ____, key in ipairs(__TS__ObjectValues(object)) do
        set:add(key)
    end
    return set
end
--- Helper function to add all of the values in one set to another set. The first set passed will be
-- modified in place.
-- 
-- This function is variadic, meaning that you can specify N sets to add to the first set.
function ____exports.addSetsToSet(self, mainSet, ...)
    local setsToAdd = {...}
    for ____, set in ipairs(setsToAdd) do
        for ____, value in __TS__Iterator(set) do
            mainSet:add(value)
        end
    end
end
--- Helper function to create a new set that is the composition of two or more sets.
-- 
-- This function is variadic, meaning that you can specify N sets.
function ____exports.combineSets(self, ...)
    local sets = {...}
    local newSet = __TS__New(Set)
    for ____, set in ipairs(sets) do
        for ____, value in __TS__Iterator(set) do
            newSet:add(value)
        end
    end
    return newSet
end
--- Helper function to copy a set. (You can also use a Set constructor to accomplish this task.)
function ____exports.copySet(self, oldSet)
    local newSet = __TS__New(Set)
    for ____, value in __TS__Iterator(oldSet) do
        newSet:add(value)
    end
    return newSet
end
--- Helper function to delete all of the values in one set from another set. The first set passed
-- will be modified in place.
-- 
-- This function is variadic, meaning that you can specify N sets to remove from the first set.
function ____exports.deleteSetsFromSet(self, mainSet, ...)
    local setsToRemove = {...}
    for ____, set in ipairs(setsToRemove) do
        for ____, value in __TS__Iterator(set) do
            mainSet:delete(value)
        end
    end
end
--- Helper function to get a random element from the provided set.
-- 
-- If you want to get an unseeded element, you must explicitly pass `undefined` to the `seedOrRNG`
-- parameter.
-- 
-- @param set The set to get an element from.
-- @param seedOrRNG The `Seed` or `RNG` object to use. If an `RNG` object is provided, the
-- `RNG.Next` method will be called. If `undefined` is provided, it will default to
-- a random seed.
-- @param exceptions Optional. An array of elements to skip over if selected.
function ____exports.getRandomSetElement(self, set, seedOrRNG, exceptions)
    if exceptions == nil then
        exceptions = {}
    end
    local array = ____exports.getSortedSetValues(nil, set)
    return getRandomArrayElement(nil, array, seedOrRNG, exceptions)
end
--- Helper function to get all possible combinations of the given set. This includes the combination
-- of an empty set.
-- 
-- For example, if this function is provided a set containing 1, 2, and 3, then it will return an
-- array containing the following sets:
-- 
-- - [] (if `includeEmptyArray` is set to true)
-- - [1]
-- - [2]
-- - [3]
-- - [1, 2]
-- - [1, 3]
-- - [2, 3]
-- - [1, 2, 3]
-- 
-- @param set The set to get the combinations of.
-- @param includeEmptyArray Whether to include an empty array in the combinations.
function ____exports.getSetCombinations(self, set, includeEmptyArray)
    local values = ____exports.getSortedSetValues(nil, set)
    local combinations = getArrayCombinations(nil, values, includeEmptyArray)
    return __TS__ArrayMap(
        combinations,
        function(____, array) return __TS__New(ReadonlySet, array) end
    )
end
--- Helper function to convert the keys of an object to a read-only set.
-- 
-- Note that the set values will be inserted in a random order, due to how `pairs` works under the
-- hood.
-- 
-- Also see the `objectKeysToSet` function.
function ____exports.objectKeysToReadonlySet(self, object)
    return ____exports.objectKeysToSet(nil, object)
end
--- Helper function to convert the values of an object to a read-only set.
-- 
-- Note that the set values will be inserted in a random order, due to how `pairs` works under the
-- hood.
-- 
-- Also see the `objectValuesToSet` function.
function ____exports.objectValuesToReadonlySet(self, object)
    return ____exports.objectValuesToSet(nil, object)
end
--- Helper function to add one or more elements to a set at once without having to repeatedly call
-- the `Set.add` method.
-- 
-- This function is variadic, meaning that you can pass as many things as you want to add.
function ____exports.setAdd(self, set, ...)
    local elements = {...}
    for ____, element in ipairs(elements) do
        set:add(element)
    end
end
--- Helper function to check for one or more elements in a set at once without having to repeatedly
-- call the `Set.has` method.
-- 
-- This function is variadic, meaning that you can pass as many things as you want to check for. It
-- will return true if one or more elements are found.
function ____exports.setHas(self, set, ...)
    local elements = {...}
    return __TS__ArraySome(
        elements,
        function(____, element) return set:has(element) end
    )
end
--- Helper function to sum every value in a set together.
function ____exports.sumSet(self, set)
    local values = {__TS__Spread(set)}
    return sumArray(nil, values)
end
return ____exports
