# @file pendulum.rb
# Question-8 from some problem set.
# @date 03/28/2026
# Rearrange the word so the character with the smallest ASCII value
# is placed at the center, the second smallest to its right and the
# third to its left and so on.
#
# Example-1: In : COMPUTER
# Out: TPMCEORU
#
# Example-2: In : SCIENCE
# Out: SIECCEN
#
# The problem specifies characters should be rearranged by invoking
# the function 'int minimumCharIndex(String str)'.
def pendulum_0(s)
def minimum_char_index(s)
s.index(s.each_byte.min.chr) # faster than each_char.min
end
s = s.dup
n = s.size
m = (n+1) / 2
r = []
n.times do |i|
j = i / 2
k = minimum_char_index(s)
r[i.even? ? m-j-1 : m+j] = s.slice!(k)
end
r.join
end
# Breaking the rules a bit.
def pendulum_0_1(s)
def minimum_index(a, first, last)
subarray = a[first...last]
subarray.index(subarray.min) + first
end
s = s.bytes
n = s.size
m = (n+1) / 2
r = []
n.times do |i|
j = i / 2
k = minimum_index(s, i, n)
r[i.even? ? m-j-1 : m+j] = s[k]
s[k] = s[i]
end
r.map(&:chr).join
end
def pendulum_0_2(s)
def swap(a, i, j)
a[i], a[j] = a[j], a[i] if i != j
end
def minimum_index(a, first, last)
subarray = a[first...last]
subarray.index(subarray.min) + first
end
def selectsort!(a)
(a.size-1).times do |i|
swap(a, i, minimum_index(a, i, a.size))
end
a
end
n = s.size
m = (n+1) / 2
r = []
selectsort!(s.bytes).each_with_index do |x, i|
j = i / 2
r[i.even? ? m-j-1 : m+j] = x
end
r.map(&:chr).join
end
# Breaking the rules a bit more.
def pendulum_1(s)
n = s.size
m = (n+1) / 2
r = []
s.chars.sort!.each_with_index do |x, i|
j = i / 2
r[i.even? ? m-j-1 : m+j] = x
end
r.join
end
def pendulum_2(s)
r = []
s.chars.sort!.each_with_index do |x, i|
r.public_send(i.even? ? :unshift : :push, x)
end
r.join
end
def pendulum_3(s)
t = s.chars.sort!
l, r = t.drop(1).partition.each_with_index{|_, i| i.odd?}
(l.reverse + t.take(1) + r).join
end
def pendulum_4(s)
l, r = (1...s.size).partition{|i| i.even?}
s.chars.sort!.values_at(*l.reverse, 0, *r).join
end
# Main.
def test(f)
ss = ["", "a", "bca", "computer", "science"]
rs = ["", "a", "cab", "tpmceoru", "sieccen"]
ss.zip(rs) do |s, r|
s = f.call(s)
if s != r
puts ".Failed: expected `#{r}' got `#{s}'"
return
end
end
puts ".Passed"
end
def time(f)
def choices(pool, n)
n.times.map{pool.sample}
end
symbols = ('a'..'z').to_a
elapsed = 0
12.times do |i|
s = choices(symbols, 2**i).join
t = Time.now
f.call(s)
elapsed += Time.now - t
end
printf(".Elapsed: %.6fs\n", elapsed)
end
fs = [
:pendulum_0,
:pendulum_0_1,
:pendulum_0_2,
:pendulum_1,
:pendulum_2,
:pendulum_3,
:pendulum_4
]
fs.map(&method(:method)).each do |f|
puts f.name
test(f)
time(f)
end
IyBAZmlsZSBwZW5kdWx1bS5yYgojIFF1ZXN0aW9uLTggZnJvbSBzb21lIHByb2JsZW0gc2V0LgojIEBkYXRlIDAzLzI4LzIwMjYKCiMgUmVhcnJhbmdlIHRoZSB3b3JkIHNvIHRoZSBjaGFyYWN0ZXIgd2l0aCB0aGUgc21hbGxlc3QgQVNDSUkgdmFsdWUKIyBpcyBwbGFjZWQgYXQgdGhlIGNlbnRlciwgdGhlIHNlY29uZCBzbWFsbGVzdCB0byBpdHMgcmlnaHQgYW5kIHRoZQojIHRoaXJkIHRvIGl0cyBsZWZ0IGFuZCBzbyBvbi4KIwojIEV4YW1wbGUtMTogSW4gOiBDT01QVVRFUgojICAgICAgICAgICAgT3V0OiBUUE1DRU9SVQojCiMgRXhhbXBsZS0yOiBJbiA6IFNDSUVOQ0UKIyAgICAgICAgICAgIE91dDogU0lFQ0NFTgojCiMgVGhlIHByb2JsZW0gc3BlY2lmaWVzIGNoYXJhY3RlcnMgc2hvdWxkIGJlIHJlYXJyYW5nZWQgYnkgaW52b2tpbmcKIyB0aGUgZnVuY3Rpb24gJ2ludCBtaW5pbXVtQ2hhckluZGV4KFN0cmluZyBzdHIpJy4KCmRlZiBwZW5kdWx1bV8wKHMpCiAgZGVmIG1pbmltdW1fY2hhcl9pbmRleChzKQogICAgcy5pbmRleChzLmVhY2hfYnl0ZS5taW4uY2hyKSAjIGZhc3RlciB0aGFuIGVhY2hfY2hhci5taW4KICBlbmQKCiAgcyA9IHMuZHVwCiAgbiA9IHMuc2l6ZQogIG0gPSAobisxKSAvIDIKICByID0gW10KICBuLnRpbWVzIGRvIHxpfAogICAgaiA9IGkgLyAyCiAgICBrID0gbWluaW11bV9jaGFyX2luZGV4KHMpCiAgICByW2kuZXZlbj8gPyBtLWotMSA6IG0ral0gPSBzLnNsaWNlIShrKQogIGVuZAogIHIuam9pbgplbmQKCiMgQnJlYWtpbmcgdGhlIHJ1bGVzIGEgYml0LgoKZGVmIHBlbmR1bHVtXzBfMShzKQogIGRlZiBtaW5pbXVtX2luZGV4KGEsIGZpcnN0LCBsYXN0KQogICAgc3ViYXJyYXkgPSBhW2ZpcnN0Li4ubGFzdF0KICAgIHN1YmFycmF5LmluZGV4KHN1YmFycmF5Lm1pbikgKyBmaXJzdAogIGVuZAoKICBzID0gcy5ieXRlcwogIG4gPSBzLnNpemUKICBtID0gKG4rMSkgLyAyCiAgciA9IFtdCiAgbi50aW1lcyBkbyB8aXwKICAgIGogPSBpIC8gMgogICAgayA9IG1pbmltdW1faW5kZXgocywgaSwgbikKICAgIHJbaS5ldmVuPyA/IG0tai0xIDogbStqXSA9IHNba10KICAgIHNba10gPSBzW2ldCiAgZW5kCiAgci5tYXAoJjpjaHIpLmpvaW4KZW5kCgpkZWYgcGVuZHVsdW1fMF8yKHMpCiAgZGVmIHN3YXAoYSwgaSwgaikKICAgIGFbaV0sIGFbal0gPSBhW2pdLCBhW2ldIGlmIGkgIT0gagogIGVuZAoKICBkZWYgbWluaW11bV9pbmRleChhLCBmaXJzdCwgbGFzdCkKICAgIHN1YmFycmF5ID0gYVtmaXJzdC4uLmxhc3RdCiAgICBzdWJhcnJheS5pbmRleChzdWJhcnJheS5taW4pICsgZmlyc3QKICBlbmQKCiAgZGVmIHNlbGVjdHNvcnQhKGEpCiAgICAoYS5zaXplLTEpLnRpbWVzIGRvIHxpfAogICAgICBzd2FwKGEsIGksIG1pbmltdW1faW5kZXgoYSwgaSwgYS5zaXplKSkKICAgIGVuZAogICAgYQogIGVuZAoKICBuID0gcy5zaXplCiAgbSA9IChuKzEpIC8gMgogIHIgPSBbXQogIHNlbGVjdHNvcnQhKHMuYnl0ZXMpLmVhY2hfd2l0aF9pbmRleCBkbyB8eCwgaXwKICAgIGogPSBpIC8gMgogICAgcltpLmV2ZW4/ID8gbS1qLTEgOiBtK2pdID0geAogIGVuZAogIHIubWFwKCY6Y2hyKS5qb2luCmVuZAoKIyBCcmVha2luZyB0aGUgcnVsZXMgYSBiaXQgbW9yZS4KCmRlZiBwZW5kdWx1bV8xKHMpCiAgbiA9IHMuc2l6ZQogIG0gPSAobisxKSAvIDIKICByID0gW10KICBzLmNoYXJzLnNvcnQhLmVhY2hfd2l0aF9pbmRleCBkbyB8eCwgaXwKICAgIGogPSBpIC8gMgogICAgcltpLmV2ZW4/ID8gbS1qLTEgOiBtK2pdID0geAogIGVuZAogIHIuam9pbgplbmQKCmRlZiBwZW5kdWx1bV8yKHMpCiAgciA9IFtdCiAgcy5jaGFycy5zb3J0IS5lYWNoX3dpdGhfaW5kZXggZG8gfHgsIGl8CiAgICByLnB1YmxpY19zZW5kKGkuZXZlbj8gPyA6dW5zaGlmdCA6IDpwdXNoLCB4KQogIGVuZAogIHIuam9pbgplbmQKCmRlZiBwZW5kdWx1bV8zKHMpCiAgdCA9IHMuY2hhcnMuc29ydCEKICBsLCByID0gdC5kcm9wKDEpLnBhcnRpdGlvbi5lYWNoX3dpdGhfaW5kZXh7fF8sIGl8IGkub2RkP30KICAobC5yZXZlcnNlICsgdC50YWtlKDEpICsgcikuam9pbgplbmQKCmRlZiBwZW5kdWx1bV80KHMpCiAgbCwgciA9ICgxLi4ucy5zaXplKS5wYXJ0aXRpb257fGl8IGkuZXZlbj99CiAgcy5jaGFycy5zb3J0IS52YWx1ZXNfYXQoKmwucmV2ZXJzZSwgMCwgKnIpLmpvaW4KZW5kCgojIE1haW4uCgpkZWYgdGVzdChmKQogIHNzID0gWyIiLCAiYSIsICJiY2EiLCAiY29tcHV0ZXIiLCAic2NpZW5jZSJdCiAgcnMgPSBbIiIsICJhIiwgImNhYiIsICJ0cG1jZW9ydSIsICJzaWVjY2VuIl0KCiAgc3MuemlwKHJzKSBkbyB8cywgcnwKICAgIHMgPSBmLmNhbGwocykKICAgIGlmIHMgIT0gcgogICAgICBwdXRzICIuRmFpbGVkOiBleHBlY3RlZCBgI3tyfScgZ290IGAje3N9JyIKICAgICAgcmV0dXJuCiAgICBlbmQKICBlbmQKICBwdXRzICIuUGFzc2VkIgplbmQKCmRlZiB0aW1lKGYpCiAgZGVmIGNob2ljZXMocG9vbCwgbikKICAgIG4udGltZXMubWFwe3Bvb2wuc2FtcGxlfQogIGVuZAoKICBzeW1ib2xzID0gKCdhJy4uJ3onKS50b19hCiAgZWxhcHNlZCA9IDAKCiAgMTIudGltZXMgZG8gfGl8CiAgICBzID0gY2hvaWNlcyhzeW1ib2xzLCAyKippKS5qb2luCiAgICB0ID0gVGltZS5ub3cKICAgIGYuY2FsbChzKQogICAgZWxhcHNlZCArPSBUaW1lLm5vdyAtIHQKICBlbmQKICBwcmludGYoIi5FbGFwc2VkOiAlLjZmc1xuIiwgZWxhcHNlZCkKZW5kCgpmcyA9IFsKICA6cGVuZHVsdW1fMCwKICA6cGVuZHVsdW1fMF8xLAogIDpwZW5kdWx1bV8wXzIsCiAgOnBlbmR1bHVtXzEsCiAgOnBlbmR1bHVtXzIsCiAgOnBlbmR1bHVtXzMsCiAgOnBlbmR1bHVtXzQKXQoKZnMubWFwKCZtZXRob2QoOm1ldGhvZCkpLmVhY2ggZG8gfGZ8CiAgcHV0cyBmLm5hbWUKICB0ZXN0KGYpCiAgdGltZShmKQplbmQ=