The existing shift/push method touches every element in the table once per step. The triple reverse method touches every element exactly twice.
function tablex.rotate(t, n)
local tlen = #t
local mid = -n % tlen
tablex.reverse(t)
tablex.reverse(t, 1, mid)
tablex.reverse(t, mid+1, tlen)
end
For this to work your tablex.reverse needs to take optional bound arguments. Note that tablex.reverse should also be caching the length value rather than recalculating it in the loop.