local F=script.Parent local K=require(F:WaitForChild("\065\098\115\116\114\097\099\116\076\105\115\116"))local d="\067\097\110\110\111\116 \099\111\110\115\116\114\117\099\116 \065\114\114\097\121\076\105\115\116 \102\114\111\109 \116\121\112\101 \037\115\046"local J="\078\111 \102\105\114\115\116 \101\108\101\109\101\110\116 \101\120\105\115\116\115\046"local N="\078\111 \108\097\115\116 \101\108\101\109\101\110\116 \101\120\105\115\116\115\046"local P="\073\110\100\101\120 \'\037\115\' \105\115 \111\117\116 \111\102 \098\111\117\110\100\115\046"local M="\076\097\115\116 \105\110\100\101\120 \105\115 \115\109\097\108\108\101\114 \116\104\097\110 \102\105\114\115\116 \105\110\100\101\120\046"local j=K.new()j.__index=j function j.new(F)local K=setmetatable({},j)K.array={}if F~=nil then local J=type(F)if J=="\116\097\098\108\101"then if type(F.Enumerator)=="\102\117\110\099\116\105\111\110"then for F,d in F:Enumerator()do K.array[#K.array+1]=d end else for F,d in ipairs(F)do K.array[#K.array+1]=d end end else error(string.format(d,J))end end return K end function j.Enumerator(F)return ipairs(F.array)end function j.Count(F)return#F.array end function j.Clear(F)F.array={}end function j.Remove(K,F)for d,J in ipairs(K.array)do if J==F then table.remove(K.array,d)return true end end return false end function j.RemoveAll(K,F)local d={}for F,K in F:Enumerator()do d[K]=(d[K]or 0)+1 end local J=K.array local N=false local P=1 for F,K in ipairs(J)do if not d[K]or d[K]==0 then if F~=P then J[P]=K J[F]=nil end P=P+1 else d[K]=d[K]-1 J[F]=nil N=true end end return N end function j.RetainAll(K,F)local d=K.array local J=false local N=1 for K,P in ipairs(d)do if F:Contains(P)then if K~=N then d[N]=P d[K]=nil end N=N+1 else d[K]=nil J=true end end return J end function j.First(F)if#F.array==0 then error(J)end return F.array[1]end function j.Get(K,F)if F<1 or F>#K.array then error(string.format(P,F))end return K.array[F]end function j.IndexOf(d,F,K)local J=d.array if K==nil then K=1 elseif K<1 or K>#J then error(string.format(P,K))end if#J==0 then return 0 end for K=K,#J,1 do if J[K]==F then return K end end return 0 end function j.Last(F)if#F.array==0 then error(N)end return F.array[#F.array]end function j.LastIndexOf(K,F)local d=K.array if#d==0 then return 0 end for K=#d,1,-1 do if d[K]==F then return K end end return 0 end function j.Sub(d,F,K)local J=d.array K=K or#J if F<1 or F>#J then error(string.format(P,F))end if K<1 or K>#J then error(string.format(P,K))end if K<F then error(M)end local N=j.new()for F=F,K,1 do N:Push(J[F])end return N end function j.Delete(K,F)if F<1 or F>#K.array then error(string.format(P,F))end table.remove(K.array,F)return true end function j.Insert(d,F,K)if F<1 or F>#d.array+1 then error(string.format(P,F))end table.insert(d.array,F,K)return true end function j.InsertAll(d,F,K)local J=d.array if F<1 or F>#J+1 then error(string.format(P,F))end local N=K:Count()for F=#J,F,-1 do J[F+N]=J[F]end for K,d in K:Enumerator()do J[F]=d F=F+1 end return true end function j.Pop(F)if#F.array==0 then error(N)end local K=F.array[#F.array]F.array[#F.array]=nil return K end function j.Push(K,F)K.array[#K.array+1]=F return true end j.Add=j.Push function j.Set(d,F,K)if F<1 or F>#d.array then error(string.format(P,F))end local J=d.array[F]==K d.array[F]=K return not J end function j.Shift(F)if#F.array==0 then error(J)end local K=F.array[1]table.remove(F.array,1)return K end function j.Unshift(K,F)table.insert(K.array,1,F)return true end return j
--- An array-based @{List} of items.
-- A List implementation that stores items using an array. This provides random
-- access to any index, however does so at the expense of insertion and
-- deletion everywhere except at the end.
--
-- Access, insertion at the end, and deletion at the end are Θ(1). Insertion
-- elsewhere, deletion elsewhere, and search are Θ(n).
--
-- ArrayList implements all optional @{List} and @{Collection} methods.
--
-- **Extends:** @{AbstractList}
--
-- **Implements:** @{List}, @{Collection}, @{Enumerable}
--
-- @classmod ArrayList

local module = script.Parent
local AbstractList = require(module:WaitForChild("AbstractList"))

local ErrorConstruct = "Cannot construct ArrayList from type %s."
local ErrorFirst = "No first element exists."
local ErrorLast = "No last element exists."
local ErrorBounds = "Index '%s' is out of bounds."
local ErrorOrder = "Last index is smaller than first index."

local ArrayList = AbstractList.new()

ArrayList.__index = ArrayList

--- Creates a new ArrayList.
-- Creates a ArrayList copy of the provided @{Collection} or array-indexed
-- table if one is provided, otherwise creates an empty ArrayList.
--
-- @param collection the @{Collection} or table to copy
-- @return the new ArrayList
-- @static
function ArrayList.new(collection)
	local self = setmetatable({}, ArrayList)
	self.array = {}
	if collection ~= nil then
		local typeCollection = type(collection)
		if typeCollection == "table" then
			if type(collection.Enumerator) == "function" then
				-- If there's an Enumerator, assume it acts as a collection
				for _, value in collection:Enumerator() do
					self.array[#self.array + 1] = value
				end
			else -- Otherwise its just a table (we can't know otherwise)
				for _, value in ipairs(collection) do
					self.array[#self.array + 1] = value
				end
			end
		else
			error(string.format(ErrorConstruct, typeCollection))
		end
	end
	return self
end

--- Creates an enumerator for the ArrayList.
-- The enumerator can be used directly in a generic for loop similar to pairs
-- or ipairs.
--
-- @return the enumerator generator
-- @return the invariant state
-- @return the control variable state
-- @from @{Enumerable}
function ArrayList:Enumerator()
	return ipairs(self.array)
end

--- Gets the number of items in the ArrayList.
--
-- @return the number of items
-- @from @{Collection}
function ArrayList:Count()
	return #self.array
end

--- Removes everything from the ArrayList.
--
-- @from @{Collection}
function ArrayList:Clear()
	self.array = {} -- The array will be garbage collected (may still be in use)
end

--- Removes the specified item from the ArrayList.
-- Removes only a single item. If there are multiple of the same item, it
-- removes only the first encountered.
--
-- When an item is removed any others are shifted to fill the gap left at
-- the index of removal.
--
-- @param item the item to remove from the ArrayList
-- @return true if the ArrayList changed as a result, false otherwise
-- @from @{Collection}
function ArrayList:Remove(item)
	for index, value in ipairs(self.array) do
		if value == item then
			table.remove(self.array, index)
			return true -- The ArrayList has been changed
		end
	end
	return false -- Nothing changed
end

--- Removes all provided items from the ArrayList.
-- Removes each instance of a provided item only once for each time provided.
-- If there are multiple of the same item in this ArrayList, it removes only
-- the first encountered for each provided.
--
-- @param items the @{Collection} of items to remove from this ArrayList
-- @return true if the ArrayList changed as a result, false otherwise
-- @from @{Collection}
function ArrayList:RemoveAll(items)
	local remove = {}
	for _, value in items:Enumerator() do
		remove[value] = (remove[value] or 0) + 1
	end
	local array = self.array
	local changed = false -- We can't report a change immediately
	local empty = 1
	for index, value in ipairs(array) do
		if not remove[value] or remove[value] == 0 then -- If we aren't removing it
			if index ~= empty then -- If we're not at the earlier empty index
				array[empty] = value -- Move our value there
				array[index] = nil
			end
			empty = empty + 1
		else -- If this is being removed
			remove[value] = remove[value] - 1 -- Remove it from our multiset
			array[index] = nil
			changed = true
		end
	end
	return changed
end

--- Removes all items except those provided from the ArrayList.
-- Retains only the items contained in the specified @{Collection}. If there are
-- duplicates they are all kept.
--
-- @param items the @{Collection} of items to retain in this ArrayList
-- @return true if the ArrayList changed as a result, false otherwise
-- @from @{Collection}
function ArrayList:RetainAll(items)
	local array = self.array
	local changed = false -- We can't report a change immediately
	local empty = 1
	for index, value in ipairs(array) do
		if items:Contains(value) then -- If this is being retained
			if index ~= empty then -- If we're not at the earlier empty index
				array[empty] = value -- Move our value there
				array[index] = nil
			end
			empty = empty + 1
		else -- If this isn't being retained
			array[index] = nil
			changed = true
		end
	end
	return changed
end

--- Gets the item at the beginning of the ArrayList.
--
-- @return the first item in the ArrayList
-- @raise if the ArrayList is empty
-- @from @{List}
function ArrayList:First()
	if #self.array == 0 then
		error(ErrorFirst)
	end
	return self.array[1]
end

--- Gets the item at the specified index in the ArrayList.
--
-- @param index the index to get
-- @return the item in the ArrayList at the specified index
-- @raise if the index is out of bounds of the ArrayList
-- @from @{List}
function ArrayList:Get(index)
	if index < 1 or index > #self.array then
		error(string.format(ErrorBounds, index))
	end
	return self.array[index]
end

--- Determines the index of a specific item in the ArrayList.
-- Starts from a specified index or from the beginning if none is provided.
--
-- @param item the item to locate
-- @param index the index to start looking from
-- @return the index of the item in the ArrayList if found, 0 otherwise
-- @raise if the index is out of bounds of the ArrayList
-- @from @{List}
function ArrayList:IndexOf(item, index)
	local array = self.array
	if index == nil then
		index = 1 -- If index isn't provided, start at the beginning
	elseif index < 1 or index > #array then -- If it is, check the bounds
		error(string.format(ErrorBounds, index))
	end
	if #array == 0 then
		return 0 -- If we're empty, its not here
	end
	for currentIndex = index, #array do
		if array[currentIndex] == item then
			return currentIndex
		end
	end
	return 0 -- It wasn't here
end

--- Gets the item at the end of the ArrayList.
--
-- @return the last item in the ArrayList
-- @raise if the ArrayList is empty
-- @from @{List}
function ArrayList:Last()
	if #self.array == 0 then
		error(ErrorLast)
	end
	return self.array[#self.array]
end

--- Determines the last index of a specific item in the ArrayList.
-- Only returns the very last occurrence of the item in the ArrayList.
--
-- @param item the item to locate
-- @return the index of the item in the ArrayList if found, 0 otherwise
-- @from @{List}
function ArrayList:LastIndexOf(item)
	local array = self.array
	if #array == 0 then
		return 0 -- If we're empty, its not here
	end
	for index = #array, 1, -1 do
		if array[index] == item then
			return index
		end
	end
	return 0 -- It wasn't here
end

--- Creates a new sub-list of this ArrayList.
-- Creates the list that is the portion of this ArrayList between the
-- specified indices or from the first specified index to the end if only one
-- index is specified.
--
-- @param first the index to start at
-- @param last the index to end at
-- @return the new ArrayList
-- @raise
-- * if the first index is out of bounds
-- * if the last index is out of bounds
-- * if the last index is smaller than the first index
-- @from @{List}
function ArrayList:Sub(first, last)
	local array = self.array
	last = last or #array -- If last isn't provided, go to the end
	if first < 1 or first > #array then
		error(string.format(ErrorBounds, first))
	end
	if last < 1 or last > #array then
		error(string.format(ErrorBounds, last))
	end
	if last < first then
		error(ErrorOrder)
	end
	local sub = ArrayList.new()
	for index = first, last do
		sub:Push(array[index])
	end
	return sub
end

--- Removes the item at the specified index from the ArrayList.
-- Shifts other elements to fill the gap left at the index of removal.
--
-- @param index the index of the item to remove from the ArrayList
-- @return true always since the ArrayList is always changed
-- @raise if the index is out of bounds of the ArrayList
-- @from @{List}
function ArrayList:Delete(index)
	if index < 1 or index > #self.array then
		error(string.format(ErrorBounds, index))
	end
	table.remove(self.array, index)
	return true -- The ArrayList is always changed
end

--- Inserts the item into the ArrayList at the specified index.
-- Shifts other elements to make space at the index of insertion.
--
-- @param index the index to insert the item in the ArrayList
-- @param item the item to add
-- @return true always since the ArrayList is always changed
-- @raise if the index is out of bounds of the ArrayList
-- @from @{List}
function ArrayList:Insert(index, item)
	if index < 1 or index > #self.array + 1 then
		error(string.format(ErrorBounds, index))
	end
	table.insert(self.array, index, item)
	return true -- The ArrayList is always changed
end

--- Inserts multiple items into the ArrayList at the specified index.
-- Inserts all items from the provided @{Collection} in an arbitrary,
-- deterministic order. The order is the same as the order of enumeration.
--
-- Shifts other elements to make space at the index of insertion.
--
-- @param index the index to insert the items in the ArrayList
-- @param items the @{Collection} of items to add to this ArrayList
-- @return true always since the ArrayList is always changed
-- @raise if the index is out of bounds of the ArrayList
-- @from @{List}
function ArrayList:InsertAll(index, items)
	local array = self.array
	if index < 1 or index > #array + 1 then
		error(string.format(ErrorBounds, index))
	end
	local offset = items:Count()
	for currentIndex = #array, index, -1 do
		array[currentIndex + offset] = array[currentIndex]
	end
	for _, value in items:Enumerator() do
		array[index] = value
		index = index + 1
	end
	return true
end

--- Gets an item from the end and removes that item from the ArrayList.
--
-- @return the item in the ArrayList
-- @raise if the ArrayList is empty
-- @from @{List}
function ArrayList:Pop()
	if #self.array == 0 then
		error(ErrorLast)
	end
	local last = self.array[#self.array]
	self.array[#self.array] = nil
	return last
end

--- Adds an item to the end of the ArrayList.
--
-- @param item the item to add
-- @return true always since the ArrayList is always changed
-- @from @{List}
function ArrayList:Push(item)
	self.array[#self.array + 1] = item
	return true -- The ArrayList is always changed
end

--- Adds an item to the ArrayList.
--
-- @param item the item to add
-- @return true always since the ArrayList is always changed
-- @function Add
-- @from @{Collection}
ArrayList.Add = ArrayList.Push

--- Sets the element at the specified index.
--
-- @param index the index to set
-- @param	item the item to set at the index
-- @return true if the ArrayList changed as a result, false otherwise
-- @raise if the index is out of bounds of the ArrayList
-- @from @{List}
function ArrayList:Set(index, item)
	if index < 1 or index > #self.array then
		error(string.format(ErrorBounds, index))
	end
	local same = self.array[index] == item
	self.array[index] = item
	return not same
end

--- Gets an item from the beginning and removes that item from the ArrayList.
-- Shifts other elements to fill the gap left.
--
-- @return the item in the ArrayList
-- @raise if the ArrayList is empty
-- @from @{List}
function ArrayList:Shift()
	if #self.array == 0 then
		error(ErrorFirst)
	end
	local first = self.array[1]
	table.remove(self.array, 1)
	return first
end

--- Adds an item to the beginning of the ArrayList.
-- Shifts other elements to make space.
--
-- @param item the item to add
-- @return true always since the ArrayList is always changed
-- @from @{List}
function ArrayList:Unshift(item)
	table.insert(self.array, 1, item)
	return true -- The ArrayList is always changed
end

return ArrayList
