local super = require "Object"

SimplexSearch = super:new()

function SimplexSearch:new(points, evaluate)
    self = super.new(self)
    
    self._points = points
    
    local cache = {}
    self._evaluate = function(p)
        if not cache[p] then
            cache[p] = evaluate(p)
        end
        return cache[p]
    end
    
    return self
end

function SimplexSearch:iterate()
    local points = self._points
    local evaluate = self._evaluate
    table.sort(points, function(p1, p2) return (evaluate(p1) < evaluate(p2)) end)
    
    local best = points[1]
    local worst = points[#points]
    
    local centroid = { (best[1] + points[2][1]) / 2, (best[2] + points[2][2]) / 2 }
    
    local reflection = { 2 * centroid[1] - worst[1], 2 * centroid[2] - worst[2] }
    if evaluate(best) <= evaluate(reflection) and evaluate(reflection) < evaluate(points[#points - 1]) then
        points[#points] = reflection
    elseif evaluate(reflection) < evaluate(best) then
        local expansion = { 3 * centroid[1] - 2 * worst[1], 3 * centroid[2] - 2 * worst[2] }
        if evaluate(expansion) < evaluate(reflection) then
            points[#points] = expansion
        else
            points[#points] = reflection
        end
    else
        local contraction = { (centroid[1] + worst[1]) / 2, (centroid[2] + worst[2]) / 2 }
        if evaluate(contraction) < evaluate(worst) then
            points[#points] = contraction
        else
            points[2] = centroid
            points[3] = { (best[1] + points[3][1]) / 2, (best[2] + points[3][2]) / 2 }
        end
    end
    
    self._points = points
    
    return self._points[1], self._evaluate(self._points[1])
end

return SimplexSearch
