-- these are standard library functions that are available no matter what; don't require any
-- prefix to use them

function clamp (Value, Min, Max)

   if (Value > Max) then Value = Max end
   if (Value < Min) then Value = Min end
   return Value
end

-- can't do an in-place swap because we can't pass references in lua, but this will work

function swap (A, B)

   return B, A
end

-- return a default value, in case the value isn't in the table

function nz (Value, Default)

   if (Value) then return Value
   end
   return Default
end

-- return the object length; this just looks cleaner

function size (Table)

   return #Table
end

function sizeof (Table)

   return #Table
end

-- return all the keys in a table, ordered

function table_keys (Table)

   local Keys = {}
   for K, V in pairs (Table) do table.insert (Keys, K)
   end
   return Keys
end

function table_values (Table)

   local Values = {}
   for K, V in pairs (Table) do table.insert (Values, V)
   end
   return Values
end

-- return true if the tabel contains the given element

function table_contains (Table, Element)

   for ID, TableElement in pairs (Table) do

      if (TableElement == Element) then

         return true
      end
   end

   return false
end

-- return the index of the table element

function table_find_element (Table, Element)

   for ID, TableElement in pairs (Table) do

      if (TableElement == Element) then return ID
      end
   end

   return nil
end

-- remove an element

function table_remove_element (Table, Element)

   for ID, TableElement in pairs (Table) do

      if (TableElement == Element) then

         table.remove (Table, ID)
         return
      end
   end
end

-- return a slice of a table

function table_slice (Table, StartIndex, EndIndex)

   local NewTable = {}
   for Index = StartIndex, EndIndex, 1 do

      table.insert (NewTable, Table [Index])
   end
   return NewTable
end

-- choose a random element from the table

function table_choose (Table)

   local Index = 1 + math.floor (RN () * #Table)

   if Index < 1 then Index = 1
   end

   if Index > #Table then Index = #Table
   end

   return Table [Index]
end

-- copy a list of fields from a table

function table_copyfields (Table, Fields)

   local Copy = {}
   for ID, Field in pairs (Fields) do

      Copy [Field] = Table [Field]
   end
   return Copy
end

-- merge multiple tables together (not arrays right now); multiple parameters for ease of use

function table_mapmerge (Table1, Table2, Table3)

   function mapmerge (SrcT, DestT)

      if (not SrcT) then return end
      for ID, Element in pairs (SrcT) do DestT[ID] = Element end
   end

   local DestT = {}
   mapmerge (Table1, DestT)
   mapmerge (Table2, DestT)
   mapmerge (Table3, DestT)
   return DestT
end

-- merge multiple arrays together; multi params for ease of use

function table_arraymerge (Table1, Table2, Table3)

   function arraymerge (SrcT, DestT)

      if (not SrcT) then return end
      for ID, Element in pairs (SrcT) do table.insert (DestT, Element) end
   end

   local DestT = {}
   arraymerge (Table1, DestT)
   arraymerge (Table2, DestT)
   arraymerge (Table3, DestT)
   return DestT
end

-- array merge table 2 into table 1

function table_concat (Table1, Table2)

   for K, V in pairs (Table2) do table.insert (Table1, V)
   end
end

-- return the keys in the table

function table_keys (Table)

   local Keys = {}
   for K, V in pairs (Table) do

      table.insert (Keys, K)
   end
   return Keys
end

-- search for an element; if it's a number, return that; if it's a string, look for an element in the table where
-- the "type" field matches it; if it's a table, then remove that. this is often useful but may seem a bit arbitrary

function table_smart_find (Table, SearchParam)

   local SearchType = type (SearchParam)
   if ("number" == SearchType) then

      return SearchParam

   elseif ("string" == SearchType) then

      for ID, Element in pairs (Table) do

         if (Element.type == SearchParam) then return ID
         end
      end

   elseif ("table" == SearchType) then

      for ID, Element in pairs (Table) do

         if (Element == SearchParam) then return ID
         end
      end
   end
end

-- count how many elements in a set

function set_empty (Set)

   for E, W in pairs (Set) do return false
   end
   return true
end

-- the weighted choice algorithm; table should be Values => Weights

function set_choose_weighted (Set)

   if (set_empty (Set)) then return nil
   end

   -- first, calculate the total sum of all weights
   local Sum = 0.0
   for E, W in pairs (Set) do Sum = Sum + W
   end
   if (0.0 == Sum) then return nil
   end

   -- choose a destination weight randomly, then go through and find a matching element
   local CurSum = 0.0
   local R = RN () * Sum
   for E, W in pairs (Set) do

      if (R > CurSum and R <= CurSum + W) then return E
      end
      CurSum = CurSum + W
   end

   -- very rare that we end up here, due to numerical rounding so just return the first element
   return Set [1]
end

-- capitalize the first letter of a string

function capitalize (S)

   if (string.len (S) == 0) then return S
   end

   local FirstByte = string.byte (S, 1)
   if (FirstByte >= 97 and FirstByte <= 122) then

      FirstByte = FirstByte - 97 + 65
   end

   S = string.sub (S, 2)
   S = string.char (FirstByte) .. S

   return S
end

-- lowercase all letters in a string

function lowercase_all (S)

   local LowS = ""

   for ID = 1, string.len (S), 1 do

      local Byte = string.byte (S, ID)
      if (Byte >= 65 and Byte <= 90) then

         Byte = Byte - 65 + 97
      end

      LowS = LowS .. string.char (Byte)
   end

   return LowS
end

-- deep copy function

function deepcopy(object)
    local lookup_table = {}
    local function _copy(object)
        if type(object) ~= "table" then
            return object
        elseif lookup_table[object] then
            return lookup_table[object]
        end
        local new_table = {}
        lookup_table[object] = new_table
        for index, value in pairs(object) do
            new_table[_copy(index)] = _copy(value)
        end
        return setmetatable(new_table, getmetatable(object))
    end
    return _copy(object)
end

-- some simple mathy type stuff

function length2d (x1, y1, x2, y2)

   local dx = x1 - nz (x2, 0)
   local dy = y1 - nz (y2, 0)
   return math.sqrt (dx * dx + dy * dy)
end

function length3d (x1, y1, z1, x2, y2, z2)

   local dx = x1 - nz (x2, 0)
   local dy = y1 - nz (y2, 0)
   local dz = z1 - nz (z2, 0)
   return math.sqrt (dx * dx + dy * dy + dz * dz)
end

function delta2d (x1, y1, x2, y2)

   return { x2 - x1, y2 - y1 }
end

function norm_delta2d (x1, y1, x2, y2, lfac)

   lfac = nz (lfac, 1.0)
   local dx = x2 - x1
   local dy = y2 - y1
   local l = math.sqrt (dx * dx + dy * dy) / lfac
   if (0 == l) then return 0, 0
   end
   dx = dx / l
   dy = dy / l
   return { dx, dy }
end

function max_norm2d (dx, dy, max_length)

   local l = math.sqrt (dx * dx + dy * dy)
   if (l > max_length) then

      dx = dx * max_length / l
      dy = dy * max_length / l
   end
   return { dx, dy }
end

function clamp (A, Min, Max)

   if (A < Min) then return Min
   elseif (A > Max) then return Max
   else return A
   end
end

function RN_variance (Variance)

   return 1.0 + (RN () - 0.5) * Variance
end

function toboolean(X)
   return not not X
end

-- from: http://lua-users.org/wiki/SplitJoin
--Written for 5.0; could be made slightly cleaner with 5.1
--Splits a string based on a separator string or pattern;
--returns an array of pieces of the string.
--(May optionally supply a table as the third parameter which will be filled with the results.)
function string:split( inSplitPattern, outResults )
if not outResults then
   outResults = { }
end
local theStart = 1
local theSplitStart, theSplitEnd = string.find( self, inSplitPattern, theStart )
while theSplitStart do
   table.insert( outResults, string.sub( self, theStart, theSplitStart-1 ) )
   theStart = theSplitEnd + 1
   theSplitStart, theSplitEnd = string.find( self, inSplitPattern, theStart )
end
table.insert( outResults, string.sub( self, theStart ) )
return outResults
end

--
-- interpolate A towards B, maximum amount MaxDelta (must be positive)
--

function interpolate_scalar (A, B, MaxDelta)

   local Delta = B - A
   if (Delta < 0) then MaxDelta = MaxDelta * -1 end
   if (math.abs (MaxDelta) < math.abs (Delta)) then Delta = MaxDelta end
   A = A + Delta
   return A
end
